From 654859795734813d60c21dce1fd63c00ed2b2f8a Mon Sep 17 00:00:00 2001 From: Brad Larson Date: Sun, 9 Jul 2023 21:39:52 -0500 Subject: [PATCH 1/9] First steps in the conversion to Swift and Metal. --- .gitignore | 1 - 1TRZ.pdb.gz | Bin 23825 -> 0 bytes 57-download.png | Bin 226 -> 0 bytes 69-display.png | Bin 289 -> 0 bytes 98-palette.png | Bin 452 -> 0 bytes 98-palette@2x.png | Bin 976 -> 0 bytes 3QE5.pdb.gz => BuiltInMolecules/3QE5.pdb.gz | Bin .../Acetylcholinesterase.pdb.gz | Bin BuiltInMolecules/Buckminsterfullerene.sdf | 168 ++ BuiltInMolecules/Caffeine.pdb | 41 + BuiltInMolecules/DNA.pdb | 847 ++++++ BuiltInMolecules/Heme.sdf | 514 ++++ BuiltInMolecules/Insulin.pdb | 1326 +++++++++ BuiltInMolecules/Nanotube.pdb | 583 ++++ BuiltInMolecules/TheoreticalBearing.pdb | 414 +++ BuiltInMolecules/TheoreticalXenonPump.pdb.gz | Bin 0 -> 137201 bytes BuiltInMolecules/TransferRNA.pdb | 2565 +++++++++++++++++ Default-Landscape.png | Bin 13010 -> 0 bytes Default-Landscape@2x.png | Bin 33335 -> 0 bytes Default-Portrait.png | Bin 11476 -> 0 bytes Default-Portrait@2x.png | Bin 30689 -> 0 bytes Default.png | Bin 5775 -> 0 bytes Default@2x.png | Bin 16139 -> 0 bytes Document-molecules-320.png | Bin 24331 -> 0 bytes Document-molecules-64.png | Bin 2758 -> 0 bytes English.lproj/Info.plist | 118 - English.lproj/Localized.strings | 63 - English.lproj/MainWindow.xib | 388 --- English.lproj/SLSMoleculeGLView.xib | 384 --- French.lproj/Info.plist | 100 - French.lproj/Localized.strings | 64 - French.lproj/MainWindow.xib | 386 --- French.lproj/SLSMoleculeGLView.xib | 218 -- GLProgram.h | 29 - GLProgram.m | 233 -- GlyphishIconLicense.txt | 14 - Info.plist | 32 - LaunchScreen.storyboard | 31 - Molecules.xcodeproj/larson.mode2v3 | 1551 ---------- Molecules.xcodeproj/larson.pbxuser | 548 ---- Molecules.xcodeproj/project.pbxproj | 1257 ++++---- .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 14 + .../AppIcon.appiconset/MoleculesIcon-1024.png | Bin 0 -> 370912 bytes Molecules/Assets.xcassets/Contents.json | 6 + Molecules/FileProcessing/Atom.swift | 51 + Molecules/FileProcessing/Bond.swift | 12 + Molecules/FileProcessing/Coordinate.swift | 6 + .../FileProcessing/MolecularStructure.swift | 67 + .../FileProcessing/MoleculeDocument.swift | 84 + Molecules/FileProcessing/PDBFile.swift | 139 + Molecules/FileProcessing/SDFFile.swift | 76 + Molecules/FileProcessing/XYZFile.swift | 78 + Molecules/Info.plist | 112 + Molecules/MoleculesApp.swift | 50 + .../Preview Assets.xcassets/Contents.json | 6 + Molecules/Rendering/MetalRenderView.swift | 103 + .../Rendering/MetalRenderingDevice.swift | 26 + Molecules/Rendering/MoleculeDisplayView.swift | 62 + .../Rendering/MoleculeMetadataView.swift | 17 + .../Rendering/Shaders/SphereRaytracing.metal | 94 + MoleculesIcon.png | Bin 3840 -> 0 bytes MoleculesIcon72.png | Bin 5579 -> 0 bytes MoleculesIcon72@2x.png | Bin 17893 -> 0 bytes MoleculesIcon@2x.png | Bin 11563 -> 0 bytes MoleculesTests/PDBFileTests.swift | 52 + MoleculesTests/SDFFileTests.swift | 21 + .../TestMolecules/Caffeine.pdb.gz | Bin .../TestMolecules/DNA.pdb.gz | Bin .../TestMolecules/SampleMolecule.xyz | 49 + .../TestMolecules/TransferRNA.pdb.gz | Bin MoleculesTests/XYZFileTests.swift | 13 + MoleculesUITests/MoleculesUITests.swift | 34 + .../MoleculesUITestsLaunchTests.swift | 25 + Molecules_Prefix.pch | 8 - NSData+Gzip.h | 18 - NSData+Gzip.m | 104 - README.md | 29 + .../English.lproj/SLSMoleculeDownloadView.xib | 800 ----- .../French.lproj/SLSMoleculeDownloadView.xib | 800 ----- RotationIcon.png | Bin 1539 -> 0 bytes RotationIcon@2x.png | Bin 2354 -> 0 bytes RotationIconSelected.png | Bin 1686 -> 0 bytes RotationIconSelected@2x.png | Bin 2519 -> 0 bytes RotationIconiPad.png | Bin 1384 -> 0 bytes RotationIconiPad@2x.png | Bin 3147 -> 0 bytes RotationIconiPadCancel.png | Bin 1583 -> 0 bytes RotationIconiPadCancel@2x.png | Bin 4191 -> 0 bytes SLSAtomColorKeyController.h | 14 - SLSAtomColorKeyController.m | 249 -- SLSCellTextView.h | 23 - SLSCellTextView.m | 53 - SLSMolecule+PDB.h | 19 - SLSMolecule+PDB.m | 901 ------ SLSMolecule+SDF.h | 15 - SLSMolecule+SDF.m | 251 -- SLSMolecule.h | 108 - SLSMolecule.m | 1058 ------- SLSMoleculeAppDelegate.h | 64 - SLSMoleculeAppDelegate.m | 687 ----- SLSMoleculeCustomDownloadViewController.h | 17 - SLSMoleculeCustomDownloadViewController.m | 178 -- SLSMoleculeDataSourceViewController.h | 17 - SLSMoleculeDataSourceViewController.m | 119 - SLSMoleculeDetailViewController.h | 28 - SLSMoleculeDetailViewController.m | 349 --- SLSMoleculeDownloadController.h | 34 - SLSMoleculeDownloadController.m | 262 -- SLSMoleculeDownloadView.xib | 514 ---- SLSMoleculeGLView.h | 26 - SLSMoleculeGLView.m | 138 - SLSMoleculeGLViewController.h | 79 - SLSMoleculeGLViewController.m | 729 ----- SLSMoleculeLibraryTableCell.h | 22 - SLSMoleculeLibraryTableCell.m | 43 - SLSMoleculeRootViewController.h | 56 - SLSMoleculeRootViewController.m | 303 -- SLSMoleculeSearchViewController.h | 42 - SLSMoleculeSearchViewController.m | 872 ------ SLSMoleculeTableViewController.h | 48 - SLSMoleculeTableViewController.m | 432 --- SLSMoleculeWebDetailViewController.h | 29 - SLSMoleculeWebDetailViewController.m | 132 - SLSMoleculeiPadRootViewController.h | 37 - SLSMoleculeiPadRootViewController.m | 340 --- SLSOpenGLES11Renderer.h | 21 - SLSOpenGLES11Renderer.m | 585 ---- SLSOpenGLES20Renderer.h | 115 - SLSOpenGLES20Renderer.m | 1913 ------------ SLSOpenGLESRenderer.h | 151 - SLSOpenGLESRenderer.m | 546 ---- SLSTextViewController.h | 21 - SLSTextViewController.m | 106 - Shaders/CylinderAmbientOcclusion.fsh | 71 - Shaders/CylinderAmbientOcclusion.vsh | 57 - Shaders/CylinderDepth.fsh | 37 - Shaders/CylinderDepth.vsh | 65 - Shaders/CylinderRaytracing.fsh | 111 - Shaders/CylinderRaytracing.vsh | 80 - Shaders/PlainDisplay.fsh | 11 - Shaders/PlainDisplay.vsh | 10 - Shaders/SphereAOLookup.fsh | 45 - Shaders/SphereAOLookup.vsh | 19 - Shaders/SphereAmbientOcclusion.fsh | 62 - Shaders/SphereAmbientOcclusion.vsh | 53 - Shaders/SphereDepth.fsh | 27 - Shaders/SphereDepth.vsh | 37 - Shaders/SphereDepthWrite.fsh | 7 - Shaders/SphereDepthWrite.vsh | 20 - Shaders/SphereRaytracing.fsh | 64 - Shaders/SphereRaytracing.vsh | 40 - VCTitleCase.h | 36 - VCTitleCase.m | 169 -- VisualizationIcon.png | Bin 1827 -> 0 bytes VisualizationIcon@2x.png | Bin 2825 -> 0 bytes dbcreation.sh | 8 - greenButton.png | Bin 1491 -> 0 bytes main.m | 17 - molecules.sql | Bin 7168 -> 0 bytes redButton.png | Bin 1472 -> 0 bytes 160 files changed, 8324 insertions(+), 19310 deletions(-) delete mode 100644 1TRZ.pdb.gz delete mode 100644 57-download.png delete mode 100644 69-display.png delete mode 100644 98-palette.png delete mode 100644 98-palette@2x.png rename 3QE5.pdb.gz => BuiltInMolecules/3QE5.pdb.gz (100%) rename 1EVE.pdb.gz => BuiltInMolecules/Acetylcholinesterase.pdb.gz (100%) create mode 100644 BuiltInMolecules/Buckminsterfullerene.sdf create mode 100644 BuiltInMolecules/Caffeine.pdb create mode 100644 BuiltInMolecules/DNA.pdb create mode 100644 BuiltInMolecules/Heme.sdf create mode 100644 BuiltInMolecules/Insulin.pdb create mode 100644 BuiltInMolecules/Nanotube.pdb create mode 100644 BuiltInMolecules/TheoreticalBearing.pdb create mode 100644 BuiltInMolecules/TheoreticalXenonPump.pdb.gz create mode 100644 BuiltInMolecules/TransferRNA.pdb delete mode 100644 Default-Landscape.png delete mode 100644 Default-Landscape@2x.png delete mode 100644 Default-Portrait.png delete mode 100644 Default-Portrait@2x.png delete mode 100644 Default.png delete mode 100644 Default@2x.png delete mode 100644 Document-molecules-320.png delete mode 100644 Document-molecules-64.png delete mode 100644 English.lproj/Info.plist delete mode 100644 English.lproj/Localized.strings delete mode 100644 English.lproj/MainWindow.xib delete mode 100644 English.lproj/SLSMoleculeGLView.xib delete mode 100644 French.lproj/Info.plist delete mode 100644 French.lproj/Localized.strings delete mode 100644 French.lproj/MainWindow.xib delete mode 100644 French.lproj/SLSMoleculeGLView.xib delete mode 100644 GLProgram.h delete mode 100644 GLProgram.m delete mode 100644 GlyphishIconLicense.txt delete mode 100644 Info.plist delete mode 100644 LaunchScreen.storyboard delete mode 100644 Molecules.xcodeproj/larson.mode2v3 delete mode 100644 Molecules.xcodeproj/larson.pbxuser mode change 100755 => 100644 Molecules.xcodeproj/project.pbxproj create mode 100644 Molecules/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 Molecules/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 Molecules/Assets.xcassets/AppIcon.appiconset/MoleculesIcon-1024.png create mode 100644 Molecules/Assets.xcassets/Contents.json create mode 100644 Molecules/FileProcessing/Atom.swift create mode 100644 Molecules/FileProcessing/Bond.swift create mode 100644 Molecules/FileProcessing/Coordinate.swift create mode 100644 Molecules/FileProcessing/MolecularStructure.swift create mode 100644 Molecules/FileProcessing/MoleculeDocument.swift create mode 100644 Molecules/FileProcessing/PDBFile.swift create mode 100644 Molecules/FileProcessing/SDFFile.swift create mode 100644 Molecules/FileProcessing/XYZFile.swift create mode 100644 Molecules/Info.plist create mode 100644 Molecules/MoleculesApp.swift create mode 100644 Molecules/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 Molecules/Rendering/MetalRenderView.swift create mode 100644 Molecules/Rendering/MetalRenderingDevice.swift create mode 100644 Molecules/Rendering/MoleculeDisplayView.swift create mode 100644 Molecules/Rendering/MoleculeMetadataView.swift create mode 100644 Molecules/Rendering/Shaders/SphereRaytracing.metal delete mode 100644 MoleculesIcon.png delete mode 100644 MoleculesIcon72.png delete mode 100644 MoleculesIcon72@2x.png delete mode 100644 MoleculesIcon@2x.png create mode 100644 MoleculesTests/PDBFileTests.swift create mode 100644 MoleculesTests/SDFFileTests.swift rename caffeine.pdb.gz => MoleculesTests/TestMolecules/Caffeine.pdb.gz (100%) rename 1BNA.pdb.gz => MoleculesTests/TestMolecules/DNA.pdb.gz (100%) create mode 100644 MoleculesTests/TestMolecules/SampleMolecule.xyz rename 4TRA.pdb.gz => MoleculesTests/TestMolecules/TransferRNA.pdb.gz (100%) create mode 100644 MoleculesTests/XYZFileTests.swift create mode 100644 MoleculesUITests/MoleculesUITests.swift create mode 100644 MoleculesUITests/MoleculesUITestsLaunchTests.swift delete mode 100644 Molecules_Prefix.pch delete mode 100644 NSData+Gzip.h delete mode 100644 NSData+Gzip.m create mode 100755 README.md delete mode 100644 Resources-iPad/English.lproj/SLSMoleculeDownloadView.xib delete mode 100644 Resources-iPad/French.lproj/SLSMoleculeDownloadView.xib delete mode 100644 RotationIcon.png delete mode 100644 RotationIcon@2x.png delete mode 100644 RotationIconSelected.png delete mode 100644 RotationIconSelected@2x.png delete mode 100644 RotationIconiPad.png delete mode 100644 RotationIconiPad@2x.png delete mode 100644 RotationIconiPadCancel.png delete mode 100644 RotationIconiPadCancel@2x.png delete mode 100644 SLSAtomColorKeyController.h delete mode 100644 SLSAtomColorKeyController.m delete mode 100755 SLSCellTextView.h delete mode 100755 SLSCellTextView.m delete mode 100644 SLSMolecule+PDB.h delete mode 100644 SLSMolecule+PDB.m delete mode 100644 SLSMolecule+SDF.h delete mode 100644 SLSMolecule+SDF.m delete mode 100644 SLSMolecule.h delete mode 100644 SLSMolecule.m delete mode 100644 SLSMoleculeAppDelegate.h delete mode 100644 SLSMoleculeAppDelegate.m delete mode 100644 SLSMoleculeCustomDownloadViewController.h delete mode 100644 SLSMoleculeCustomDownloadViewController.m delete mode 100644 SLSMoleculeDataSourceViewController.h delete mode 100644 SLSMoleculeDataSourceViewController.m delete mode 100644 SLSMoleculeDetailViewController.h delete mode 100644 SLSMoleculeDetailViewController.m delete mode 100644 SLSMoleculeDownloadController.h delete mode 100644 SLSMoleculeDownloadController.m delete mode 100644 SLSMoleculeDownloadView.xib delete mode 100644 SLSMoleculeGLView.h delete mode 100644 SLSMoleculeGLView.m delete mode 100644 SLSMoleculeGLViewController.h delete mode 100644 SLSMoleculeGLViewController.m delete mode 100644 SLSMoleculeLibraryTableCell.h delete mode 100644 SLSMoleculeLibraryTableCell.m delete mode 100644 SLSMoleculeRootViewController.h delete mode 100644 SLSMoleculeRootViewController.m delete mode 100644 SLSMoleculeSearchViewController.h delete mode 100644 SLSMoleculeSearchViewController.m delete mode 100644 SLSMoleculeTableViewController.h delete mode 100644 SLSMoleculeTableViewController.m delete mode 100644 SLSMoleculeWebDetailViewController.h delete mode 100644 SLSMoleculeWebDetailViewController.m delete mode 100644 SLSMoleculeiPadRootViewController.h delete mode 100644 SLSMoleculeiPadRootViewController.m delete mode 100644 SLSOpenGLES11Renderer.h delete mode 100644 SLSOpenGLES11Renderer.m delete mode 100644 SLSOpenGLES20Renderer.h delete mode 100644 SLSOpenGLES20Renderer.m delete mode 100644 SLSOpenGLESRenderer.h delete mode 100644 SLSOpenGLESRenderer.m delete mode 100755 SLSTextViewController.h delete mode 100755 SLSTextViewController.m delete mode 100644 Shaders/CylinderAmbientOcclusion.fsh delete mode 100644 Shaders/CylinderAmbientOcclusion.vsh delete mode 100644 Shaders/CylinderDepth.fsh delete mode 100644 Shaders/CylinderDepth.vsh delete mode 100644 Shaders/CylinderRaytracing.fsh delete mode 100644 Shaders/CylinderRaytracing.vsh delete mode 100644 Shaders/PlainDisplay.fsh delete mode 100644 Shaders/PlainDisplay.vsh delete mode 100644 Shaders/SphereAOLookup.fsh delete mode 100644 Shaders/SphereAOLookup.vsh delete mode 100644 Shaders/SphereAmbientOcclusion.fsh delete mode 100644 Shaders/SphereAmbientOcclusion.vsh delete mode 100644 Shaders/SphereDepth.fsh delete mode 100644 Shaders/SphereDepth.vsh delete mode 100644 Shaders/SphereDepthWrite.fsh delete mode 100644 Shaders/SphereDepthWrite.vsh delete mode 100644 Shaders/SphereRaytracing.fsh delete mode 100644 Shaders/SphereRaytracing.vsh delete mode 100644 VCTitleCase.h delete mode 100644 VCTitleCase.m delete mode 100644 VisualizationIcon.png delete mode 100644 VisualizationIcon@2x.png delete mode 100755 dbcreation.sh delete mode 100644 greenButton.png delete mode 100644 main.m delete mode 100644 molecules.sql delete mode 100644 redButton.png diff --git a/.gitignore b/.gitignore index f12bf87..3002497 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ # Exclude the build directory build/* -examples/FilterShowcase/build* # Exclude temp nibs and swap files *~.nib diff --git a/1TRZ.pdb.gz b/1TRZ.pdb.gz deleted file mode 100644 index bb44f033c3ddd3ca99bb5d84ce89e229bf23c915..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23825 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%0Dr05wajZh7C4~}C(5mIZgE1&Yk;anc6bfkTm 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 XZ;r3KB)BAc?@0dkQM`;P0E7MqcW|Vj diff --git a/57-download.png b/57-download.png deleted file mode 100644 index a4e5884fa641d1a9e2c4ad6d215024f91a606bf7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 226 zcmeAS@N?(olHy`uVBq!ia0vp^!ayv+!3H9yAL9p7k|nMYCBgY=CFO}lsSJ)O`AMk? zp1FzXsX?iUDV2pMQ*D5XT0C7GLn>}1B_t$hus!N`_%omBk4Ori@)ZR;wo?by-Bz3o zVO-qCsQT}J`jHCfeC6g*d5mFg`_#5w=a@QfY8G5DR(QwEVZg9m WFGYZ-yXF$mJq(_%elF{r5}E*{q)|fv diff --git a/69-display.png b/69-display.png deleted file mode 100644 index de42c635e75ad2b9327b6cb3c5b95431bd094521..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 289 zcmV++0p9+JP)5z-&nFKX9a=KXTGs~1*i3{=K^?Imd zpJ|5hOgwq5Kdm|kh*%`P6MNzj3dd%g5?kUxoW_JXVn4$z@sm%`FM9H2QN`8#>1>7B`J%`Mj}rDSdbr@kYsv#V=OQJ_Ub@ nDj37BZ&@*k-;YUc>;)JAF9JotEAAM-00000NkvXXu0mjf>LGF9 diff --git a/98-palette.png b/98-palette.png deleted file mode 100644 index 31fc3bb844ec3a4f5e5b02b8aac277fe3ebe0da4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 452 zcmV;#0XzPQP)W$9b1aiv8fn zC9l`--FtTzrNWBA5g362{5a5@(@GmOEggp6-~qf_@d?P9nCG6A8g9W5TzY<=RTDYP zt2UhS`5bJ6eef*itZ`fxu_?F#Z>-!|iR|$c*b?knt9)gv%=;PV9D+}_HjXUwM7;OJ zN-KR>i|IKkFyn-WW3a^Fg`=bu&aHjt3m$4*yhTd;wSB3#4F2wbxW6 zn{DzjpYLd{m~0+(rMXF9TC)jO*}Gl!PwMalEKHGFtge;kCZ>TKt!utZe|+rVNUR{W zek-h_TK7pAn@L&(OK!3+fqhp}k9i?|GGU$s4D1BskhxLf3OW*$y!AORXI*<6DQ>Wl uCUG|JQPTnQ+*5t4Dzu}|gC@qm0t^6-C9JE|K4nS(0000126oEP)?IBuVfK4AU0Jix5{5PxmtHQ*!u zod;Y4oHWew0d>GQ2@f#qbP=}X4+Qc|hJaT@V|_qx$5LPm-6|vgj;KIbhK6to8*WO8 z3~__LkPv4tegb$Y+`9p|2gpj+IjdMdYmq3J5@p=zBoCA{R=*n}@8I=Kz-P($MM=Cb zD&G4Xu!d_r1ssNq`A1IV^LZWlt|F`W_!3o?IY4iSavE>}@C$GXP*KG78^ANbPrPOZ zd=I#e*XLRCK@Lg}=*X+$IlL!iUO0!|Tm@WI%-xsVV@m7^*BiJmqzahG^Au>3VZ_>U zNKh-p%IaMn_!$>+q{n< z_*X088C*3m96G6_Lm*VLjQgn*Ivzs4!h%~dHX7xCcf%ryRAnA@g-t4I)`}52T<#d2 zQ$Jsoe8dlH*pdYpd~jk{}&|B18g0 +c12c3c4c5c1c6c7c8c2c9c1c3c2c3c4c4c%10c5c5c6c6c7c7c%11c8c9c8c9c1c2c1c2c3c4c3c4c%10c5c5c6c6c7c7c%11c8c8c9c1c1c2c3c2c4c5c6c3c7c8c1c23 + +> +InChI=1S/C60/c1-2-5-6-3(1)8-12-10-4(1)9-11-7(2)17-21-13(5)23-24-14(6)22-18(8)28-20(12)30-26-16(10)15(9)25-29-19(11)27(17)37-41-31(21)33(23)43-44-34(24)32(22)42-38(28)48-40(30)46-36(26)35(25)45-39(29)47(37)55-49(41)51(43)57-52(44)50(42)56(48)59-54(46)53(45)58(55)60(57)59 + +> +XMWRBQBLMFGWIX-UHFFFAOYSA-N + +> +C60 + +$$$$ diff --git a/BuiltInMolecules/Caffeine.pdb b/BuiltInMolecules/Caffeine.pdb new file mode 100644 index 0000000..143490f --- /dev/null +++ b/BuiltInMolecules/Caffeine.pdb @@ -0,0 +1,41 @@ +COMPNDStr1 +REMARK [c]1([n+]([CH3])[c]([c]2([c]([n+]1[CH3])[n][cH][n+]2[CH3]))[O-])[O-] +ATOM 1 C 1 -1.799 0.022 0.602 +ATOM 2 N 1 -1.586 -0.945 -0.363 +ATOM 3 C 1 -2.731 -1.654 -0.954 +ATOM 4 C 1 -0.301 -1.248 -0.776 +ATOM 5 C 1 0.789 -0.574 -0.214 +ATOM 6 C 1 0.563 0.404 0.763 +ATOM 7 N 1 -0.728 0.694 1.163 +ATOM 8 C 1 -0.964 1.720 2.189 +ATOM 9 N 1 1.752 0.896 1.139 +ATOM 10 C 1 2.729 0.287 0.454 +ATOM 11 N 1 2.158 -0.638 -0.400 +ATOM 12 C 1 2.871 -1.524 -1.331 +ATOM 13 O 1 -0.101 -2.186 -1.712 +ATOM 14 O 1 -3.048 0.309 0.996 +ATOM 15 H 1 3.786 0.485 0.553 +ATOM 16 H 1 -2.947 -2.544 -0.368 +ATOM 17 H 1 -2.491 -1.941 -1.976 +ATOM 18 H 1 -3.601 -1.000 -0.955 +ATOM 19 H 1 -1.088 2.689 1.711 +ATOM 20 H 1 -0.114 1.756 2.868 +ATOM 21 H 1 -1.864 1.473 2.748 +ATOM 22 H 1 2.981 -1.026 -2.293 +ATOM 23 H 1 2.305 -2.444 -1.462 +ATOM 24 H 1 3.855 -1.757 -0.929 +TER +CONECT 1 2 7 14 0 +CONECT 2 1 3 4 0 +CONECT 3 2 16 17 18 +CONECT 4 2 5 13 0 +CONECT 5 4 6 11 0 +CONECT 6 5 7 9 0 +CONECT 7 1 6 8 0 +CONECT 8 7 19 20 21 +CONECT 9 6 10 0 0 +CONECT 10 9 11 15 0 +CONECT 11 5 10 12 0 +CONECT 12 11 22 23 24 +CONECT 13 4 0 0 0 +CONECT 14 1 0 0 0 diff --git a/BuiltInMolecules/DNA.pdb b/BuiltInMolecules/DNA.pdb new file mode 100644 index 0000000..b32795c --- /dev/null +++ b/BuiltInMolecules/DNA.pdb @@ -0,0 +1,847 @@ +HEADER DEOXYRIBONUCLEIC ACID 26-JAN-81 1BNA +TITLE STRUCTURE OF A B-DNA DODECAMER. CONFORMATION AND DYNAMICS +COMPND MOL_ID: 1; +COMPND 2 MOLECULE: DNA (5'-D(*CP*GP*CP*GP*AP*AP*TP*TP*CP*GP*CP*G)- +COMPND 3 3'); +COMPND 4 CHAIN: A, B; +COMPND 5 ENGINEERED: YES +SOURCE MOL_ID: 1; +SOURCE 2 SYNTHETIC: YES +KEYWDS B-DNA, DOUBLE HELIX +EXPDTA X-RAY DIFFRACTION +AUTHOR H.R.DREW,R.M.WING,T.TAKANO,C.BROKA,S.TANAKA,K.ITAKURA, +AUTHOR 2 R.E.DICKERSON +REVDAT 2 01-APR-03 1BNA 1 JRNL +REVDAT 1 21-MAY-81 1BNA 0 +JRNL AUTH H.R.DREW,R.M.WING,T.TAKANO,C.BROKA,S.TANAKA, +JRNL AUTH 2 K.ITAKURA,R.E.DICKERSON +JRNL TITL STRUCTURE OF A B-DNA DODECAMER: CONFORMATION AND +JRNL TITL 2 DYNAMICS. +JRNL REF PROC.NATL.ACAD.SCI.USA V. 78 2179 1981 +JRNL REFN ASTM PNASA6 US ISSN 0027-8424 +REMARK 1 +REMARK 1 REFERENCE 1 +REMARK 1 AUTH R.E.DICKERSON,H.R.DREW +REMARK 1 TITL KINEMATIC MODEL FOR B-DNA +REMARK 1 REF PROC.NATL.ACAD.SCI.USA V. 78 7318 1981 +REMARK 1 REFN ASTM PNASA6 US ISSN 0027-8424 +REMARK 1 REFERENCE 2 +REMARK 1 AUTH R.E.DICKERSON,H.R.DREW +REMARK 1 TITL STRUCTURE OF A B-DNA DODECAMER. II. INFLUENCE OF +REMARK 1 TITL 2 BASE SEQUENCE ON HELIX STRUCTURE +REMARK 1 REF J.MOL.BIOL. V. 149 761 1981 +REMARK 1 REFN ASTM JMOBAK UK ISSN 0022-2836 +REMARK 1 REFERENCE 3 +REMARK 1 AUTH H.R.DREW,R.E.DICKERSON +REMARK 1 TITL STRUCTURE OF A B-DNA DODECAMER. III. GEOMETRY OF +REMARK 1 TITL 2 HYDRATION +REMARK 1 REF J.MOL.BIOL. V. 151 535 1981 +REMARK 1 REFN ASTM JMOBAK UK ISSN 0022-2836 +REMARK 1 REFERENCE 4 +REMARK 1 AUTH R.WING,H.R.DREW,T.TAKANO,C.BROKA,S.TANAKA, +REMARK 1 AUTH 2 K.ITAKURA,R.E.DICKERSON +REMARK 1 TITL CRYSTAL STRUCTURE ANALYSIS OF A COMPLETE TURN OF +REMARK 1 TITL 2 B-DNA +REMARK 1 REF NATURE V. 287 755 1980 +REMARK 1 REFN ASTM NATUAS UK ISSN 0028-0836 +REMARK 2 +REMARK 2 RESOLUTION. 1.90 ANGSTROMS. +REMARK 3 +REMARK 3 REFINEMENT. +REMARK 3 PROGRAM : JACK-LEVITT +REMARK 3 AUTHORS : JACK,LEVITT +REMARK 3 +REMARK 3 DATA USED IN REFINEMENT. +REMARK 3 RESOLUTION RANGE HIGH (ANGSTROMS) : 1.90 +REMARK 3 RESOLUTION RANGE LOW (ANGSTROMS) : 8.00 +REMARK 3 DATA CUTOFF (SIGMA(F)) : NULL +REMARK 3 DATA CUTOFF HIGH (ABS(F)) : NULL +REMARK 3 DATA CUTOFF LOW (ABS(F)) : NULL +REMARK 3 COMPLETENESS (WORKING+TEST) (%) : NULL +REMARK 3 NUMBER OF REFLECTIONS : 2725 +REMARK 3 +REMARK 3 FIT TO DATA USED IN REFINEMENT. +REMARK 3 CROSS-VALIDATION METHOD : NULL +REMARK 3 FREE R VALUE TEST SET SELECTION : NULL +REMARK 3 R VALUE (WORKING SET) : 0.178 +REMARK 3 FREE R VALUE : NULL +REMARK 3 FREE R VALUE TEST SET SIZE (%) : NULL +REMARK 3 FREE R VALUE TEST SET COUNT : NULL +REMARK 3 ESTIMATED ERROR OF FREE R VALUE : NULL +REMARK 3 +REMARK 3 FIT IN THE HIGHEST RESOLUTION BIN. +REMARK 3 TOTAL NUMBER OF BINS USED : NULL +REMARK 3 BIN RESOLUTION RANGE HIGH (A) : NULL +REMARK 3 BIN RESOLUTION RANGE LOW (A) : NULL +REMARK 3 BIN COMPLETENESS (WORKING+TEST) (%) : NULL +REMARK 3 REFLECTIONS IN BIN (WORKING SET) : NULL +REMARK 3 BIN R VALUE (WORKING SET) : NULL +REMARK 3 BIN FREE R VALUE : NULL +REMARK 3 BIN FREE R VALUE TEST SET SIZE (%) : NULL +REMARK 3 BIN FREE R VALUE TEST SET COUNT : NULL +REMARK 3 ESTIMATED ERROR OF BIN FREE R VALUE : NULL +REMARK 3 +REMARK 3 NUMBER OF NON-HYDROGEN ATOMS USED IN REFINEMENT. +REMARK 3 PROTEIN ATOMS : 0 +REMARK 3 NUCLEIC ACID ATOMS : 486 +REMARK 3 HETEROGEN ATOMS : 0 +REMARK 3 SOLVENT ATOMS : 80 +REMARK 3 +REMARK 3 B VALUES. +REMARK 3 FROM WILSON PLOT (A**2) : NULL +REMARK 3 MEAN B VALUE (OVERALL, A**2) : NULL +REMARK 3 OVERALL ANISOTROPIC B VALUE. +REMARK 3 B11 (A**2) : NULL +REMARK 3 B22 (A**2) : NULL +REMARK 3 B33 (A**2) : NULL +REMARK 3 B12 (A**2) : NULL +REMARK 3 B13 (A**2) : NULL +REMARK 3 B23 (A**2) : NULL +REMARK 3 +REMARK 3 ESTIMATED COORDINATE ERROR. +REMARK 3 ESD FROM LUZZATI PLOT (A) : NULL +REMARK 3 ESD FROM SIGMAA (A) : NULL +REMARK 3 LOW RESOLUTION CUTOFF (A) : NULL +REMARK 3 +REMARK 3 CROSS-VALIDATED ESTIMATED COORDINATE ERROR. +REMARK 3 ESD FROM C-V LUZZATI PLOT (A) : NULL +REMARK 3 ESD FROM C-V SIGMAA (A) : NULL +REMARK 3 +REMARK 3 RMS DEVIATIONS FROM IDEAL VALUES. +REMARK 3 BOND LENGTHS (A) : NULL +REMARK 3 BOND ANGLES (DEGREES) : NULL +REMARK 3 DIHEDRAL ANGLES (DEGREES) : NULL +REMARK 3 IMPROPER ANGLES (DEGREES) : NULL +REMARK 3 +REMARK 3 ISOTROPIC THERMAL MODEL : NULL +REMARK 3 +REMARK 3 ISOTROPIC THERMAL FACTOR RESTRAINTS. RMS SIGMA +REMARK 3 MAIN-CHAIN BOND (A**2) : NULL ; NULL +REMARK 3 MAIN-CHAIN ANGLE (A**2) : NULL ; NULL +REMARK 3 SIDE-CHAIN BOND (A**2) : NULL ; NULL +REMARK 3 SIDE-CHAIN ANGLE (A**2) : NULL ; NULL +REMARK 3 +REMARK 3 NCS MODEL : NULL +REMARK 3 +REMARK 3 NCS RESTRAINTS. RMS SIGMA/WEIGHT +REMARK 3 GROUP 1 POSITIONAL (A) : NULL ; NULL +REMARK 3 GROUP 1 B-FACTOR (A**2) : NULL ; NULL +REMARK 3 +REMARK 3 PARAMETER FILE 1 : NULL +REMARK 3 TOPOLOGY FILE 1 : NULL +REMARK 3 +REMARK 3 OTHER REFINEMENT REMARKS: NULL +REMARK 4 +REMARK 4 1BNA COMPLIES WITH FORMAT V. 3.0, 1-DEC-2006 +REMARK 4 +REMARK 4 THIS IS THE REMEDIATED VERSION OF THIS PDB ENTRY. +REMARK 4 REMEDIATED DATA FILE REVISION 3.100 (2007-03-16) +REMARK 200 +REMARK 200 EXPERIMENTAL DETAILS +REMARK 200 EXPERIMENT TYPE : X-RAY DIFFRACTION +REMARK 200 DATE OF DATA COLLECTION : NULL +REMARK 200 TEMPERATURE (KELVIN) : NULL +REMARK 200 PH : NULL +REMARK 200 NUMBER OF CRYSTALS USED : NULL +REMARK 200 +REMARK 200 SYNCHROTRON (Y/N) : N +REMARK 200 RADIATION SOURCE : NULL +REMARK 200 BEAMLINE : NULL +REMARK 200 X-RAY GENERATOR MODEL : NULL +REMARK 200 MONOCHROMATIC OR LAUE (M/L) : NULL +REMARK 200 WAVELENGTH OR RANGE (A) : NULL +REMARK 200 MONOCHROMATOR : NULL +REMARK 200 OPTICS : NULL +REMARK 200 +REMARK 200 DETECTOR TYPE : DIFFRACTOMETER +REMARK 200 DETECTOR MANUFACTURER : NULL +REMARK 200 INTENSITY-INTEGRATION SOFTWARE : NULL +REMARK 200 DATA SCALING SOFTWARE : NULL +REMARK 200 +REMARK 200 NUMBER OF UNIQUE REFLECTIONS : 5534 +REMARK 200 RESOLUTION RANGE HIGH (A) : 1.900 +REMARK 200 RESOLUTION RANGE LOW (A) : 8.000 +REMARK 200 REJECTION CRITERIA (SIGMA(I)) : NULL +REMARK 200 +REMARK 200 OVERALL. +REMARK 200 COMPLETENESS FOR RANGE (%) : NULL +REMARK 200 DATA REDUNDANCY : NULL +REMARK 200 R MERGE (I) : NULL +REMARK 200 R SYM (I) : NULL +REMARK 200 FOR THE DATA SET : NULL +REMARK 200 +REMARK 200 IN THE HIGHEST RESOLUTION SHELL. +REMARK 200 HIGHEST RESOLUTION SHELL, RANGE HIGH (A) : NULL +REMARK 200 HIGHEST RESOLUTION SHELL, RANGE LOW (A) : NULL +REMARK 200 COMPLETENESS FOR SHELL (%) : NULL +REMARK 200 DATA REDUNDANCY IN SHELL : NULL +REMARK 200 R MERGE FOR SHELL (I) : NULL +REMARK 200 R SYM FOR SHELL (I) : NULL +REMARK 200 FOR SHELL : NULL +REMARK 200 +REMARK 200 DIFFRACTION PROTOCOL: NULL +REMARK 200 METHOD USED TO DETERMINE THE STRUCTURE: NULL +REMARK 200 SOFTWARE USED: NULL +REMARK 200 STARTING MODEL: NULL +REMARK 200 +REMARK 200 REMARK: NULL +REMARK 280 +REMARK 280 CRYSTAL +REMARK 280 SOLVENT CONTENT, VS (%): NULL +REMARK 280 MATTHEWS COEFFICIENT, VM (ANGSTROMS**3/DA): NULL +REMARK 280 +REMARK 280 CRYSTALLIZATION CONDITIONS: VAPOR DIFFUSION, TEMPERATURE 290.00K +REMARK 290 +REMARK 290 CRYSTALLOGRAPHIC SYMMETRY +REMARK 290 SYMMETRY OPERATORS FOR SPACE GROUP: P 21 21 21 +REMARK 290 +REMARK 290 SYMOP SYMMETRY +REMARK 290 NNNMMM OPERATOR +REMARK 290 1555 X,Y,Z +REMARK 290 2555 1/2-X,-Y,1/2+Z +REMARK 290 3555 -X,1/2+Y,1/2-Z +REMARK 290 4555 1/2+X,1/2-Y,-Z +REMARK 290 +REMARK 290 WHERE NNN -> OPERATOR NUMBER +REMARK 290 MMM -> TRANSLATION VECTOR +REMARK 290 +REMARK 290 CRYSTALLOGRAPHIC SYMMETRY TRANSFORMATIONS +REMARK 290 THE FOLLOWING TRANSFORMATIONS OPERATE ON THE ATOM/HETATM +REMARK 290 RECORDS IN THIS ENTRY TO PRODUCE CRYSTALLOGRAPHICALLY +REMARK 290 RELATED MOLECULES. +REMARK 290 SMTRY1 1 1.000000 0.000000 0.000000 0.00000 +REMARK 290 SMTRY2 1 0.000000 1.000000 0.000000 0.00000 +REMARK 290 SMTRY3 1 0.000000 0.000000 1.000000 0.00000 +REMARK 290 SMTRY1 2 -1.000000 0.000000 0.000000 12.43500 +REMARK 290 SMTRY2 2 0.000000 -1.000000 0.000000 0.00000 +REMARK 290 SMTRY3 2 0.000000 0.000000 1.000000 33.10000 +REMARK 290 SMTRY1 3 -1.000000 0.000000 0.000000 0.00000 +REMARK 290 SMTRY2 3 0.000000 1.000000 0.000000 20.19500 +REMARK 290 SMTRY3 3 0.000000 0.000000 -1.000000 33.10000 +REMARK 290 SMTRY1 4 1.000000 0.000000 0.000000 12.43500 +REMARK 290 SMTRY2 4 0.000000 -1.000000 0.000000 20.19500 +REMARK 290 SMTRY3 4 0.000000 0.000000 -1.000000 0.00000 +REMARK 290 +REMARK 290 REMARK: NULL +REMARK 300 +REMARK 300 BIOMOLECULE: 1 +REMARK 300 THIS ENTRY CONTAINS THE CRYSTALLOGRAPHIC ASYMMETRIC UNIT +REMARK 300 WHICH CONSISTS OF 2 CHAIN(S). SEE REMARK 350 FOR +REMARK 300 INFORMATION ON GENERATING THE BIOLOGICAL MOLECULE(S). +REMARK 350 +REMARK 350 GENERATING THE BIOMOLECULE +REMARK 350 COORDINATES FOR A COMPLETE MULTIMER REPRESENTING THE KNOWN +REMARK 350 BIOLOGICALLY SIGNIFICANT OLIGOMERIZATION STATE OF THE +REMARK 350 MOLECULE CAN BE GENERATED BY APPLYING BIOMT TRANSFORMATIONS +REMARK 350 GIVEN BELOW. BOTH NON-CRYSTALLOGRAPHIC AND +REMARK 350 CRYSTALLOGRAPHIC OPERATIONS ARE GIVEN. +REMARK 350 +REMARK 350 BIOMOLECULE: 1 +REMARK 350 APPLY THE FOLLOWING TO CHAINS: A, B +REMARK 350 BIOMT1 1 1.000000 0.000000 0.000000 0.00000 +REMARK 350 BIOMT2 1 0.000000 1.000000 0.000000 0.00000 +REMARK 350 BIOMT3 1 0.000000 0.000000 1.000000 0.00000 +REMARK 500 +REMARK 500 GEOMETRY AND STEREOCHEMISTRY +REMARK 500 SUBTOPIC: CLOSE CONTACTS IN SAME ASYMMETRIC UNIT +REMARK 500 +REMARK 500 THE FOLLOWING ATOMS ARE IN CLOSE CONTACT. +REMARK 500 +REMARK 500 ATM1 RES C SSEQI ATM2 RES C SSEQI +REMARK 500 O HOH 62 O HOH 77 1.61 +REMARK 500 OP2 DA A 6 O HOH 65 1.89 +REMARK 500 OP2 DG A 10 O HOH 70 2.02 +REMARK 500 O HOH 54 O HOH 86 2.07 +REMARK 500 O HOH 63 O HOH 77 2.09 +REMARK 500 O HOH 31 O HOH 99 2.12 +REMARK 525 +REMARK 525 SOLVENT +REMARK 525 THE FOLLOWING SOLVENT MOLECULES LIE FARTHER THAN EXPECTED +REMARK 525 FROM THE PROTEIN OR NUCLEIC ACID MOLECULE AND MAY BE +REMARK 525 ASSOCIATED WITH A SYMMETRY RELATED MOLECULE (M=MODEL +REMARK 525 NUMBER; RES=RESIDUE NAME; C=CHAIN IDENTIFIER; SSEQ=SEQUENCE +REMARK 525 NUMBER; I=INSERTION CODE): +REMARK 525 +REMARK 525 M RES CSSEQI +REMARK 525 HOH 89 DISTANCE = 6.29 ANGSTROMS +REMARK 525 HOH 94 DISTANCE = 5.58 ANGSTROMS +SEQRES 1 A 12 DC DG DC DG DA DA DT DT DC DG DC DG +SEQRES 1 B 12 DC DG DC DG DA DA DT DT DC DG DC DG +FORMUL 3 HOH *80(H2 O) +CRYST1 24.870 40.390 66.200 90.00 90.00 90.00 P 21 21 21 8 +ORIGX1 1.000000 0.000000 0.000000 0.00000 +ORIGX2 0.000000 1.000000 0.000000 0.00000 +ORIGX3 0.000000 0.000000 1.000000 0.00000 +SCALE1 0.040209 0.000000 0.000000 0.00000 +SCALE2 0.000000 0.024759 0.000000 0.00000 +SCALE3 0.000000 0.000000 0.015106 0.00000 +ATOM 1 O5' DC A 1 18.935 34.195 25.617 1.00 64.35 O +ATOM 2 C5' DC A 1 19.130 33.921 24.219 1.00 44.69 C +ATOM 3 C4' DC A 1 19.961 32.668 24.100 1.00 31.28 C +ATOM 4 O4' DC A 1 19.360 31.583 24.852 1.00 37.45 O +ATOM 5 C3' DC A 1 20.172 32.122 22.694 1.00 46.72 C +ATOM 6 O3' DC A 1 21.350 31.325 22.681 1.00 48.89 O +ATOM 7 C2' DC A 1 18.948 31.223 22.647 1.00 30.88 C +ATOM 8 C1' DC A 1 19.231 30.482 23.944 1.00 36.58 C +ATOM 9 N1 DC A 1 18.070 29.661 24.380 1.00 40.51 N +ATOM 10 C2 DC A 1 18.224 28.454 25.015 1.00 16.62 C +ATOM 11 O2 DC A 1 19.360 28.014 25.214 1.00 27.75 O +ATOM 12 N3 DC A 1 17.143 27.761 25.377 1.00 20.55 N +ATOM 13 C4 DC A 1 15.917 28.226 25.120 1.00 34.72 C +ATOM 14 N4 DC A 1 14.828 27.477 25.444 1.00 40.31 N +ATOM 15 C5 DC A 1 15.719 29.442 24.471 1.00 30.78 C +ATOM 16 C6 DC A 1 16.843 30.171 24.101 1.00 25.90 C +ATOM 17 P DG A 2 22.409 31.286 21.483 1.00 58.85 P +ATOM 18 OP1 DG A 2 23.536 32.157 21.851 1.00 57.82 O +ATOM 19 OP2 DG A 2 21.822 31.459 20.139 1.00 78.33 O +ATOM 20 O5' DG A 2 22.840 29.751 21.498 1.00 40.36 O +ATOM 21 C5' DG A 2 23.543 29.175 22.594 1.00 47.19 C +ATOM 22 C4' DG A 2 23.494 27.709 22.279 1.00 47.81 C +ATOM 23 O4' DG A 2 22.193 27.252 22.674 1.00 38.76 O +ATOM 24 C3' DG A 2 23.693 27.325 20.807 1.00 28.58 C +ATOM 25 O3' DG A 2 24.723 26.320 20.653 1.00 40.44 O +ATOM 26 C2' DG A 2 22.273 26.885 20.416 1.00 21.14 C +ATOM 27 C1' DG A 2 21.721 26.304 21.716 1.00 33.95 C +ATOM 28 N9 DG A 2 20.237 26.470 21.780 1.00 34.00 N +ATOM 29 C8 DG A 2 19.526 27.584 21.429 1.00 36.47 C +ATOM 30 N7 DG A 2 18.207 27.455 21.636 1.00 32.37 N +ATOM 31 C5 DG A 2 18.083 26.212 22.142 1.00 15.06 C +ATOM 32 C6 DG A 2 16.904 25.525 22.545 1.00 11.88 C +ATOM 33 O6 DG A 2 15.739 25.916 22.518 1.00 21.30 O +ATOM 34 N1 DG A 2 17.197 24.279 23.037 1.00 15.44 N +ATOM 35 C2 DG A 2 18.434 23.717 23.155 1.00 9.63 C +ATOM 36 N2 DG A 2 18.508 22.456 23.668 1.00 16.69 N +ATOM 37 N3 DG A 2 19.537 24.360 22.770 1.00 30.98 N +ATOM 38 C4 DG A 2 19.290 25.594 22.274 1.00 18.56 C +ATOM 39 P DC A 3 25.064 25.621 19.252 1.00 44.67 P +ATOM 40 OP1 DC A 3 26.506 25.316 19.220 1.00 53.89 O +ATOM 41 OP2 DC A 3 24.559 26.412 18.115 1.00 57.79 O +ATOM 42 O5' DC A 3 24.260 24.246 19.327 1.00 35.42 O +ATOM 43 C5' DC A 3 24.584 23.285 20.335 1.00 45.75 C +ATOM 44 C4' DC A 3 23.523 22.233 20.245 1.00 43.02 C +ATOM 45 O4' DC A 3 22.256 22.844 20.453 1.00 36.85 O +ATOM 46 C3' DC A 3 23.424 21.557 18.903 1.00 40.14 C +ATOM 47 O3' DC A 3 24.121 20.309 18.928 1.00 49.62 O +ATOM 48 C2' DC A 3 21.930 21.406 18.661 1.00 53.79 C +ATOM 49 C1' DC A 3 21.278 21.966 19.909 1.00 22.18 C +ATOM 50 N1 DC A 3 20.196 22.889 19.521 1.00 25.44 N +ATOM 51 C2 DC A 3 18.909 22.584 19.816 1.00 19.81 C +ATOM 52 O2 DC A 3 18.685 21.512 20.382 1.00 29.92 O +ATOM 53 N3 DC A 3 17.935 23.447 19.502 1.00 21.59 N +ATOM 54 C4 DC A 3 18.217 24.603 18.897 1.00 14.01 C +ATOM 55 N4 DC A 3 17.221 25.499 18.629 1.00 26.88 N +ATOM 56 C5 DC A 3 19.526 24.945 18.571 1.00 27.59 C +ATOM 57 C6 DC A 3 20.537 24.048 18.899 1.00 27.05 C +ATOM 58 P DG A 4 24.249 19.412 17.617 1.00 44.54 P +ATOM 59 OP1 DG A 4 25.420 18.535 17.765 1.00 61.90 O +ATOM 60 OP2 DG A 4 24.208 20.296 16.440 1.00 37.36 O +ATOM 61 O5' DG A 4 22.931 18.537 17.670 1.00 32.01 O +ATOM 62 C5' DG A 4 22.714 17.625 18.753 1.00 37.89 C +ATOM 63 C4' DG A 4 21.393 16.960 18.505 1.00 53.00 C +ATOM 64 O4' DG A 4 20.353 17.952 18.496 1.00 38.79 O +ATOM 65 C3' DG A 4 21.264 16.229 17.176 1.00 56.72 C +ATOM 66 O3' DG A 4 20.284 15.214 17.238 1.00 64.12 O +ATOM 67 C2' DG A 4 20.793 17.368 16.288 1.00 40.81 C +ATOM 68 C1' DG A 4 19.716 17.901 17.218 1.00 30.52 C +ATOM 69 N9 DG A 4 19.305 19.281 16.869 1.00 28.53 N +ATOM 70 C8 DG A 4 20.017 20.263 16.232 1.00 27.82 C +ATOM 71 N7 DG A 4 19.313 21.394 16.077 1.00 28.01 N +ATOM 72 C5 DG A 4 18.121 21.100 16.635 1.00 23.22 C +ATOM 73 C6 DG A 4 16.952 21.904 16.749 1.00 29.21 C +ATOM 74 O6 DG A 4 16.769 23.057 16.368 1.00 38.58 O +ATOM 75 N1 DG A 4 15.933 21.214 17.352 1.00 27.94 N +ATOM 76 C2 DG A 4 15.972 19.930 17.816 1.00 23.44 C +ATOM 77 N2 DG A 4 14.831 19.416 18.353 1.00 42.64 N +ATOM 78 N3 DG A 4 17.068 19.179 17.717 1.00 21.56 N +ATOM 79 C4 DG A 4 18.084 19.825 17.121 1.00 23.44 C +ATOM 80 P DA A 5 20.356 13.969 16.245 1.00 57.01 P +ATOM 81 OP1 DA A 5 21.116 12.891 16.892 1.00 58.59 O +ATOM 82 OP2 DA A 5 20.837 14.423 14.910 1.00 51.96 O +ATOM 83 O5' DA A 5 18.810 13.581 16.161 1.00 47.12 O +ATOM 84 C5' DA A 5 18.015 13.569 17.362 1.00 47.67 C +ATOM 85 C4' DA A 5 16.672 14.088 16.957 1.00 64.79 C +ATOM 86 O4' DA A 5 16.842 15.447 16.561 1.00 47.60 O +ATOM 87 C3' DA A 5 16.019 13.393 15.764 1.00 51.50 C +ATOM 88 O3' DA A 5 14.762 12.796 16.120 1.00 52.18 O +ATOM 89 C2' DA A 5 15.952 14.498 14.696 1.00 45.00 C +ATOM 90 C1' DA A 5 15.851 15.732 15.569 1.00 26.88 C +ATOM 91 N9 DA A 5 16.391 16.916 14.867 1.00 16.69 N +ATOM 92 C8 DA A 5 17.658 17.103 14.382 1.00 28.14 C +ATOM 93 N7 DA A 5 17.863 18.346 13.913 1.00 34.85 N +ATOM 94 C5 DA A 5 16.673 18.953 14.098 1.00 22.49 C +ATOM 95 C6 DA A 5 16.230 20.279 13.819 1.00 18.12 C +ATOM 96 N6 DA A 5 17.045 21.222 13.268 1.00 29.30 N +ATOM 97 N1 DA A 5 14.966 20.578 14.118 1.00 27.61 N +ATOM 98 C2 DA A 5 14.178 19.652 14.669 1.00 18.53 C +ATOM 99 N3 DA A 5 14.463 18.392 14.984 1.00 29.16 N +ATOM 100 C4 DA A 5 15.750 18.110 14.661 1.00 15.08 C +ATOM 101 P DA A 6 13.866 12.006 15.063 1.00 43.68 P +ATOM 102 OP1 DA A 6 13.028 11.039 15.800 1.00 42.55 O +ATOM 103 OP2 DA A 6 14.715 11.499 13.968 1.00 54.20 O +ATOM 104 O5' DA A 6 12.879 13.111 14.480 1.00 28.20 O +ATOM 105 C5' DA A 6 11.802 13.597 15.290 1.00 42.29 C +ATOM 106 C4' DA A 6 11.111 14.603 14.435 1.00 33.23 C +ATOM 107 O4' DA A 6 12.152 15.460 13.962 1.00 41.48 O +ATOM 108 C3' DA A 6 10.417 14.070 13.187 1.00 18.16 C +ATOM 109 O3' DA A 6 9.007 14.369 13.181 1.00 30.42 O +ATOM 110 C2' DA A 6 11.240 14.692 12.061 1.00 52.97 C +ATOM 111 C1' DA A 6 11.699 15.974 12.719 1.00 38.93 C +ATOM 112 N9 DA A 6 12.918 16.526 12.078 1.00 19.06 N +ATOM 113 C8 DA A 6 14.115 15.899 11.868 1.00 17.83 C +ATOM 114 N7 DA A 6 15.049 16.714 11.356 1.00 29.55 N +ATOM 115 C5 DA A 6 14.416 17.901 11.246 1.00 19.88 C +ATOM 116 C6 DA A 6 14.873 19.187 10.815 1.00 17.26 C +ATOM 117 N6 DA A 6 16.161 19.418 10.427 1.00 19.85 N +ATOM 118 N1 DA A 6 13.999 20.191 10.852 1.00 17.93 N +ATOM 119 C2 DA A 6 12.753 19.962 11.272 1.00 23.00 C +ATOM 120 N3 DA A 6 12.210 18.824 11.698 1.00 21.37 N +ATOM 121 C4 DA A 6 13.116 17.823 11.657 1.00 15.93 C +ATOM 122 P DT A 7 8.081 14.050 11.915 1.00 40.72 P +ATOM 123 OP1 DT A 7 6.668 13.960 12.342 1.00 46.75 O +ATOM 124 OP2 DT A 7 8.600 12.894 11.137 1.00 42.53 O +ATOM 125 O5' DT A 7 8.239 15.387 11.076 1.00 35.21 O +ATOM 126 C5' DT A 7 7.907 16.635 11.686 1.00 34.88 C +ATOM 127 C4' DT A 7 8.162 17.628 10.598 1.00 31.45 C +ATOM 128 O4' DT A 7 9.543 17.580 10.279 1.00 46.82 O +ATOM 129 C3' DT A 7 7.461 17.284 9.296 1.00 23.76 C +ATOM 130 O3' DT A 7 6.251 18.034 9.162 1.00 44.27 O +ATOM 131 C2' DT A 7 8.532 17.527 8.223 1.00 26.30 C +ATOM 132 C1' DT A 7 9.644 18.209 9.019 1.00 28.96 C +ATOM 133 N1 DT A 7 11.021 17.903 8.565 1.00 20.47 N +ATOM 134 C2 DT A 7 11.822 18.923 8.176 1.00 28.01 C +ATOM 135 O2 DT A 7 11.383 20.077 8.143 1.00 40.01 O +ATOM 136 N3 DT A 7 13.119 18.641 7.852 1.00 27.94 N +ATOM 137 C4 DT A 7 13.633 17.372 7.882 1.00 15.14 C +ATOM 138 O4 DT A 7 14.830 17.222 7.619 1.00 32.54 O +ATOM 139 C5 DT A 7 12.781 16.325 8.235 1.00 10.83 C +ATOM 140 C7 DT A 7 13.269 14.902 8.236 1.00 36.33 C +ATOM 141 C6 DT A 7 11.465 16.616 8.594 1.00 12.19 C +ATOM 142 P DT A 8 5.384 17.990 7.824 1.00 49.10 P +ATOM 143 OP1 DT A 8 4.025 18.444 8.180 1.00 41.11 O +ATOM 144 OP2 DT A 8 5.458 16.668 7.160 1.00 39.21 O +ATOM 145 O5' DT A 8 6.086 19.118 6.927 1.00 48.80 O +ATOM 146 C5' DT A 8 6.146 20.478 7.418 1.00 34.73 C +ATOM 147 C4' DT A 8 6.995 21.229 6.438 1.00 28.73 C +ATOM 148 O4' DT A 8 8.188 20.458 6.284 1.00 39.07 O +ATOM 149 C3' DT A 8 6.418 21.332 5.029 1.00 37.88 C +ATOM 150 O3' DT A 8 5.967 22.667 4.696 1.00 52.04 O +ATOM 151 C2' DT A 8 7.513 20.718 4.139 1.00 32.80 C +ATOM 152 C1' DT A 8 8.736 20.855 5.034 1.00 36.58 C +ATOM 153 N1 DT A 8 9.823 19.876 4.759 1.00 24.57 N +ATOM 154 C2 DT A 8 11.086 20.316 4.494 1.00 19.41 C +ATOM 155 O2 DT A 8 11.324 21.516 4.389 1.00 32.74 O +ATOM 156 N3 DT A 8 12.094 19.403 4.412 1.00 25.12 N +ATOM 157 C4 DT A 8 11.876 18.060 4.551 1.00 31.35 C +ATOM 158 O4 DT A 8 12.858 17.317 4.503 1.00 28.53 O +ATOM 159 C5 DT A 8 10.569 17.611 4.765 1.00 22.80 C +ATOM 160 C7 DT A 8 10.261 16.140 4.896 1.00 24.98 C +ATOM 161 C6 DT A 8 9.545 18.548 4.904 1.00 20.28 C +ATOM 162 P DC A 9 5.531 23.071 3.209 1.00 48.97 P +ATOM 163 OP1 DC A 9 4.648 24.244 3.269 1.00 62.33 O +ATOM 164 OP2 DC A 9 5.010 21.905 2.470 1.00 51.53 O +ATOM 165 O5' DC A 9 6.926 23.547 2.611 1.00 43.99 O +ATOM 166 C5' DC A 9 7.636 24.627 3.249 1.00 50.86 C +ATOM 167 C4' DC A 9 8.897 24.853 2.457 1.00 46.66 C +ATOM 168 O4' DC A 9 9.638 23.627 2.448 1.00 42.69 O +ATOM 169 C3' DC A 9 8.717 25.240 0.998 1.00 56.96 C +ATOM 170 O3' DC A 9 9.470 26.414 0.667 1.00 63.54 O +ATOM 171 C2' DC A 9 9.126 23.965 0.253 1.00 50.41 C +ATOM 172 C1' DC A 9 10.241 23.483 1.157 1.00 41.08 C +ATOM 173 N1 DC A 9 10.524 22.022 1.015 1.00 37.23 N +ATOM 174 C2 DC A 9 11.814 21.603 0.840 1.00 40.54 C +ATOM 175 O2 DC A 9 12.691 22.447 0.670 1.00 43.89 O +ATOM 176 N3 DC A 9 12.106 20.297 0.873 1.00 32.57 N +ATOM 177 C4 DC A 9 11.141 19.395 1.046 1.00 24.65 C +ATOM 178 N4 DC A 9 11.461 18.075 1.089 1.00 27.84 N +ATOM 179 C5 DC A 9 9.803 19.775 1.177 1.00 17.61 C +ATOM 180 C6 DC A 9 9.499 21.133 1.167 1.00 30.63 C +ATOM 181 P DG A 10 9.055 27.333 -0.581 1.00 65.48 P +ATOM 182 OP1 DG A 10 9.496 28.717 -0.258 1.00 59.09 O +ATOM 183 OP2 DG A 10 7.632 27.106 -0.947 1.00 45.71 O +ATOM 184 O5' DG A 10 9.954 26.765 -1.771 1.00 70.30 O +ATOM 185 C5' DG A 10 11.382 26.940 -1.720 1.00 71.73 C +ATOM 186 C4' DG A 10 11.972 26.090 -2.802 1.00 58.69 C +ATOM 187 O4' DG A 10 11.802 24.724 -2.404 1.00 41.03 O +ATOM 188 C3' DG A 10 11.327 26.178 -4.188 1.00 45.61 C +ATOM 189 O3' DG A 10 12.311 26.096 -5.214 1.00 52.70 O +ATOM 190 C2' DG A 10 10.414 24.962 -4.186 1.00 36.02 C +ATOM 191 C1' DG A 10 11.429 24.028 -3.587 1.00 50.90 C +ATOM 192 N9 DG A 10 10.890 22.713 -3.200 1.00 45.86 N +ATOM 193 C8 DG A 10 9.616 22.315 -2.910 1.00 44.49 C +ATOM 194 N7 DG A 10 9.541 21.009 -2.613 1.00 39.96 N +ATOM 195 C5 DG A 10 10.818 20.588 -2.718 1.00 38.99 C +ATOM 196 C6 DG A 10 11.376 19.292 -2.511 1.00 35.78 C +ATOM 197 O6 DG A 10 10.813 18.252 -2.179 1.00 34.90 O +ATOM 198 N1 DG A 10 12.729 19.299 -2.720 1.00 23.54 N +ATOM 199 C2 DG A 10 13.498 20.365 -3.082 1.00 8.73 C +ATOM 200 N2 DG A 10 14.834 20.169 -3.237 1.00 23.15 N +ATOM 201 N3 DG A 10 12.982 21.573 -3.267 1.00 24.68 N +ATOM 202 C4 DG A 10 11.656 21.601 -3.061 1.00 31.53 C +ATOM 203 P DC A 11 12.763 27.421 -5.980 1.00 60.62 P +ATOM 204 OP1 DC A 11 12.796 28.572 -5.049 1.00 63.74 O +ATOM 205 OP2 DC A 11 11.886 27.542 -7.164 1.00 52.44 O +ATOM 206 O5' DC A 11 14.272 27.086 -6.366 1.00 57.57 O +ATOM 207 C5' DC A 11 15.275 27.108 -5.318 1.00 54.70 C +ATOM 208 C4' DC A 11 16.222 25.946 -5.510 1.00 72.51 C +ATOM 209 O4' DC A 11 15.443 24.754 -5.397 1.00 47.18 O +ATOM 210 C3' DC A 11 16.942 25.827 -6.848 1.00 29.82 C +ATOM 211 O3' DC A 11 18.340 25.511 -6.701 1.00 43.53 O +ATOM 212 C2' DC A 11 16.118 24.767 -7.578 1.00 51.34 C +ATOM 213 C1' DC A 11 15.856 23.836 -6.414 1.00 30.07 C +ATOM 214 N1 DC A 11 14.672 22.975 -6.637 1.00 23.25 N +ATOM 215 C2 DC A 11 14.802 21.628 -6.529 1.00 20.38 C +ATOM 216 O2 DC A 11 15.924 21.178 -6.314 1.00 38.77 O +ATOM 217 N3 DC A 11 13.723 20.842 -6.627 1.00 15.92 N +ATOM 218 C4 DC A 11 12.515 21.373 -6.836 1.00 15.82 C +ATOM 219 N4 DC A 11 11.410 20.574 -6.872 1.00 28.04 N +ATOM 220 C5 DC A 11 12.348 22.744 -6.978 1.00 26.17 C +ATOM 221 C6 DC A 11 13.470 23.558 -6.869 1.00 35.50 C +ATOM 222 P DG A 12 19.331 25.774 -7.925 1.00 55.98 P +ATOM 223 OP1 DG A 12 20.704 25.976 -7.408 1.00 45.83 O +ATOM 224 OP2 DG A 12 18.763 26.851 -8.758 1.00 44.26 O +ATOM 225 O5' DG A 12 19.302 24.412 -8.763 1.00 62.63 O +ATOM 226 C5' DG A 12 20.109 23.284 -8.359 1.00 69.50 C +ATOM 227 C4' DG A 12 19.748 22.167 -9.299 1.00 39.92 C +ATOM 228 O4' DG A 12 18.350 21.969 -9.139 1.00 32.00 O +ATOM 229 C3' DG A 12 19.921 22.404 -10.815 1.00 50.39 C +ATOM 230 O3' DG A 12 20.985 21.635 -11.401 1.00 64.13 O +ATOM 231 C2' DG A 12 18.535 22.062 -11.381 1.00 36.18 C +ATOM 232 C1' DG A 12 17.965 21.200 -10.269 1.00 24.79 C +ATOM 233 N9 DG A 12 16.493 21.220 -10.265 1.00 28.44 N +ATOM 234 C8 DG A 12 15.663 22.289 -10.478 1.00 31.85 C +ATOM 235 N7 DG A 12 14.368 21.958 -10.390 1.00 38.26 N +ATOM 236 C5 DG A 12 14.388 20.640 -10.102 1.00 28.99 C +ATOM 237 C6 DG A 12 13.301 19.742 -9.856 1.00 42.63 C +ATOM 238 O6 DG A 12 12.091 19.967 -9.857 1.00 49.17 O +ATOM 239 N1 DG A 12 13.750 18.466 -9.625 1.00 40.15 N +ATOM 240 C2 DG A 12 15.042 18.043 -9.605 1.00 33.42 C +ATOM 241 N2 DG A 12 15.259 16.717 -9.406 1.00 40.53 N +ATOM 242 N3 DG A 12 16.061 18.885 -9.792 1.00 37.34 N +ATOM 243 C4 DG A 12 15.660 20.156 -10.027 1.00 31.14 C +TER 244 DG A 12 +ATOM 245 O5' DC B 13 7.458 11.884 -9.070 1.00 66.23 O +ATOM 246 C5' DC B 13 8.252 10.968 -9.854 1.00 71.49 C +ATOM 247 C4' DC B 13 9.714 11.141 -9.512 1.00 56.82 C +ATOM 248 O4' DC B 13 10.144 12.455 -9.908 1.00 57.92 O +ATOM 249 C3' DC B 13 10.103 10.989 -8.055 1.00 34.34 C +ATOM 250 O3' DC B 13 11.293 10.221 -7.904 1.00 42.11 O +ATOM 251 C2' DC B 13 10.254 12.437 -7.607 1.00 29.08 C +ATOM 252 C1' DC B 13 10.896 13.044 -8.837 1.00 38.40 C +ATOM 253 N1 DC B 13 10.575 14.487 -8.944 1.00 34.33 N +ATOM 254 C2 DC B 13 11.559 15.430 -9.006 1.00 22.98 C +ATOM 255 O2 DC B 13 12.725 15.066 -8.932 1.00 50.83 O +ATOM 256 N3 DC B 13 11.246 16.714 -9.193 1.00 37.14 N +ATOM 257 C4 DC B 13 9.980 17.088 -9.334 1.00 42.60 C +ATOM 258 N4 DC B 13 9.698 18.395 -9.589 1.00 54.91 N +ATOM 259 C5 DC B 13 8.939 16.162 -9.274 1.00 56.67 C +ATOM 260 C6 DC B 13 9.265 14.824 -9.080 1.00 49.21 C +ATOM 261 P DG B 14 11.602 9.510 -6.502 1.00 60.42 P +ATOM 262 OP1 DG B 14 11.666 8.032 -6.664 1.00 57.44 O +ATOM 263 OP2 DG B 14 10.644 10.010 -5.494 1.00 46.07 O +ATOM 264 O5' DG B 14 13.051 10.094 -6.177 1.00 50.94 O +ATOM 265 C5' DG B 14 14.100 10.021 -7.156 1.00 34.84 C +ATOM 266 C4' DG B 14 15.113 10.992 -6.657 1.00 48.06 C +ATOM 267 O4' DG B 14 14.556 12.300 -6.755 1.00 37.01 O +ATOM 268 C3' DG B 14 15.445 10.806 -5.189 1.00 50.58 C +ATOM 269 O3' DG B 14 16.836 10.560 -5.013 1.00 51.98 O +ATOM 270 C2' DG B 14 14.937 12.100 -4.529 1.00 40.32 C +ATOM 271 C1' DG B 14 15.058 13.086 -5.671 1.00 46.69 C +ATOM 272 N9 DG B 14 14.036 14.140 -5.536 1.00 29.17 N +ATOM 273 C8 DG B 14 12.710 13.957 -5.259 1.00 23.48 C +ATOM 274 N7 DG B 14 12.016 15.103 -5.269 1.00 37.54 N +ATOM 275 C5 DG B 14 12.937 16.041 -5.558 1.00 26.27 C +ATOM 276 C6 DG B 14 12.761 17.451 -5.710 1.00 40.82 C +ATOM 277 O6 DG B 14 11.723 18.111 -5.630 1.00 44.39 O +ATOM 278 N1 DG B 14 13.952 18.079 -5.973 1.00 19.52 N +ATOM 279 C2 DG B 14 15.171 17.485 -6.107 1.00 18.48 C +ATOM 280 N2 DG B 14 16.244 18.292 -6.325 1.00 36.58 N +ATOM 281 N3 DG B 14 15.329 16.161 -5.986 1.00 46.96 N +ATOM 282 C4 DG B 14 14.179 15.499 -5.721 1.00 35.70 C +ATOM 283 P DC B 15 17.478 10.380 -3.569 1.00 46.26 P +ATOM 284 OP1 DC B 15 18.665 9.516 -3.729 1.00 46.07 O +ATOM 285 OP2 DC B 15 16.427 9.940 -2.633 1.00 40.43 O +ATOM 286 O5' DC B 15 17.957 11.865 -3.208 1.00 40.97 O +ATOM 287 C5' DC B 15 18.963 12.531 -3.996 1.00 28.78 C +ATOM 288 C4' DC B 15 18.936 13.958 -3.536 1.00 32.84 C +ATOM 289 O4' DC B 15 17.592 14.409 -3.622 1.00 37.24 O +ATOM 290 C3' DC B 15 19.253 14.139 -2.066 1.00 43.98 C +ATOM 291 O3' DC B 15 20.659 14.219 -1.858 1.00 40.90 O +ATOM 292 C2' DC B 15 18.520 15.417 -1.728 1.00 36.26 C +ATOM 293 C1' DC B 15 17.545 15.602 -2.872 1.00 20.54 C +ATOM 294 N1 DC B 15 16.145 15.696 -2.428 1.00 23.10 N +ATOM 295 C2 DC B 15 15.507 16.886 -2.558 1.00 32.12 C +ATOM 296 O2 DC B 15 16.162 17.846 -2.957 1.00 30.04 O +ATOM 297 N3 DC B 15 14.209 16.983 -2.264 1.00 32.94 N +ATOM 298 C4 DC B 15 13.536 15.919 -1.825 1.00 16.43 C +ATOM 299 N4 DC B 15 12.205 16.017 -1.553 1.00 34.91 N +ATOM 300 C5 DC B 15 14.164 14.689 -1.652 1.00 22.75 C +ATOM 301 C6 DC B 15 15.509 14.584 -1.979 1.00 26.42 C +ATOM 302 P DG B 16 21.304 14.529 -0.436 1.00 42.39 P +ATOM 303 OP1 DG B 16 22.696 14.087 -0.524 1.00 60.41 O +ATOM 304 OP2 DG B 16 20.488 13.954 0.650 1.00 51.09 O +ATOM 305 O5' DG B 16 21.306 16.117 -0.363 1.00 45.08 O +ATOM 306 C5' DG B 16 22.177 16.876 -1.212 1.00 33.20 C +ATOM 307 C4' DG B 16 21.739 18.292 -1.021 1.00 24.95 C +ATOM 308 O4' DG B 16 20.305 18.225 -1.048 1.00 32.83 O +ATOM 309 C3' DG B 16 22.101 18.959 0.293 1.00 41.12 C +ATOM 310 O3' DG B 16 22.592 20.293 0.097 1.00 53.45 O +ATOM 311 C2' DG B 16 20.820 18.829 1.121 1.00 28.93 C +ATOM 312 C1' DG B 16 19.765 18.985 0.046 1.00 37.44 C +ATOM 313 N9 DG B 16 18.513 18.299 0.468 1.00 17.75 N +ATOM 314 C8 DG B 16 18.363 17.062 1.039 1.00 17.96 C +ATOM 315 N7 DG B 16 17.080 16.744 1.281 1.00 24.14 N +ATOM 316 C5 DG B 16 16.400 17.832 0.868 1.00 9.96 C +ATOM 317 C6 DG B 16 14.996 18.090 0.882 1.00 18.10 C +ATOM 318 O6 DG B 16 14.082 17.378 1.280 1.00 31.13 O +ATOM 319 N1 DG B 16 14.712 19.349 0.418 1.00 17.72 N +ATOM 320 C2 DG B 16 15.606 20.268 -0.027 1.00 16.23 C +ATOM 321 N2 DG B 16 15.134 21.493 -0.382 1.00 33.42 N +ATOM 322 N3 DG B 16 16.912 20.017 -0.072 1.00 26.37 N +ATOM 323 C4 DG B 16 17.236 18.794 0.384 1.00 31.72 C +ATOM 324 P DA B 17 22.904 21.238 1.339 1.00 46.87 P +ATOM 325 OP1 DA B 17 23.994 22.183 1.025 1.00 47.75 O +ATOM 326 OP2 DA B 17 23.104 20.390 2.538 1.00 46.81 O +ATOM 327 O5' DA B 17 21.577 22.107 1.390 1.00 39.51 O +ATOM 328 C5' DA B 17 21.216 22.833 0.200 1.00 30.37 C +ATOM 329 C4' DA B 17 20.101 23.788 0.484 1.00 35.43 C +ATOM 330 O4' DA B 17 18.913 23.054 0.816 1.00 43.05 O +ATOM 331 C3' DA B 17 20.347 24.743 1.633 1.00 44.50 C +ATOM 332 O3' DA B 17 19.732 26.010 1.411 1.00 78.59 O +ATOM 333 C2' DA B 17 19.752 23.945 2.791 1.00 44.42 C +ATOM 334 C1' DA B 17 18.497 23.393 2.145 1.00 42.55 C +ATOM 335 N9 DA B 17 18.079 22.095 2.758 1.00 34.56 N +ATOM 336 C8 DA B 17 18.847 21.020 3.133 1.00 20.07 C +ATOM 337 N7 DA B 17 18.114 19.984 3.584 1.00 27.60 N +ATOM 338 C5 DA B 17 16.842 20.424 3.488 1.00 18.80 C +ATOM 339 C6 DA B 17 15.577 19.817 3.786 1.00 32.58 C +ATOM 340 N6 DA B 17 15.448 18.537 4.242 1.00 29.54 N +ATOM 341 N1 DA B 17 14.482 20.557 3.593 1.00 35.01 N +ATOM 342 C2 DA B 17 14.597 21.801 3.118 1.00 36.47 C +ATOM 343 N3 DA B 17 15.700 22.472 2.783 1.00 38.96 N +ATOM 344 C4 DA B 17 16.791 21.706 3.002 1.00 28.24 C +ATOM 345 P DA B 18 19.803 27.141 2.526 1.00 46.11 P +ATOM 346 OP1 DA B 18 19.796 28.478 1.888 1.00 49.20 O +ATOM 347 OP2 DA B 18 20.953 26.858 3.426 1.00 43.48 O +ATOM 348 O5' DA B 18 18.396 26.939 3.241 1.00 40.83 O +ATOM 349 C5' DA B 18 17.203 27.028 2.452 1.00 40.72 C +ATOM 350 C4' DA B 18 16.035 26.958 3.388 1.00 66.52 C +ATOM 351 O4' DA B 18 15.856 25.612 3.850 1.00 44.25 O +ATOM 352 C3' DA B 18 16.101 27.861 4.615 1.00 63.34 C +ATOM 353 O3' DA B 18 14.890 28.608 4.757 1.00 55.65 O +ATOM 354 C2' DA B 18 16.368 26.844 5.724 1.00 34.49 C +ATOM 355 C1' DA B 18 15.561 25.655 5.243 1.00 29.45 C +ATOM 356 N9 DA B 18 16.104 24.373 5.755 1.00 20.03 N +ATOM 357 C8 DA B 18 17.411 23.967 5.830 1.00 16.51 C +ATOM 358 N7 DA B 18 17.539 22.706 6.276 1.00 20.58 N +ATOM 359 C5 DA B 18 16.266 22.309 6.480 1.00 21.66 C +ATOM 360 C6 DA B 18 15.715 21.073 6.933 1.00 17.93 C +ATOM 361 N6 DA B 18 16.483 19.994 7.243 1.00 20.37 N +ATOM 362 N1 DA B 18 14.389 20.994 7.036 1.00 20.81 N +ATOM 363 C2 DA B 18 13.636 22.041 6.708 1.00 26.77 C +ATOM 364 N3 DA B 18 14.019 23.234 6.265 1.00 26.83 N +ATOM 365 C4 DA B 18 15.367 23.291 6.174 1.00 27.48 C +ATOM 366 P DT B 19 14.604 29.545 6.020 1.00 48.40 P +ATOM 367 OP1 DT B 19 13.792 30.696 5.582 1.00 50.18 O +ATOM 368 OP2 DT B 19 15.852 29.836 6.749 1.00 44.42 O +ATOM 369 O5' DT B 19 13.633 28.628 6.885 1.00 53.86 O +ATOM 370 C5' DT B 19 12.398 28.171 6.303 1.00 55.04 C +ATOM 371 C4' DT B 19 11.809 27.217 7.302 1.00 44.86 C +ATOM 372 O4' DT B 19 12.767 26.184 7.534 1.00 48.52 O +ATOM 373 C3' DT B 19 11.515 27.822 8.669 1.00 41.77 C +ATOM 374 O3' DT B 19 10.103 27.952 8.891 1.00 57.02 O +ATOM 375 C2' DT B 19 12.267 26.906 9.630 1.00 39.28 C +ATOM 376 C1' DT B 19 12.426 25.645 8.799 1.00 27.68 C +ATOM 377 N1 DT B 19 13.609 24.850 9.205 1.00 21.67 N +ATOM 378 C2 DT B 19 13.442 23.575 9.656 1.00 31.71 C +ATOM 379 O2 DT B 19 12.311 23.101 9.802 1.00 36.00 O +ATOM 380 N3 DT B 19 14.551 22.825 9.913 1.00 24.66 N +ATOM 381 C4 DT B 19 15.815 23.321 9.777 1.00 40.64 C +ATOM 382 O4 DT B 19 16.755 22.570 10.029 1.00 31.47 O +ATOM 383 C5 DT B 19 15.972 24.647 9.362 1.00 31.79 C +ATOM 384 C7 DT B 19 17.345 25.239 9.234 1.00 30.05 C +ATOM 385 C6 DT B 19 14.844 25.405 9.048 1.00 14.35 C +ATOM 386 P DT B 20 9.513 28.533 10.260 1.00 48.24 P +ATOM 387 OP1 DT B 20 8.145 29.007 9.998 1.00 41.28 O +ATOM 388 OP2 DT B 20 10.455 29.513 10.841 1.00 53.39 O +ATOM 389 O5' DT B 20 9.395 27.223 11.153 1.00 36.57 O +ATOM 390 C5' DT B 20 8.576 26.148 10.664 1.00 50.41 C +ATOM 391 C4' DT B 20 8.655 25.060 11.678 1.00 32.08 C +ATOM 392 O4' DT B 20 10.003 24.615 11.764 1.00 48.38 O +ATOM 393 C3' DT B 20 8.272 25.471 13.087 1.00 29.99 C +ATOM 394 O3' DT B 20 7.199 24.657 13.553 1.00 45.14 O +ATOM 395 C2' DT B 20 9.586 25.307 13.860 1.00 32.42 C +ATOM 396 C1' DT B 20 10.190 24.148 13.089 1.00 39.56 C +ATOM 397 N1 DT B 20 11.660 24.070 13.205 1.00 20.36 N +ATOM 398 C2 DT B 20 12.257 22.880 13.486 1.00 27.55 C +ATOM 399 O2 DT B 20 11.583 21.866 13.691 1.00 38.33 O +ATOM 400 N3 DT B 20 13.620 22.829 13.497 1.00 29.60 N +ATOM 401 C4 DT B 20 14.402 23.914 13.225 1.00 30.11 C +ATOM 402 O4 DT B 20 15.625 23.764 13.252 1.00 32.92 O +ATOM 403 C5 DT B 20 13.774 25.126 12.933 1.00 24.11 C +ATOM 404 C7 DT B 20 14.563 26.358 12.612 1.00 23.96 C +ATOM 405 C6 DT B 20 12.385 25.187 12.926 1.00 19.78 C +ATOM 406 P DC B 21 6.594 24.823 15.016 1.00 54.73 P +ATOM 407 OP1 DC B 21 5.169 24.424 14.987 1.00 53.98 O +ATOM 408 OP2 DC B 21 6.870 26.189 15.511 1.00 65.53 O +ATOM 409 O5' DC B 21 7.409 23.731 15.839 1.00 50.67 O +ATOM 410 C5' DC B 21 7.331 22.352 15.433 1.00 60.86 C +ATOM 411 C4' DC B 21 8.100 21.598 16.461 1.00 40.86 C +ATOM 412 O4' DC B 21 9.478 21.902 16.263 1.00 36.88 O +ATOM 413 C3' DC B 21 7.766 22.045 17.879 1.00 53.80 C +ATOM 414 O3' DC B 21 7.036 21.041 18.611 1.00 79.04 O +ATOM 415 C2' DC B 21 9.123 22.414 18.469 1.00 48.43 C +ATOM 416 C1' DC B 21 10.107 21.743 17.523 1.00 36.51 C +ATOM 417 N1 DC B 21 11.328 22.556 17.331 1.00 24.72 N +ATOM 418 C2 DC B 21 12.534 21.939 17.329 1.00 30.96 C +ATOM 419 O2 DC B 21 12.560 20.731 17.579 1.00 34.53 O +ATOM 420 N3 DC B 21 13.639 22.639 17.035 1.00 31.69 N +ATOM 421 C4 DC B 21 13.560 23.938 16.739 1.00 21.53 C +ATOM 422 N4 DC B 21 14.685 24.628 16.404 1.00 23.72 N +ATOM 423 C5 DC B 21 12.338 24.609 16.736 1.00 30.74 C +ATOM 424 C6 DC B 21 11.193 23.878 17.035 1.00 27.58 C +ATOM 425 P DG B 22 6.509 21.324 20.099 1.00 56.50 P +ATOM 426 OP1 DG B 22 5.387 20.397 20.396 1.00 50.81 O +ATOM 427 OP2 DG B 22 6.235 22.774 20.306 1.00 53.84 O +ATOM 428 O5' DG B 22 7.767 20.924 20.993 1.00 66.30 O +ATOM 429 C5' DG B 22 8.216 19.559 21.073 1.00 73.42 C +ATOM 430 C4' DG B 22 9.422 19.557 21.977 1.00 42.96 C +ATOM 431 O4' DG B 22 10.493 20.260 21.319 1.00 52.87 O +ATOM 432 C3' DG B 22 9.267 20.267 23.325 1.00 38.51 C +ATOM 433 O3' DG B 22 10.088 19.657 24.293 1.00 60.28 O +ATOM 434 C2' DG B 22 9.751 21.670 22.990 1.00 22.00 C +ATOM 435 C1' DG B 22 10.988 21.226 22.256 1.00 24.85 C +ATOM 436 N9 DG B 22 11.599 22.357 21.543 1.00 25.91 N +ATOM 437 C8 DG B 22 11.037 23.545 21.159 1.00 23.91 C +ATOM 438 N7 DG B 22 11.921 24.362 20.566 1.00 39.18 N +ATOM 439 C5 DG B 22 13.072 23.653 20.580 1.00 25.66 C +ATOM 440 C6 DG B 22 14.370 24.003 20.102 1.00 28.34 C +ATOM 441 O6 DG B 22 14.747 25.057 19.585 1.00 31.85 O +ATOM 442 N1 DG B 22 15.268 22.983 20.308 1.00 25.22 N +ATOM 443 C2 DG B 22 15.023 21.776 20.891 1.00 11.07 C +ATOM 444 N2 DG B 22 16.066 20.914 21.038 1.00 25.92 N +ATOM 445 N3 DG B 22 13.815 21.452 21.350 1.00 19.05 N +ATOM 446 C4 DG B 22 12.902 22.429 21.151 1.00 23.69 C +ATOM 447 P DC B 23 9.477 18.627 25.340 1.00 55.93 P +ATOM 448 OP1 DC B 23 8.767 17.534 24.627 1.00 45.14 O +ATOM 449 OP2 DC B 23 8.670 19.409 26.312 1.00 41.61 O +ATOM 450 O5' DC B 23 10.807 18.067 26.034 1.00 59.70 O +ATOM 451 C5' DC B 23 11.688 17.170 25.310 1.00 63.13 C +ATOM 452 C4' DC B 23 13.115 17.573 25.593 1.00 27.86 C +ATOM 453 O4' DC B 23 13.284 18.804 24.893 1.00 50.51 O +ATOM 454 C3' DC B 23 13.441 17.879 27.059 1.00 46.45 C +ATOM 455 O3' DC B 23 14.341 16.938 27.677 1.00 57.21 O +ATOM 456 C2' DC B 23 13.928 19.322 27.025 1.00 68.01 C +ATOM 457 C1' DC B 23 14.312 19.508 25.568 1.00 32.05 C +ATOM 458 N1 DC B 23 14.144 20.932 25.170 1.00 23.28 N +ATOM 459 C2 DC B 23 15.199 21.595 24.630 1.00 20.62 C +ATOM 460 O2 DC B 23 16.257 20.984 24.504 1.00 29.62 O +ATOM 461 N3 DC B 23 15.067 22.877 24.257 1.00 39.00 N +ATOM 462 C4 DC B 23 13.898 23.510 24.404 1.00 30.44 C +ATOM 463 N4 DC B 23 13.771 24.813 24.018 1.00 34.66 N +ATOM 464 C5 DC B 23 12.795 22.866 24.967 1.00 27.74 C +ATOM 465 C6 DC B 23 12.935 21.540 25.359 1.00 24.58 C +ATOM 466 P DG B 24 14.658 17.064 29.247 1.00 53.70 P +ATOM 467 OP1 DG B 24 14.863 15.717 29.825 1.00 61.79 O +ATOM 468 OP2 DG B 24 13.633 17.912 29.920 1.00 36.06 O +ATOM 469 O5' DG B 24 16.033 17.880 29.284 1.00 34.06 O +ATOM 470 C5' DG B 24 17.243 17.320 28.742 1.00 46.57 C +ATOM 471 C4' DG B 24 18.208 18.464 28.758 1.00 50.89 C +ATOM 472 O4' DG B 24 17.716 19.428 27.829 1.00 32.02 O +ATOM 473 C3' DG B 24 18.230 19.236 30.058 1.00 30.38 C +ATOM 474 O3' DG B 24 18.978 18.583 31.084 1.00 61.06 O +ATOM 475 C2' DG B 24 18.885 20.519 29.578 1.00 53.33 C +ATOM 476 C1' DG B 24 18.276 20.693 28.188 1.00 35.03 C +ATOM 477 N9 DG B 24 17.164 21.659 28.139 1.00 30.25 N +ATOM 478 C8 DG B 24 15.874 21.536 28.580 1.00 30.86 C +ATOM 479 N7 DG B 24 15.129 22.614 28.308 1.00 44.08 N +ATOM 480 C5 DG B 24 15.990 23.436 27.673 1.00 16.87 C +ATOM 481 C6 DG B 24 15.765 24.729 27.117 1.00 19.36 C +ATOM 482 O6 DG B 24 14.719 25.373 27.067 1.00 33.30 O +ATOM 483 N1 DG B 24 16.926 25.257 26.604 1.00 15.78 N +ATOM 484 C2 DG B 24 18.157 24.666 26.579 1.00 11.92 C +ATOM 485 N2 DG B 24 19.208 25.386 26.096 1.00 29.76 N +ATOM 486 N3 DG B 24 18.350 23.438 27.053 1.00 21.95 N +ATOM 487 C4 DG B 24 17.231 22.893 27.570 1.00 13.89 C +TER 488 DG B 24 +HETATM 489 O HOH 25 19.736 30.706 18.656 1.00 51.86 O +HETATM 490 O HOH 26 14.354 27.683 16.369 1.00 40.92 O +HETATM 491 O HOH 27 9.864 22.509 9.123 1.00 39.67 O +HETATM 492 O HOH 28 19.526 19.144 7.481 1.00 51.15 O +HETATM 493 O HOH 29 25.754 12.744 -1.835 1.00 51.80 O +HETATM 494 O HOH 30 7.478 20.604 -9.000 1.00 44.82 O +HETATM 495 O HOH 31 10.879 26.039 -8.906 1.00 47.07 O +HETATM 496 O HOH 32 18.320 24.816 14.948 1.00 47.72 O +HETATM 497 O HOH 33 9.012 24.586 7.009 1.00 43.42 O +HETATM 498 O HOH 34 10.152 19.917 13.381 1.00 48.04 O +HETATM 499 O HOH 35 7.764 21.397 11.075 1.00 41.41 O +HETATM 500 O HOH 36 9.821 13.442 8.572 1.00 45.76 O +HETATM 501 O HOH 37 13.239 14.428 2.049 1.00 55.54 O +HETATM 502 O HOH 38 8.915 15.602 -3.388 1.00 50.97 O +HETATM 503 O HOH 39 17.505 26.340 -10.581 1.00 51.90 O +HETATM 504 O HOH 40 28.496 23.515 18.349 1.00 45.37 O +HETATM 505 O HOH 41 11.346 24.175 4.920 1.00 45.03 O +HETATM 506 O HOH 42 12.601 23.000 29.167 1.00 51.36 O +HETATM 507 O HOH 43 10.440 25.542 24.443 1.00 56.79 O +HETATM 508 O HOH 44 16.979 28.689 16.284 1.00 50.41 O +HETATM 509 O HOH 45 4.794 22.966 13.368 1.00 45.95 O +HETATM 510 O HOH 46 4.208 25.591 10.828 1.00 51.06 O +HETATM 511 O HOH 47 6.362 24.374 9.188 1.00 51.85 O +HETATM 512 O HOH 48 7.688 28.411 7.883 1.00 49.33 O +HETATM 513 O HOH 49 18.379 17.074 4.809 1.00 50.72 O +HETATM 514 O HOH 50 9.098 16.119 1.277 1.00 51.80 O +HETATM 515 O HOH 51 26.464 23.826 1.396 1.00 53.21 O +HETATM 516 O HOH 52 11.014 11.318 -2.909 1.00 51.36 O +HETATM 517 O HOH 53 9.476 27.782 26.498 1.00 60.04 O +HETATM 518 O HOH 54 16.488 29.195 19.861 1.00 54.92 O +HETATM 519 O HOH 55 22.078 25.894 15.396 1.00 62.20 O +HETATM 520 O HOH 56 5.522 27.411 9.017 1.00 62.36 O +HETATM 521 O HOH 57 18.456 28.409 8.821 1.00 59.63 O +HETATM 522 O HOH 58 7.133 14.448 4.647 1.00 57.15 O +HETATM 523 O HOH 59 22.610 15.544 3.846 1.00 57.52 O +HETATM 524 O HOH 60 24.407 13.162 2.229 1.00 52.30 O +HETATM 525 O HOH 61 7.988 11.556 -2.976 1.00 59.14 O +HETATM 526 O HOH 62 14.095 28.151 21.614 1.00 53.85 O +HETATM 527 O HOH 63 14.213 27.722 18.905 1.00 57.29 O +HETATM 528 O HOH 64 27.164 31.710 20.331 1.00 56.84 O +HETATM 529 O HOH 65 15.295 11.873 12.209 1.00 57.34 O +HETATM 530 O HOH 66 18.180 16.604 9.966 1.00 61.52 O +HETATM 531 O HOH 67 6.216 17.035 1.672 1.00 62.91 O +HETATM 532 O HOH 68 19.101 11.433 1.080 1.00 59.79 O +HETATM 533 O HOH 69 12.607 10.967 0.261 1.00 60.87 O +HETATM 534 O HOH 70 7.055 25.519 -2.053 1.00 55.96 O +HETATM 535 O HOH 71 15.062 26.024 -0.766 1.00 56.35 O +HETATM 536 O HOH 72 16.380 6.413 -4.784 1.00 59.07 O +HETATM 537 O HOH 73 14.059 5.751 -6.198 1.00 56.68 O +HETATM 538 O HOH 74 12.454 11.354 9.415 1.00 68.40 O +HETATM 539 O HOH 75 9.613 17.039 29.793 1.00 63.48 O +HETATM 540 O HOH 76 11.492 29.103 20.090 1.00 67.46 O +HETATM 541 O HOH 77 14.220 29.189 20.392 1.00 48.22 O +HETATM 542 O HOH 78 6.138 19.149 13.844 1.00 62.26 O +HETATM 543 O HOH 79 17.315 9.638 13.392 1.00 65.70 O +HETATM 544 O HOH 80 18.951 25.757 12.989 1.00 66.47 O +HETATM 545 O HOH 81 20.460 18.861 12.664 1.00 63.00 O +HETATM 546 O HOH 82 3.529 19.338 12.599 1.00 65.32 O +HETATM 547 O HOH 83 25.276 15.890 -1.301 1.00 64.53 O +HETATM 548 O HOH 84 16.223 12.351 9.406 1.00 63.59 O +HETATM 549 O HOH 85 12.989 29.901 -9.282 1.00 64.97 O +HETATM 550 O HOH 86 17.510 30.569 18.702 1.00 61.79 O +HETATM 551 O HOH 87 25.377 12.891 19.011 1.00 73.80 O +HETATM 552 O HOH 88 13.610 15.742 18.593 1.00 69.48 O +HETATM 553 O HOH 89 18.012 32.598 15.262 1.00 67.52 O +HETATM 554 O HOH 90 2.622 23.030 10.332 1.00 68.01 O +HETATM 555 O HOH 91 19.701 22.518 9.511 1.00 70.25 O +HETATM 556 O HOH 92 8.723 13.216 6.359 1.00 70.66 O +HETATM 557 O HOH 93 19.727 29.488 6.155 1.00 69.43 O +HETATM 558 O HOH 94 17.241 11.563 4.511 1.00 72.18 O +HETATM 559 O HOH 95 26.545 19.404 -1.091 1.00 70.14 O +HETATM 560 O HOH 96 9.697 18.315 14.885 1.00 69.10 O +HETATM 561 O HOH 97 18.779 13.814 11.704 1.00 71.14 O +HETATM 562 O HOH 98 14.292 25.159 2.287 1.00 68.44 O +HETATM 563 O HOH 99 12.227 25.192 -10.299 1.00 70.46 O +HETATM 564 O HOH 100 12.292 30.291 27.102 1.00 73.04 O +HETATM 565 O HOH 101 9.396 27.092 16.993 1.00 72.98 O +HETATM 566 O HOH 102 20.170 23.000 12.999 1.00 73.63 O +HETATM 567 O HOH 103 19.987 21.691 6.802 1.00 72.66 O +HETATM 568 O HOH 104 18.692 31.584 4.596 1.00 72.98 O +MASTER 246 0 0 0 0 0 0 6 566 2 0 2 +END diff --git a/BuiltInMolecules/Heme.sdf b/BuiltInMolecules/Heme.sdf new file mode 100644 index 0000000..9b5a540 --- /dev/null +++ b/BuiltInMolecules/Heme.sdf @@ -0,0 +1,514 @@ +1HHO_HEM_A_143 + RCSB PDB01311304003D +Coordinates from PDB:1HHO:A:143 Model:1 without hydrogens + 43 50 0 0 0 0 999 V2000 + 9.4750 10.0470 -17.9660 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.7180 11.8920 -13.6910 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.9750 7.9280 -11.6610 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.4760 6.1130 -15.8830 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.0470 10.8750 -16.9570 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.7720 12.1070 -17.2100 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.1040 12.5930 -15.9880 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.5990 11.6860 -15.0360 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.9510 13.7610 -15.5930 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.6740 12.8920 -18.5060 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.3670 14.3760 -18.4800 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.5610 14.8640 -19.9130 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.4360 15.6950 -20.1910 O 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8480 14.3380 -20.7590 O 0 0 0 0 0 0 0 0 0 0 0 0 + 10.3490 10.9460 -12.7660 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.6740 11.0230 -11.3540 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.1810 9.8920 -10.7540 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.5720 9.1520 -11.8490 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.2320 12.2750 -10.7440 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8660 9.5720 -9.2980 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.3690 10.2820 -8.2020 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4330 7.1100 -12.6550 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.8070 5.8260 -12.3760 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3820 5.3040 -13.5570 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.7440 6.2650 -14.5540 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3990 5.3800 -11.0080 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.7540 3.9660 -13.8300 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.4360 2.7900 -13.6160 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.8630 7.0830 -16.8210 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.6500 6.9680 -18.2260 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.1710 8.0500 -18.7970 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.7280 8.8680 -17.7550 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.4070 5.6860 -18.9590 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.2910 8.2260 -20.2840 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.5420 7.5960 -20.8820 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.4750 7.8250 -22.3750 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4470 7.4850 -22.9550 O 0 0 0 0 0 0 0 0 0 0 0 0 + 10.4380 8.3610 -22.9290 O 0 0 0 0 0 0 0 0 0 0 0 0 + 10.0060 10.6120 -15.6250 N 0 0 0 0 0 0 0 0 0 0 0 0 + 9.6950 9.7910 -13.0820 N 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4120 7.3810 -14.0060 N 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4880 8.2580 -16.5320 N 0 0 0 0 0 0 0 0 0 0 0 0 + 9.0210 9.0340 -14.8480 Fe 0 0 0 0 0 0 0 0 0 0 0 0 + 1 5 1 0 0 0 0 + 1 32 2 0 0 0 0 + 2 8 1 0 0 0 0 + 2 15 2 0 0 0 0 + 3 18 1 0 0 0 0 + 3 22 2 0 0 0 0 + 4 25 2 0 0 0 0 + 4 29 1 0 0 0 0 + 5 6 2 0 0 0 0 + 5 39 1 0 0 0 0 + 6 7 1 0 0 0 0 + 6 10 1 0 0 0 0 + 7 8 2 0 0 0 0 + 7 9 1 0 0 0 0 + 8 39 1 0 0 0 0 + 10 11 1 0 0 0 0 + 11 12 1 0 0 0 0 + 12 13 2 0 0 0 0 + 12 14 1 0 0 0 0 + 15 16 1 0 0 0 0 + 15 40 1 0 0 0 0 + 16 17 2 0 0 0 0 + 16 19 1 0 0 0 0 + 17 18 1 0 0 0 0 + 17 20 1 0 0 0 0 + 18 40 2 0 0 0 0 + 20 21 2 0 0 0 0 + 22 23 1 0 0 0 0 + 22 41 1 0 0 0 0 + 23 24 2 0 0 0 0 + 23 26 1 0 0 0 0 + 24 25 1 0 0 0 0 + 24 27 1 0 0 0 0 + 25 41 1 0 0 0 0 + 27 28 2 0 0 0 0 + 29 30 1 0 0 0 0 + 29 42 2 0 0 0 0 + 30 31 2 0 0 0 0 + 30 33 1 0 0 0 0 + 31 32 1 0 0 0 0 + 31 34 1 0 0 0 0 + 32 42 1 0 0 0 0 + 34 35 1 0 0 0 0 + 35 36 1 0 0 0 0 + 36 37 2 0 0 0 0 + 36 38 1 0 0 0 0 + 43 39 1 0 0 0 0 + 43 40 1 0 0 0 0 + 43 41 1 0 0 0 0 + 43 42 1 0 0 0 0 +A 1 +CHA +A 2 +CHB +A 3 +CHC +A 4 +CHD +A 5 +C1A +A 6 +C2A +A 7 +C3A +A 8 +C4A +A 9 +CMA +A 10 +CAA +A 11 +CBA +A 12 +CGA +A 13 +O1A +A 14 +O2A +A 15 +C1B +A 16 +C2B +A 17 +C3B +A 18 +C4B +A 19 +CMB +A 20 +CAB +A 21 +CBB +A 22 +C1C +A 23 +C2C +A 24 +C3C +A 25 +C4C +A 26 +CMC +A 27 +CAC +A 28 +CBC +A 29 +C1D +A 30 +C2D +A 31 +C3D +A 32 +C4D +A 33 +CMD +A 34 +CAD +A 35 +CBD +A 36 +CGD +A 37 +O1D +A 38 +O2D +A 39 +NA +A 40 +NB +A 41 +NC +A 42 +ND +A 43 +FE +M END +> +1HHO_HEM_A_143 + +> +HEM + +> +1HHO + +> +A + +> +143 + +> + + +> +1 + +> + + +> +0 + +> +C34 N4 O4 Fe + +> +PROTOPORPHYRIN IX CONTAINING FE + +> +3-[(5Z,10Z,14Z,19Z)-18-(2-carboxyethyl)-8,13-bis(ethenyl)-3,7,12,17-tetramethyl-21,23-dihydroporphyrin-2-yl]propanoic acid + +> +HEME + +> +NON-POLYMER + +> +C34 H32 Fe N4 O4 + +> +616.487 + +> +2009-08-11 + +> + + +> + + +> + + +> +Y + +> +InChI=1S/C34H34N4O4/c1-7-21-17(3)25-13-26-19(5)23(9-11-33(39)40)31(37-26)16-32-24(10-12-34(41)42)20(6)28(38-32)15-30-22(8-2)18(4)27(36-30)14-29(21)35-25/h7-8,13-16,36-37H,1-2,9-12H2,3-6H3,(H,39,40)(H,41,42)/b25-13-,26-13-,27-14-,28-15-,29-14-,30-15-,31-16-,32-16- + +> +FEDYMSUPMFCVOD-UJJXFSCMSA-N + +> +Cc1c2/cc/3\nc(/cc\4/c(c(/c(/[nH]4)c/c5n/c(c\c(c1CCC(=O)O)[nH]2)/C(=C5C)CCC(=O)O)C=C)C)C(=C3C)C=C + +$$$$ +1HHO_HEM_B_147 + RCSB PDB01311304003D +Coordinates from PDB:1HHO:B:147 Model:1 without hydrogens + 43 50 0 0 0 0 999 V2000 + 8.9300 -10.2130 17.8400 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.8340 -11.6780 13.6850 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.4700 -7.9840 11.5710 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.1360 -6.2440 15.8280 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.6550 -10.9010 16.9020 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.5010 -12.0230 17.2460 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.0520 -12.4720 16.0770 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.5490 -11.5900 15.0410 C 0 0 0 0 0 0 0 0 0 0 0 0 + 12.2910 -13.2960 15.9200 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.4980 -12.6300 18.6210 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.9300 -14.0770 18.7840 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.6380 -14.4880 20.2290 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.0490 -15.5990 20.5860 O 0 0 0 0 0 0 0 0 0 0 0 0 + 10.0190 -13.6970 20.9600 O 0 0 0 0 0 0 0 0 0 0 0 0 + 10.2960 -10.7780 12.7650 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.5500 -10.8620 11.3860 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.8790 -9.8180 10.7930 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.2260 -9.1210 11.8350 C 0 0 0 0 0 0 0 0 0 0 0 0 + 11.5420 -11.8440 10.8410 C 0 0 0 0 0 0 0 0 0 0 0 0 + 9.5850 -9.4590 9.3710 C 0 0 0 0 0 0 0 0 0 0 0 0 + 10.2470 -10.0320 8.2970 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.9710 -7.1690 12.5620 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.4730 -5.8460 12.3100 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.0880 -5.3450 13.5000 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3850 -6.3640 14.4790 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.2940 -5.1890 10.9680 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.0690 -4.2680 13.6610 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.4640 -2.9730 13.9060 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.4960 -7.2000 16.7330 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3680 -7.0590 18.1610 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.8190 -8.2050 18.6890 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.2570 -9.0390 17.6170 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.2030 -5.7890 18.9550 C 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0890 -8.4010 20.1440 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.4700 -9.7120 20.5650 C 0 0 0 0 0 0 0 0 0 0 0 0 + 7.3350 -9.7500 22.0690 C 0 0 0 0 0 0 0 0 0 0 0 0 + 6.1830 -9.6060 22.4890 O 0 0 0 0 0 0 0 0 0 0 0 0 + 8.3450 -9.9000 22.7730 O 0 0 0 0 0 0 0 0 0 0 0 0 + 9.7650 -10.5770 15.5700 N 0 0 0 0 0 0 0 0 0 0 0 0 + 9.4560 -9.7300 13.0590 N 0 0 0 0 0 0 0 0 0 0 0 0 + 8.0390 -7.4340 13.9070 N 0 0 0 0 0 0 0 0 0 0 0 0 + 8.1170 -8.3750 16.4280 N 0 0 0 0 0 0 0 0 0 0 0 0 + 8.9690 -9.0340 14.8060 Fe 0 0 0 0 0 0 0 0 0 0 0 0 + 1 5 1 0 0 0 0 + 1 32 2 0 0 0 0 + 2 8 1 0 0 0 0 + 2 15 2 0 0 0 0 + 3 18 1 0 0 0 0 + 3 22 2 0 0 0 0 + 4 25 2 0 0 0 0 + 4 29 1 0 0 0 0 + 5 6 2 0 0 0 0 + 5 39 1 0 0 0 0 + 6 7 1 0 0 0 0 + 6 10 1 0 0 0 0 + 7 8 2 0 0 0 0 + 7 9 1 0 0 0 0 + 8 39 1 0 0 0 0 + 10 11 1 0 0 0 0 + 11 12 1 0 0 0 0 + 12 13 2 0 0 0 0 + 12 14 1 0 0 0 0 + 15 16 1 0 0 0 0 + 15 40 1 0 0 0 0 + 16 17 2 0 0 0 0 + 16 19 1 0 0 0 0 + 17 18 1 0 0 0 0 + 17 20 1 0 0 0 0 + 18 40 2 0 0 0 0 + 20 21 2 0 0 0 0 + 22 23 1 0 0 0 0 + 22 41 1 0 0 0 0 + 23 24 2 0 0 0 0 + 23 26 1 0 0 0 0 + 24 25 1 0 0 0 0 + 24 27 1 0 0 0 0 + 25 41 1 0 0 0 0 + 27 28 2 0 0 0 0 + 29 30 1 0 0 0 0 + 29 42 2 0 0 0 0 + 30 31 2 0 0 0 0 + 30 33 1 0 0 0 0 + 31 32 1 0 0 0 0 + 31 34 1 0 0 0 0 + 32 42 1 0 0 0 0 + 34 35 1 0 0 0 0 + 35 36 1 0 0 0 0 + 36 37 2 0 0 0 0 + 36 38 1 0 0 0 0 + 43 39 1 0 0 0 0 + 43 40 1 0 0 0 0 + 43 41 1 0 0 0 0 + 43 42 1 0 0 0 0 +A 1 +CHA +A 2 +CHB +A 3 +CHC +A 4 +CHD +A 5 +C1A +A 6 +C2A +A 7 +C3A +A 8 +C4A +A 9 +CMA +A 10 +CAA +A 11 +CBA +A 12 +CGA +A 13 +O1A +A 14 +O2A +A 15 +C1B +A 16 +C2B +A 17 +C3B +A 18 +C4B +A 19 +CMB +A 20 +CAB +A 21 +CBB +A 22 +C1C +A 23 +C2C +A 24 +C3C +A 25 +C4C +A 26 +CMC +A 27 +CAC +A 28 +CBC +A 29 +C1D +A 30 +C2D +A 31 +C3D +A 32 +C4D +A 33 +CMD +A 34 +CAD +A 35 +CBD +A 36 +CGD +A 37 +O1D +A 38 +O2D +A 39 +NA +A 40 +NB +A 41 +NC +A 42 +ND +A 43 +FE +M END +> +1HHO_HEM_B_147 + +> +HEM + +> +1HHO + +> +B + +> +147 + +> + + +> +1 + +> + + +> +0 + +> +C34 N4 O4 Fe + +> +PROTOPORPHYRIN IX CONTAINING FE + +> +3-[(5Z,10Z,14Z,19Z)-18-(2-carboxyethyl)-8,13-bis(ethenyl)-3,7,12,17-tetramethyl-21,23-dihydroporphyrin-2-yl]propanoic acid + +> +HEME + +> +NON-POLYMER + +> +C34 H32 Fe N4 O4 + +> +616.487 + +> +2009-08-11 + +> + + +> + + +> + + +> +Y + +> +InChI=1S/C34H34N4O4/c1-7-21-17(3)25-13-26-19(5)23(9-11-33(39)40)31(37-26)16-32-24(10-12-34(41)42)20(6)28(38-32)15-30-22(8-2)18(4)27(36-30)14-29(21)35-25/h7-8,13-16,36-37H,1-2,9-12H2,3-6H3,(H,39,40)(H,41,42)/b25-13-,26-13-,27-14-,28-15-,29-14-,30-15-,31-16-,32-16- + +> +FEDYMSUPMFCVOD-UJJXFSCMSA-N + +> +Cc1c2/cc/3\nc(/cc\4/c(c(/c(/[nH]4)c/c5n/c(c\c(c1CCC(=O)O)[nH]2)/C(=C5C)CCC(=O)O)C=C)C)C(=C3C)C=C + +$$$$ diff --git a/BuiltInMolecules/Insulin.pdb b/BuiltInMolecules/Insulin.pdb new file mode 100644 index 0000000..b0f698b --- /dev/null +++ b/BuiltInMolecules/Insulin.pdb @@ -0,0 +1,1326 @@ +HEADER HORMONE 19-NOV-93 1TRZ +TITLE CRYSTALLOGRAPHIC EVIDENCE FOR DUAL COORDINATION AROUND ZINC +TITLE 2 IN THE T3R3 HUMAN INSULIN HEXAMER +COMPND MOL_ID: 1; +COMPND 2 MOLECULE: INSULIN; +COMPND 3 CHAIN: A, C; +COMPND 4 ENGINEERED: YES; +COMPND 5 MOL_ID: 2; +COMPND 6 MOLECULE: INSULIN; +COMPND 7 CHAIN: B, D; +COMPND 8 ENGINEERED: YES +SOURCE MOL_ID: 1; +SOURCE 2 MOL_ID: 2 +KEYWDS HORMONE +EXPDTA X-RAY DIFFRACTION +AUTHOR E.CISZAK,G.D.SMITH +REVDAT 2 24-SEP-99 1TRZ 1 JRNL +REVDAT 1 31-JAN-94 1TRZ 0 +JRNL AUTH E.CISZAK,G.D.SMITH +JRNL TITL CRYSTALLOGRAPHIC EVIDENCE FOR DUAL COORDINATION +JRNL TITL 2 AROUND ZINC IN THE T3R3 HUMAN INSULIN HEXAMER. +JRNL REF BIOCHEMISTRY V. 33 1512 1994 +JRNL REFN ASTM BICHAW US ISSN 0006-2960 +REMARK 1 +REMARK 1 REFERENCE 1 +REMARK 1 AUTH G.D.SMITH,D.C.SWENSON,E.J.DODSON,G.G.DODSON, +REMARK 1 AUTH 2 C.D.REYNOLDS +REMARK 1 TITL STRUCTURAL STABILITY IN THE 4-ZINC HUMAN INSULIN +REMARK 1 TITL 2 HEXAMER +REMARK 1 REF PROC.NATL.ACAD.SCI.USA V. 81 7093 1984 +REMARK 1 REFN ASTM PNASA6 US ISSN 0027-8424 +REMARK 1 REFERENCE 2 +REMARK 1 AUTH G.BENTLEY,E.J.DODSON,G.G.DODSON,D.HODGKIN,D.MERCOLA +REMARK 1 TITL STRUCTURE OF INSULIN IN 4-ZINC INSULIN +REMARK 1 REF NATURE V. 261 166 1976 +REMARK 1 REFN ASTM NATUAS UK ISSN 0028-0836 +REMARK 2 +REMARK 2 RESOLUTION. 1.60 ANGSTROMS. +REMARK 3 +REMARK 3 REFINEMENT. +REMARK 3 PROGRAM : PROFFT +REMARK 3 AUTHORS : KONNERT,HENDRICKSON,FINZEL +REMARK 3 +REMARK 3 DATA USED IN REFINEMENT. +REMARK 3 RESOLUTION RANGE HIGH (ANGSTROMS) : 1.60 +REMARK 3 RESOLUTION RANGE LOW (ANGSTROMS) : NULL +REMARK 3 DATA CUTOFF (SIGMA(F)) : NULL +REMARK 3 COMPLETENESS FOR RANGE (%) : NULL +REMARK 3 NUMBER OF REFLECTIONS : NULL +REMARK 3 +REMARK 3 FIT TO DATA USED IN REFINEMENT. +REMARK 3 CROSS-VALIDATION METHOD : NULL +REMARK 3 FREE R VALUE TEST SET SELECTION : NULL +REMARK 3 R VALUE (WORKING + TEST SET) : 0.172 +REMARK 3 R VALUE (WORKING SET) : NULL +REMARK 3 FREE R VALUE : NULL +REMARK 3 FREE R VALUE TEST SET SIZE (%) : NULL +REMARK 3 FREE R VALUE TEST SET COUNT : NULL +REMARK 3 +REMARK 3 FIT/AGREEMENT OF MODEL WITH ALL DATA. +REMARK 3 R VALUE (WORKING + TEST SET, NO CUTOFF) : NULL +REMARK 3 R VALUE (WORKING SET, NO CUTOFF) : NULL +REMARK 3 FREE R VALUE (NO CUTOFF) : NULL +REMARK 3 FREE R VALUE TEST SET SIZE (%, NO CUTOFF) : NULL +REMARK 3 FREE R VALUE TEST SET COUNT (NO CUTOFF) : NULL +REMARK 3 TOTAL NUMBER OF REFLECTIONS (NO CUTOFF) : NULL +REMARK 3 +REMARK 3 NUMBER OF NON-HYDROGEN ATOMS USED IN REFINEMENT. +REMARK 3 PROTEIN ATOMS : 798 +REMARK 3 NUCLEIC ACID ATOMS : 0 +REMARK 3 HETEROGEN ATOMS : 5 +REMARK 3 SOLVENT ATOMS : 123 +REMARK 3 +REMARK 3 B VALUES. +REMARK 3 FROM WILSON PLOT (A**2) : NULL +REMARK 3 MEAN B VALUE (OVERALL, A**2) : NULL +REMARK 3 OVERALL ANISOTROPIC B VALUE. +REMARK 3 B11 (A**2) : NULL +REMARK 3 B22 (A**2) : NULL +REMARK 3 B33 (A**2) : NULL +REMARK 3 B12 (A**2) : NULL +REMARK 3 B13 (A**2) : NULL +REMARK 3 B23 (A**2) : NULL +REMARK 3 +REMARK 3 ESTIMATED COORDINATE ERROR. +REMARK 3 ESD FROM LUZZATI PLOT (A) : NULL +REMARK 3 ESD FROM SIGMAA (A) : NULL +REMARK 3 LOW RESOLUTION CUTOFF (A) : NULL +REMARK 3 +REMARK 3 RMS DEVIATIONS FROM IDEAL VALUES. +REMARK 3 DISTANCE RESTRAINTS. RMS SIGMA +REMARK 3 BOND LENGTH (A) : 0.018 ; NULL +REMARK 3 ANGLE DISTANCE (A) : 3.000 ; NULL +REMARK 3 INTRAPLANAR 1-4 DISTANCE (A) : NULL ; NULL +REMARK 3 H-BOND OR METAL COORDINATION (A) : NULL ; NULL +REMARK 3 +REMARK 3 PLANE RESTRAINT (A) : NULL ; NULL +REMARK 3 CHIRAL-CENTER RESTRAINT (A**3) : NULL ; NULL +REMARK 3 +REMARK 3 NON-BONDED CONTACT RESTRAINTS. +REMARK 3 SINGLE TORSION (A) : NULL ; NULL +REMARK 3 MULTIPLE TORSION (A) : NULL ; NULL +REMARK 3 H-BOND (X...Y) (A) : NULL ; NULL +REMARK 3 H-BOND (X-H...Y) (A) : NULL ; NULL +REMARK 3 +REMARK 3 CONFORMATIONAL TORSION ANGLE RESTRAINTS. +REMARK 3 SPECIFIED (DEGREES) : NULL ; NULL +REMARK 3 PLANAR (DEGREES) : NULL ; NULL +REMARK 3 STAGGERED (DEGREES) : NULL ; NULL +REMARK 3 TRANSVERSE (DEGREES) : NULL ; NULL +REMARK 3 +REMARK 3 ISOTROPIC THERMAL FACTOR RESTRAINTS. RMS SIGMA +REMARK 3 MAIN-CHAIN BOND (A**2) : NULL ; NULL +REMARK 3 MAIN-CHAIN ANGLE (A**2) : NULL ; NULL +REMARK 3 SIDE-CHAIN BOND (A**2) : NULL ; NULL +REMARK 3 SIDE-CHAIN ANGLE (A**2) : NULL ; NULL +REMARK 3 +REMARK 3 OTHER REFINEMENT REMARKS: NULL +REMARK 4 +REMARK 4 1TRZ COMPLIES WITH FORMAT V. 3.0, 1-DEC-2006 +REMARK 4 +REMARK 4 THIS IS THE REMEDIATED VERSION OF THIS PDB ENTRY. +REMARK 4 REMEDIATED DATA FILE REVISION 3.100 (2007-03-17) +REMARK 200 +REMARK 200 EXPERIMENTAL DETAILS +REMARK 200 EXPERIMENT TYPE : X-RAY DIFFRACTION +REMARK 200 DATE OF DATA COLLECTION : NULL +REMARK 200 TEMPERATURE (KELVIN) : NULL +REMARK 200 PH : NULL +REMARK 200 NUMBER OF CRYSTALS USED : NULL +REMARK 200 +REMARK 200 SYNCHROTRON (Y/N) : NULL +REMARK 200 RADIATION SOURCE : NULL +REMARK 200 BEAMLINE : NULL +REMARK 200 X-RAY GENERATOR MODEL : NULL +REMARK 200 MONOCHROMATIC OR LAUE (M/L) : NULL +REMARK 200 WAVELENGTH OR RANGE (A) : NULL +REMARK 200 MONOCHROMATOR : NULL +REMARK 200 OPTICS : NULL +REMARK 200 +REMARK 200 DETECTOR TYPE : NULL +REMARK 200 DETECTOR MANUFACTURER : NULL +REMARK 200 INTENSITY-INTEGRATION SOFTWARE : NULL +REMARK 200 DATA SCALING SOFTWARE : NULL +REMARK 200 +REMARK 200 NUMBER OF UNIQUE REFLECTIONS : NULL +REMARK 200 RESOLUTION RANGE HIGH (A) : NULL +REMARK 200 RESOLUTION RANGE LOW (A) : NULL +REMARK 200 REJECTION CRITERIA (SIGMA(I)) : NULL +REMARK 200 +REMARK 200 OVERALL. +REMARK 200 COMPLETENESS FOR RANGE (%) : NULL +REMARK 200 DATA REDUNDANCY : NULL +REMARK 200 R MERGE (I) : NULL +REMARK 200 R SYM (I) : NULL +REMARK 200 FOR THE DATA SET : NULL +REMARK 200 +REMARK 200 IN THE HIGHEST RESOLUTION SHELL. +REMARK 200 HIGHEST RESOLUTION SHELL, RANGE HIGH (A) : NULL +REMARK 200 HIGHEST RESOLUTION SHELL, RANGE LOW (A) : NULL +REMARK 200 COMPLETENESS FOR SHELL (%) : NULL +REMARK 200 DATA REDUNDANCY IN SHELL : NULL +REMARK 200 R MERGE FOR SHELL (I) : NULL +REMARK 200 R SYM FOR SHELL (I) : NULL +REMARK 200 FOR SHELL : NULL +REMARK 200 +REMARK 200 DIFFRACTION PROTOCOL: NULL +REMARK 200 METHOD USED TO DETERMINE THE STRUCTURE: NULL +REMARK 200 SOFTWARE USED: NULL +REMARK 200 STARTING MODEL: NULL +REMARK 200 +REMARK 200 REMARK: NULL +REMARK 280 +REMARK 280 CRYSTAL +REMARK 280 SOLVENT CONTENT, VS (%): 39.45 +REMARK 280 MATTHEWS COEFFICIENT, VM (ANGSTROMS**3/DA): 2.03 +REMARK 280 +REMARK 280 CRYSTALLIZATION CONDITIONS: NULL +REMARK 290 +REMARK 290 CRYSTALLOGRAPHIC SYMMETRY +REMARK 290 SYMMETRY OPERATORS FOR SPACE GROUP: H 3 +REMARK 290 +REMARK 290 SYMOP SYMMETRY +REMARK 290 NNNMMM OPERATOR +REMARK 290 1555 X,Y,Z +REMARK 290 2555 -Y,X-Y,Z +REMARK 290 3555 -X+Y,-X,Z +REMARK 290 4555 2/3+X,1/3+Y,1/3+Z +REMARK 290 5555 2/3-Y,1/3+X-Y,1/3+Z +REMARK 290 6555 2/3-X+Y,1/3-X,1/3+Z +REMARK 290 7555 1/3+X,2/3+Y,2/3+Z +REMARK 290 8555 1/3-Y,2/3+X-Y,2/3+Z +REMARK 290 9555 1/3-X+Y,2/3-X,2/3+Z +REMARK 290 +REMARK 290 WHERE NNN -> OPERATOR NUMBER +REMARK 290 MMM -> TRANSLATION VECTOR +REMARK 290 +REMARK 290 CRYSTALLOGRAPHIC SYMMETRY TRANSFORMATIONS +REMARK 290 THE FOLLOWING TRANSFORMATIONS OPERATE ON THE ATOM/HETATM +REMARK 290 RECORDS IN THIS ENTRY TO PRODUCE CRYSTALLOGRAPHICALLY +REMARK 290 RELATED MOLECULES. +REMARK 290 SMTRY1 1 1.000000 0.000000 0.000000 0.00000 +REMARK 290 SMTRY2 1 0.000000 1.000000 0.000000 0.00000 +REMARK 290 SMTRY3 1 0.000000 0.000000 1.000000 0.00000 +REMARK 290 SMTRY1 2 -0.500000 -0.866025 0.000000 0.00000 +REMARK 290 SMTRY2 2 0.866025 -0.500000 0.000000 0.00000 +REMARK 290 SMTRY3 2 0.000000 0.000000 1.000000 0.00000 +REMARK 290 SMTRY1 3 -0.500000 0.866025 0.000000 0.00000 +REMARK 290 SMTRY2 3 -0.866025 -0.500000 0.000000 0.00000 +REMARK 290 SMTRY3 3 0.000000 0.000000 1.000000 0.00000 +REMARK 290 SMTRY1 4 1.000000 0.000000 0.000000 40.31900 +REMARK 290 SMTRY2 4 0.000000 1.000000 0.000000 23.27819 +REMARK 290 SMTRY3 4 0.000000 0.000000 1.000000 12.59400 +REMARK 290 SMTRY1 5 -0.500000 -0.866025 0.000000 40.31900 +REMARK 290 SMTRY2 5 0.866025 -0.500000 0.000000 23.27819 +REMARK 290 SMTRY3 5 0.000000 0.000000 1.000000 12.59400 +REMARK 290 SMTRY1 6 -0.500000 0.866025 0.000000 40.31900 +REMARK 290 SMTRY2 6 -0.866025 -0.500000 0.000000 23.27819 +REMARK 290 SMTRY3 6 0.000000 0.000000 1.000000 12.59400 +REMARK 290 SMTRY1 7 1.000000 0.000000 0.000000 0.00000 +REMARK 290 SMTRY2 7 0.000000 1.000000 0.000000 46.55637 +REMARK 290 SMTRY3 7 0.000000 0.000000 1.000000 25.18800 +REMARK 290 SMTRY1 8 -0.500000 -0.866025 0.000000 0.00000 +REMARK 290 SMTRY2 8 0.866025 -0.500000 0.000000 46.55637 +REMARK 290 SMTRY3 8 0.000000 0.000000 1.000000 25.18800 +REMARK 290 SMTRY1 9 -0.500000 0.866025 0.000000 0.00000 +REMARK 290 SMTRY2 9 -0.866025 -0.500000 0.000000 46.55637 +REMARK 290 SMTRY3 9 0.000000 0.000000 1.000000 25.18800 +REMARK 290 +REMARK 290 REMARK: NULL +REMARK 300 +REMARK 300 BIOMOLECULE: 1, 2 +REMARK 300 THIS ENTRY CONTAINS THE CRYSTALLOGRAPHIC ASYMMETRIC UNIT +REMARK 300 WHICH CONSISTS OF 4 CHAIN(S). SEE REMARK 350 FOR +REMARK 300 INFORMATION ON GENERATING THE BIOLOGICAL MOLECULE(S). +REMARK 350 +REMARK 350 GENERATING THE BIOMOLECULE +REMARK 350 COORDINATES FOR A COMPLETE MULTIMER REPRESENTING THE KNOWN +REMARK 350 BIOLOGICALLY SIGNIFICANT OLIGOMERIZATION STATE OF THE +REMARK 350 MOLECULE CAN BE GENERATED BY APPLYING BIOMT TRANSFORMATIONS +REMARK 350 GIVEN BELOW. BOTH NON-CRYSTALLOGRAPHIC AND +REMARK 350 CRYSTALLOGRAPHIC OPERATIONS ARE GIVEN. +REMARK 350 +REMARK 350 BIOMOLECULE: 1 +REMARK 350 APPLY THE FOLLOWING TO CHAINS: A, B +REMARK 350 BIOMT1 1 1.000000 0.000000 0.000000 0.00000 +REMARK 350 BIOMT2 1 0.000000 1.000000 0.000000 0.00000 +REMARK 350 BIOMT3 1 0.000000 0.000000 1.000000 0.00000 +REMARK 350 BIOMOLECULE: 2 +REMARK 350 APPLY THE FOLLOWING TO CHAINS: C, D +REMARK 350 BIOMT1 1 1.000000 0.000000 0.000000 0.00000 +REMARK 350 BIOMT2 1 0.000000 1.000000 0.000000 0.00000 +REMARK 350 BIOMT3 1 0.000000 0.000000 1.000000 0.00000 +REMARK 375 +REMARK 375 SPECIAL POSITION +REMARK 375 THE FOLLOWING ATOMS ARE FOUND TO BE WITHIN 0.15 ANGSTROMS +REMARK 375 OF A SYMMETRY RELATED ATOM AND ARE ASSUMED TO BE ON SPECIAL +REMARK 375 POSITIONS. +REMARK 375 +REMARK 375 ATOM RES CSSEQI +REMARK 375 ZN ZN B 1 LIES ON A SPECIAL POSITION. +REMARK 375 CL CL B 2 LIES ON A SPECIAL POSITION. +REMARK 375 ZN ZN D 1 LIES ON A SPECIAL POSITION. +REMARK 375 CL CL D 2 LIES ON A SPECIAL POSITION. +REMARK 375 HOH 2 LIES ON A SPECIAL POSITION. +REMARK 375 HOH 3 LIES ON A SPECIAL POSITION. +REMARK 375 HOH 4 LIES ON A SPECIAL POSITION. +REMARK 375 HOH 6 LIES ON A SPECIAL POSITION. +REMARK 470 +REMARK 470 MISSING ATOM +REMARK 470 THE FOLLOWING RESIDUES HAVE MISSING ATOMS(M=MODEL NUMBER; +REMARK 470 RES=RESIDUE NAME; C=CHAIN IDENTIFIER; SSEQ=SEQUENCE NUMBER; +REMARK 470 I=INSERTION CODE): +REMARK 470 M RES CSSEQI ATOMS +REMARK 470 LYS B 29 CG CD CE NZ +REMARK 470 THR B 30 OG1 CG2 +REMARK 470 VAL D 2 CG1 CG2 +REMARK 470 GLU D 21 CD OE1 OE2 +REMARK 470 LYS D 29 CG CD CE NZ +REMARK 470 THR D 30 CB OG1 CG2 +REMARK 500 +REMARK 500 GEOMETRY AND STEREOCHEMISTRY +REMARK 500 SUBTOPIC: CLOSE CONTACTS IN SAME ASYMMETRIC UNIT +REMARK 500 +REMARK 500 THE FOLLOWING ATOMS ARE IN CLOSE CONTACT. +REMARK 500 +REMARK 500 ATM1 RES C SSEQI ATM2 RES C SSEQI +REMARK 500 CL CL D 2 O HOH 2 0.28 +REMARK 500 CL CL B 2 O HOH 1 1.70 +REMARK 500 OXT THR B 30 O HOH 98 1.95 +REMARK 500 ZN ZN D 1 O HOH 2 2.04 +REMARK 500 O HOH 102 O HOH 103 2.10 +REMARK 500 +REMARK 500 GEOMETRY AND STEREOCHEMISTRY +REMARK 500 SUBTOPIC: TORSION ANGLES +REMARK 500 +REMARK 500 TORSION ANGLES OUTSIDE THE EXPECTED RAMACHANDRAN REGIONS: +REMARK 500 (M=MODEL NUMBER; RES=RESIDUE NAME; C=CHAIN IDENTIFIER; +REMARK 500 SSEQ=SEQUENCE NUMBER; I=INSERTION CODE). +REMARK 500 +REMARK 500 STANDARD TABLE: +REMARK 500 FORMAT:(10X,I3,1X,A3,1X,A1,I4,A1,4X,F7.2,3X,F7.2) +REMARK 500 +REMARK 500 M RES CSSEQI PSI PHI +REMARK 500 LYS D 29 -97.67 -177.85 +REMARK 525 +REMARK 525 SOLVENT +REMARK 525 THE FOLLOWING SOLVENT MOLECULES LIE FARTHER THAN EXPECTED +REMARK 525 FROM THE PROTEIN OR NUCLEIC ACID MOLECULE AND MAY BE +REMARK 525 ASSOCIATED WITH A SYMMETRY RELATED MOLECULE (M=MODEL +REMARK 525 NUMBER; RES=RESIDUE NAME; C=CHAIN IDENTIFIER; SSEQ=SEQUENCE +REMARK 525 NUMBER; I=INSERTION CODE): +REMARK 525 +REMARK 525 M RES CSSEQI +REMARK 525 HOH 6 DISTANCE = 9.01 ANGSTROMS +REMARK 525 HOH 11 DISTANCE = 9.50 ANGSTROMS +REMARK 525 HOH 13 DISTANCE = 18.03 ANGSTROMS +REMARK 525 HOH 14 DISTANCE = 6.44 ANGSTROMS +REMARK 525 HOH 15 DISTANCE = 16.53 ANGSTROMS +REMARK 525 HOH 17 DISTANCE = 13.52 ANGSTROMS +REMARK 525 HOH 19 DISTANCE = 20.42 ANGSTROMS +REMARK 525 HOH 20 DISTANCE = 17.18 ANGSTROMS +REMARK 525 HOH 21 DISTANCE = 5.36 ANGSTROMS +REMARK 525 HOH 22 DISTANCE = 17.43 ANGSTROMS +REMARK 525 HOH 27 DISTANCE = 6.67 ANGSTROMS +REMARK 525 HOH 35 DISTANCE = 6.83 ANGSTROMS +REMARK 525 HOH 52 DISTANCE = 7.30 ANGSTROMS +REMARK 525 HOH 81 DISTANCE = 9.25 ANGSTROMS +REMARK 525 HOH 97 DISTANCE = 5.45 ANGSTROMS +REMARK 525 HOH 106 DISTANCE = 7.59 ANGSTROMS +REMARK 525 HOH 108 DISTANCE = 7.18 ANGSTROMS +REMARK 525 HOH 114 DISTANCE = 8.53 ANGSTROMS +REMARK 525 HOH 120 DISTANCE = 7.61 ANGSTROMS +DBREF 1TRZ A 1 21 UNP P01308 INS_HUMAN 31 51 +DBREF 1TRZ B 1 30 UNP P01308 INS_HUMAN 25 54 +DBREF 1TRZ C 1 21 UNP P01308 INS_HUMAN 31 51 +DBREF 1TRZ D 1 30 UNP P01308 INS_HUMAN 25 54 +SEQRES 1 A 21 GLY ILE VAL GLU GLN CYS CYS THR SER ILE CYS SER LEU +SEQRES 2 A 21 TYR GLN LEU GLU ASN TYR CYS ASN +SEQRES 1 B 30 PHE VAL ASN GLN HIS LEU CYS GLY SER HIS LEU VAL GLU +SEQRES 2 B 30 ALA LEU TYR LEU VAL CYS GLY GLU ARG GLY PHE PHE TYR +SEQRES 3 B 30 THR PRO LYS THR +SEQRES 1 C 21 GLY ILE VAL GLU GLN CYS CYS THR SER ILE CYS SER LEU +SEQRES 2 C 21 TYR GLN LEU GLU ASN TYR CYS ASN +SEQRES 1 D 30 PHE VAL ASN GLN HIS LEU CYS GLY SER HIS LEU VAL GLU +SEQRES 2 D 30 ALA LEU TYR LEU VAL CYS GLY GLU ARG GLY PHE PHE TYR +SEQRES 3 D 30 THR PRO LYS THR +HET ZN B 1 1 +HET CL B 2 1 +HET ZN D 1 1 +HET CL D 2 1 +HET NA 1 1 +HETNAM ZN ZINC ION +HETNAM CL CHLORIDE ION +HETNAM NA SODIUM ION +FORMUL 5 ZN 2(ZN 2+) +FORMUL 6 CL 2(CL 1-) +FORMUL 9 NA NA 1+ +FORMUL 10 HOH *123(H2 O) +HELIX 1 1 GLY A 1 CYS A 7 1 7 +HELIX 2 2 SER A 12 GLU A 17 1 6 +HELIX 3 3 GLY B 8 GLY B 20 1 13 +HELIX 4 4 GLU B 21 GLY B 23 5 3 +HELIX 5 5 GLY C 1 SER C 9 1 9 +HELIX 6 6 SER C 12 GLU C 17 1 6 +HELIX 7 7 ASN D 3 GLY D 20 1 18 +HELIX 8 8 GLU D 21 GLY D 23 5 3 +SHEET 1 A 2 PHE B 24 TYR B 26 0 +SHEET 2 A 2 PHE D 24 TYR D 26 -1 O PHE D 24 N TYR B 26 +SSBOND 1 CYS A 6 CYS A 11 +SSBOND 2 CYS A 7 CYS B 7 +SSBOND 3 CYS A 20 CYS B 19 +SSBOND 4 CYS C 6 CYS C 11 +SSBOND 5 CYS C 7 CYS D 7 +SSBOND 6 CYS C 20 CYS D 19 +CRYST1 80.638 80.638 37.782 90.00 90.00 120.00 H 3 18 +ORIGX1 1.000000 0.000000 0.000000 0.00000 +ORIGX2 0.000000 1.000000 0.000000 0.00000 +ORIGX3 0.000000 0.000000 1.000000 0.00000 +SCALE1 0.012401 0.007160 0.000000 0.00000 +SCALE2 0.000000 0.014320 0.000000 0.00000 +SCALE3 0.000000 0.000000 0.026468 0.00000 +ATOM 1 N GLY A 1 -0.959 20.540 -13.302 1.00 34.83 N +ATOM 2 CA GLY A 1 -0.916 20.008 -11.944 1.00 32.41 C +ATOM 3 C GLY A 1 0.057 18.832 -11.817 1.00 31.30 C +ATOM 4 O GLY A 1 0.831 18.453 -12.730 1.00 29.55 O +ATOM 5 N ILE A 2 -0.009 18.266 -10.626 1.00 30.06 N +ATOM 6 CA ILE A 2 0.908 17.139 -10.268 1.00 30.94 C +ATOM 7 C ILE A 2 0.792 15.905 -11.121 1.00 31.01 C +ATOM 8 O ILE A 2 1.829 15.294 -11.327 1.00 30.65 O +ATOM 9 CB ILE A 2 0.685 16.819 -8.753 1.00 30.38 C +ATOM 10 CG1 ILE A 2 1.797 15.907 -8.190 1.00 30.56 C +ATOM 11 CG2 ILE A 2 -0.744 16.225 -8.431 1.00 30.84 C +ATOM 12 CD1 ILE A 2 1.976 16.014 -6.625 1.00 28.71 C +ATOM 13 N VAL A 3 -0.396 15.572 -11.595 1.00 33.79 N +ATOM 14 CA VAL A 3 -0.626 14.386 -12.428 1.00 33.62 C +ATOM 15 C VAL A 3 0.165 14.468 -13.731 1.00 35.14 C +ATOM 16 O VAL A 3 0.826 13.529 -14.141 1.00 35.79 O +ATOM 17 CB VAL A 3 -2.128 14.109 -12.669 1.00 33.03 C +ATOM 18 CG1 VAL A 3 -2.259 13.012 -13.731 1.00 32.85 C +ATOM 19 CG2 VAL A 3 -2.816 13.837 -11.346 1.00 33.02 C +ATOM 20 N GLU A 4 -0.022 15.642 -14.344 1.00 36.19 N +ATOM 21 CA GLU A 4 0.679 15.912 -15.615 1.00 38.87 C +ATOM 22 C GLU A 4 2.158 15.825 -15.384 1.00 37.44 C +ATOM 23 O GLU A 4 2.866 15.111 -16.138 1.00 37.84 O +ATOM 24 CB GLU A 4 0.201 17.257 -16.182 1.00 41.95 C +ATOM 25 CG GLU A 4 -1.297 17.494 -16.096 1.00 45.96 C +ATOM 26 CD GLU A 4 -2.186 17.587 -14.916 1.00 48.93 C +ATOM 27 OE1 GLU A 4 -2.034 18.298 -13.920 1.00 51.06 O +ATOM 28 OE2 GLU A 4 -3.245 16.872 -14.995 1.00 50.17 O +ATOM 29 N GLN A 5 2.694 16.445 -14.348 1.00 37.88 N +ATOM 30 CA GLN A 5 4.110 16.436 -14.050 1.00 37.58 C +ATOM 31 C GLN A 5 4.719 15.114 -13.499 1.00 36.19 C +ATOM 32 O GLN A 5 5.825 14.911 -14.036 1.00 37.45 O +ATOM 33 CB GLN A 5 4.602 17.465 -13.012 1.00 37.75 C +ATOM 34 CG GLN A 5 4.253 18.918 -13.362 1.00 41.14 C +ATOM 35 CD GLN A 5 5.275 19.783 -12.651 1.00 42.71 C +ATOM 36 OE1 GLN A 5 5.198 20.082 -11.452 1.00 44.62 O +ATOM 37 NE2 GLN A 5 6.285 20.073 -13.476 1.00 44.49 N +ATOM 38 N CYS A 6 4.023 14.490 -12.570 1.00 34.24 N +ATOM 39 CA CYS A 6 4.613 13.291 -11.933 1.00 32.92 C +ATOM 40 C CYS A 6 4.067 11.919 -12.291 1.00 31.56 C +ATOM 41 O CYS A 6 4.690 10.935 -11.910 1.00 28.38 O +ATOM 42 CB CYS A 6 4.573 13.521 -10.404 1.00 33.33 C +ATOM 43 SG CYS A 6 5.339 14.979 -9.714 1.00 36.34 S +ATOM 44 N CYS A 7 2.936 11.895 -12.897 1.00 30.72 N +ATOM 45 CA CYS A 7 2.263 10.693 -13.346 1.00 32.27 C +ATOM 46 C CYS A 7 2.480 10.499 -14.858 1.00 34.89 C +ATOM 47 O CYS A 7 3.019 9.470 -15.319 1.00 33.55 O +ATOM 48 CB CYS A 7 0.780 10.655 -12.951 1.00 32.93 C +ATOM 49 SG CYS A 7 0.032 9.146 -13.579 1.00 33.34 S +ATOM 50 N THR A 8 2.048 11.486 -15.591 1.00 36.07 N +ATOM 51 CA THR A 8 2.184 11.485 -17.082 1.00 39.16 C +ATOM 52 C THR A 8 3.657 11.477 -17.421 1.00 37.65 C +ATOM 53 O THR A 8 4.016 10.573 -18.201 1.00 39.88 O +ATOM 54 CB THR A 8 1.239 12.623 -17.614 1.00 40.20 C +ATOM 55 OG1 THR A 8 -0.068 12.110 -17.238 1.00 42.68 O +ATOM 56 CG2 THR A 8 1.291 12.958 -19.081 1.00 40.87 C +ATOM 57 N SER A 9 4.476 12.328 -16.874 1.00 35.94 N +ATOM 58 CA SER A 9 5.912 12.525 -16.993 1.00 36.76 C +ATOM 59 C SER A 9 6.579 12.011 -15.700 1.00 36.13 C +ATOM 60 O SER A 9 5.885 11.416 -14.856 1.00 36.11 O +ATOM 61 CB SER A 9 6.348 13.975 -17.114 1.00 38.44 C +ATOM 62 OG SER A 9 5.516 14.923 -17.736 1.00 40.36 O +ATOM 63 N ILE A 10 7.856 12.278 -15.530 1.00 35.02 N +ATOM 64 CA ILE A 10 8.576 11.901 -14.314 1.00 32.74 C +ATOM 65 C ILE A 10 9.106 13.136 -13.619 1.00 34.74 C +ATOM 66 O ILE A 10 9.669 14.051 -14.265 1.00 35.64 O +ATOM 67 CB ILE A 10 9.731 10.880 -14.513 1.00 31.63 C +ATOM 68 CG1 ILE A 10 9.160 9.715 -15.300 1.00 32.57 C +ATOM 69 CG2 ILE A 10 10.337 10.613 -13.111 1.00 30.64 C +ATOM 70 CD1 ILE A 10 10.037 8.452 -15.409 1.00 35.23 C +ATOM 71 N CYS A 11 8.929 13.150 -12.333 1.00 35.69 N +ATOM 72 CA CYS A 11 9.291 14.128 -11.354 1.00 36.51 C +ATOM 73 C CYS A 11 10.464 13.782 -10.450 1.00 36.12 C +ATOM 74 O CYS A 11 10.506 12.715 -9.849 1.00 37.01 O +ATOM 75 CB CYS A 11 8.156 14.305 -10.292 1.00 37.63 C +ATOM 76 SG CYS A 11 6.939 15.512 -10.795 1.00 41.85 S +ATOM 77 N SER A 12 11.335 14.762 -10.305 1.00 34.45 N +ATOM 78 CA SER A 12 12.489 14.619 -9.421 1.00 34.29 C +ATOM 79 C SER A 12 11.944 14.901 -8.016 1.00 32.94 C +ATOM 80 O SER A 12 10.803 15.391 -7.883 1.00 31.09 O +ATOM 81 CB SER A 12 13.595 15.644 -9.739 1.00 34.57 C +ATOM 82 OG SER A 12 13.195 16.930 -9.252 1.00 37.31 O +ATOM 83 N LEU A 13 12.734 14.648 -7.005 1.00 32.47 N +ATOM 84 CA LEU A 13 12.305 14.934 -5.645 1.00 33.89 C +ATOM 85 C LEU A 13 11.953 16.417 -5.479 1.00 35.04 C +ATOM 86 O LEU A 13 10.996 16.715 -4.756 1.00 34.84 O +ATOM 87 CB LEU A 13 13.442 14.446 -4.746 1.00 31.96 C +ATOM 88 CG LEU A 13 13.421 14.872 -3.303 1.00 33.48 C +ATOM 89 CD1 LEU A 13 12.165 14.263 -2.709 1.00 33.03 C +ATOM 90 CD2 LEU A 13 14.651 14.327 -2.576 1.00 34.28 C +ATOM 91 N TYR A 14 12.741 17.258 -6.117 1.00 35.51 N +ATOM 92 CA TYR A 14 12.584 18.707 -6.017 1.00 36.68 C +ATOM 93 C TYR A 14 11.287 19.207 -6.591 1.00 35.25 C +ATOM 94 O TYR A 14 10.706 20.161 -6.042 1.00 36.09 O +ATOM 95 CB TYR A 14 13.954 19.338 -6.420 1.00 39.57 C +ATOM 96 CG TYR A 14 14.927 18.800 -5.357 1.00 43.01 C +ATOM 97 CD1 TYR A 14 14.690 19.061 -4.001 1.00 44.96 C +ATOM 98 CD2 TYR A 14 16.047 18.038 -5.670 1.00 45.21 C +ATOM 99 CE1 TYR A 14 15.520 18.573 -3.001 1.00 46.31 C +ATOM 100 CE2 TYR A 14 16.909 17.554 -4.683 1.00 46.56 C +ATOM 101 CZ TYR A 14 16.649 17.822 -3.351 1.00 47.04 C +ATOM 102 OH TYR A 14 17.501 17.338 -2.367 1.00 47.92 O +ATOM 103 N GLN A 15 10.760 18.586 -7.589 1.00 34.97 N +ATOM 104 CA GLN A 15 9.470 18.904 -8.229 1.00 33.56 C +ATOM 105 C GLN A 15 8.314 18.392 -7.365 1.00 32.79 C +ATOM 106 O GLN A 15 7.169 18.880 -7.473 1.00 32.75 O +ATOM 107 CB GLN A 15 9.337 18.329 -9.642 1.00 34.60 C +ATOM 108 CG GLN A 15 10.040 19.101 -10.742 1.00 36.70 C +ATOM 109 CD GLN A 15 10.156 18.341 -12.022 1.00 37.77 C +ATOM 110 OE1 GLN A 15 9.824 18.799 -13.122 1.00 40.78 O +ATOM 111 NE2 GLN A 15 10.635 17.102 -12.002 1.00 36.46 N +ATOM 112 N LEU A 16 8.571 17.385 -6.537 1.00 31.36 N +ATOM 113 CA LEU A 16 7.499 16.806 -5.690 1.00 29.78 C +ATOM 114 C LEU A 16 7.221 17.695 -4.503 1.00 27.61 C +ATOM 115 O LEU A 16 6.136 17.954 -3.976 1.00 26.34 O +ATOM 116 CB LEU A 16 7.971 15.378 -5.389 1.00 30.36 C +ATOM 117 CG LEU A 16 7.118 14.179 -5.145 1.00 31.40 C +ATOM 118 CD1 LEU A 16 5.877 14.056 -6.037 1.00 31.45 C +ATOM 119 CD2 LEU A 16 8.092 12.973 -5.312 1.00 30.86 C +ATOM 120 N GLU A 17 8.300 18.296 -3.992 1.00 26.53 N +ATOM 121 CA GLU A 17 8.320 19.213 -2.841 1.00 28.65 C +ATOM 122 C GLU A 17 7.520 20.482 -3.208 1.00 27.16 C +ATOM 123 O GLU A 17 7.080 21.073 -2.244 1.00 26.88 O +ATOM 124 CB GLU A 17 9.733 19.607 -2.419 1.00 32.80 C +ATOM 125 CG GLU A 17 10.417 18.375 -1.806 1.00 35.93 C +ATOM 126 CD GLU A 17 11.562 18.809 -0.912 1.00 39.29 C +ATOM 127 OE1 GLU A 17 11.324 19.150 0.236 1.00 39.62 O +ATOM 128 OE2 GLU A 17 12.611 18.733 -1.569 1.00 40.02 O +ATOM 129 N ASN A 18 7.307 20.780 -4.444 1.00 27.04 N +ATOM 130 CA ASN A 18 6.467 21.963 -4.737 1.00 28.69 C +ATOM 131 C ASN A 18 5.038 21.721 -4.263 1.00 27.78 C +ATOM 132 O ASN A 18 4.225 22.654 -4.210 1.00 25.53 O +ATOM 133 CB ASN A 18 6.476 22.067 -6.263 1.00 29.15 C +ATOM 134 CG ASN A 18 7.791 22.775 -6.663 1.00 32.10 C +ATOM 135 OD1 ASN A 18 8.098 22.497 -7.820 1.00 34.25 O +ATOM 136 ND2 ASN A 18 8.332 23.515 -5.707 1.00 32.45 N +ATOM 137 N TYR A 19 4.642 20.465 -4.005 1.00 26.74 N +ATOM 138 CA TYR A 19 3.272 20.198 -3.546 1.00 25.94 C +ATOM 139 C TYR A 19 3.165 20.037 -2.073 1.00 24.53 C +ATOM 140 O TYR A 19 2.073 19.687 -1.528 1.00 29.14 O +ATOM 141 CB TYR A 19 2.636 19.035 -4.401 1.00 24.75 C +ATOM 142 CG TYR A 19 2.712 19.300 -5.884 1.00 24.20 C +ATOM 143 CD1 TYR A 19 1.713 20.131 -6.485 1.00 27.34 C +ATOM 144 CD2 TYR A 19 3.766 18.823 -6.673 1.00 26.79 C +ATOM 145 CE1 TYR A 19 1.826 20.411 -7.838 1.00 27.55 C +ATOM 146 CE2 TYR A 19 3.807 19.094 -8.045 1.00 27.26 C +ATOM 147 CZ TYR A 19 2.824 19.887 -8.611 1.00 28.22 C +ATOM 148 OH TYR A 19 2.903 20.158 -9.937 1.00 28.73 O +ATOM 149 N CYS A 20 4.250 20.331 -1.336 1.00 27.62 N +ATOM 150 CA CYS A 20 4.194 20.265 0.137 1.00 27.07 C +ATOM 151 C CYS A 20 3.511 21.582 0.588 1.00 29.15 C +ATOM 152 O CYS A 20 3.449 22.524 -0.215 1.00 32.61 O +ATOM 153 CB CYS A 20 5.547 20.151 0.773 1.00 25.04 C +ATOM 154 SG CYS A 20 6.377 18.693 0.235 1.00 26.78 S +ATOM 155 N ASN A 21 3.120 21.590 1.837 1.00 31.81 N +ATOM 156 CA ASN A 21 2.486 22.778 2.447 1.00 36.39 C +ATOM 157 C ASN A 21 3.585 23.637 3.116 1.00 39.14 C +ATOM 158 O ASN A 21 4.773 23.378 2.845 1.00 40.52 O +ATOM 159 CB ASN A 21 1.390 22.557 3.445 1.00 36.52 C +ATOM 160 CG ASN A 21 0.118 21.825 3.139 1.00 36.74 C +ATOM 161 OD1 ASN A 21 -0.583 21.916 2.121 1.00 37.65 O +ATOM 162 ND2 ASN A 21 -0.268 21.064 4.161 1.00 37.49 N +ATOM 163 OXT ASN A 21 3.120 24.493 3.890 1.00 42.96 O +TER 164 ASN A 21 +ATOM 165 N PHE B 1 16.300 8.986 -4.984 1.00 31.97 N +ATOM 166 CA PHE B 1 14.820 8.979 -5.005 1.00 34.01 C +ATOM 167 C PHE B 1 14.495 8.220 -6.298 1.00 35.01 C +ATOM 168 O PHE B 1 15.249 8.347 -7.276 1.00 35.05 O +ATOM 169 CB PHE B 1 14.150 10.333 -4.859 1.00 33.89 C +ATOM 170 CG PHE B 1 12.772 10.248 -4.259 1.00 34.30 C +ATOM 171 CD1 PHE B 1 12.574 10.205 -2.883 1.00 32.62 C +ATOM 172 CD2 PHE B 1 11.664 10.129 -5.100 1.00 35.10 C +ATOM 173 CE1 PHE B 1 11.324 10.076 -2.340 1.00 33.91 C +ATOM 174 CE2 PHE B 1 10.356 9.986 -4.613 1.00 33.84 C +ATOM 175 CZ PHE B 1 10.247 9.942 -3.214 1.00 33.12 C +ATOM 176 N VAL B 2 13.413 7.485 -6.288 1.00 34.03 N +ATOM 177 CA VAL B 2 13.028 6.712 -7.470 1.00 35.27 C +ATOM 178 C VAL B 2 12.759 7.703 -8.597 1.00 36.90 C +ATOM 179 O VAL B 2 12.359 8.842 -8.312 1.00 36.96 O +ATOM 180 CB VAL B 2 11.837 5.817 -7.030 1.00 36.13 C +ATOM 181 CG1 VAL B 2 10.535 6.576 -7.066 1.00 32.67 C +ATOM 182 CG2 VAL B 2 11.776 4.547 -7.853 1.00 36.24 C +ATOM 183 N ASN B 3 12.992 7.276 -9.811 1.00 39.07 N +ATOM 184 CA ASN B 3 12.805 7.988 -11.067 1.00 41.16 C +ATOM 185 C ASN B 3 11.749 7.161 -11.811 1.00 40.65 C +ATOM 186 O ASN B 3 12.069 6.257 -12.615 1.00 40.81 O +ATOM 187 CB ASN B 3 14.117 8.177 -11.818 1.00 44.94 C +ATOM 188 CG ASN B 3 14.046 8.924 -13.127 1.00 47.66 C +ATOM 189 OD1 ASN B 3 13.658 10.119 -13.252 1.00 49.82 O +ATOM 190 ND2 ASN B 3 14.498 8.302 -14.238 1.00 49.24 N +ATOM 191 N GLN B 4 10.483 7.409 -11.477 1.00 38.71 N +ATOM 192 CA GLN B 4 9.436 6.644 -12.185 1.00 39.14 C +ATOM 193 C GLN B 4 8.165 7.490 -12.177 1.00 35.97 C +ATOM 194 O GLN B 4 8.114 8.457 -11.428 1.00 35.42 O +ATOM 195 CB GLN B 4 9.226 5.263 -11.602 1.00 42.28 C +ATOM 196 CG GLN B 4 8.887 5.094 -10.150 1.00 45.69 C +ATOM 197 CD GLN B 4 7.799 4.087 -9.826 1.00 48.38 C +ATOM 198 OE1 GLN B 4 6.995 3.596 -10.630 1.00 50.26 O +ATOM 199 NE2 GLN B 4 7.662 3.651 -8.563 1.00 49.03 N +ATOM 200 N HIS B 5 7.240 7.036 -12.990 1.00 34.75 N +ATOM 201 CA HIS B 5 5.889 7.666 -13.121 1.00 32.83 C +ATOM 202 C HIS B 5 5.226 7.317 -11.798 1.00 31.23 C +ATOM 203 O HIS B 5 5.154 6.099 -11.531 1.00 31.75 O +ATOM 204 CB HIS B 5 5.109 7.031 -14.277 1.00 32.82 C +ATOM 205 CG HIS B 5 5.747 7.321 -15.615 1.00 34.91 C +ATOM 206 ND1 HIS B 5 5.411 8.450 -16.336 1.00 36.14 N +ATOM 207 CD2 HIS B 5 6.674 6.658 -16.363 1.00 34.59 C +ATOM 208 CE1 HIS B 5 6.083 8.497 -17.473 1.00 35.96 C +ATOM 209 NE2 HIS B 5 6.849 7.425 -17.489 1.00 34.57 N +ATOM 210 N LEU B 6 4.804 8.257 -11.009 1.00 29.54 N +ATOM 211 CA LEU B 6 4.170 8.003 -9.694 1.00 27.30 C +ATOM 212 C LEU B 6 2.707 8.353 -9.941 1.00 27.52 C +ATOM 213 O LEU B 6 2.503 9.544 -10.246 1.00 28.98 O +ATOM 214 CB LEU B 6 4.917 8.825 -8.677 1.00 28.38 C +ATOM 215 CG LEU B 6 6.303 8.421 -8.226 1.00 28.44 C +ATOM 216 CD1 LEU B 6 6.936 9.515 -7.364 1.00 27.03 C +ATOM 217 CD2 LEU B 6 6.116 7.129 -7.439 1.00 30.13 C +ATOM 218 N CYS B 7 1.804 7.424 -9.828 1.00 28.87 N +ATOM 219 CA CYS B 7 0.400 7.674 -10.143 1.00 28.74 C +ATOM 220 C CYS B 7 -0.498 7.119 -9.040 1.00 28.43 C +ATOM 221 O CYS B 7 -0.062 6.217 -8.339 1.00 28.62 O +ATOM 222 CB CYS B 7 0.021 7.042 -11.481 1.00 31.29 C +ATOM 223 SG CYS B 7 1.000 7.476 -12.948 1.00 33.52 S +ATOM 224 N GLY B 8 -1.728 7.566 -9.019 1.00 28.85 N +ATOM 225 CA GLY B 8 -2.733 7.134 -8.065 1.00 25.67 C +ATOM 226 C GLY B 8 -2.256 7.277 -6.644 1.00 24.25 C +ATOM 227 O GLY B 8 -1.718 8.251 -6.157 1.00 25.16 O +ATOM 228 N SER B 9 -2.532 6.203 -5.900 1.00 21.96 N +ATOM 229 CA SER B 9 -2.160 6.192 -4.496 1.00 21.32 C +ATOM 230 C SER B 9 -0.604 6.158 -4.378 1.00 18.86 C +ATOM 231 O SER B 9 -0.094 6.576 -3.279 1.00 22.13 O +ATOM 232 CB SER B 9 -2.808 4.993 -3.818 1.00 23.22 C +ATOM 233 OG SER B 9 -2.422 3.805 -4.511 1.00 23.64 O +ATOM 234 N HIS B 10 0.104 5.770 -5.418 1.00 19.34 N +ATOM 235 CA HIS B 10 1.630 5.740 -5.330 1.00 17.63 C +ATOM 236 C HIS B 10 2.130 7.208 -5.280 1.00 20.67 C +ATOM 237 O HIS B 10 2.969 7.526 -4.414 1.00 18.68 O +ATOM 238 CB HIS B 10 2.246 4.986 -6.478 1.00 19.89 C +ATOM 239 CG HIS B 10 1.815 3.527 -6.536 1.00 20.28 C +ATOM 240 ND1 HIS B 10 2.151 2.746 -5.471 1.00 23.31 N +ATOM 241 CD2 HIS B 10 1.043 2.739 -7.269 1.00 20.08 C +ATOM 242 CE1 HIS B 10 1.664 1.511 -5.663 1.00 19.20 C +ATOM 243 NE2 HIS B 10 0.955 1.430 -6.752 1.00 20.24 N +ATOM 244 N LEU B 11 1.537 8.083 -6.041 1.00 19.70 N +ATOM 245 CA LEU B 11 1.900 9.521 -5.963 1.00 19.89 C +ATOM 246 C LEU B 11 1.568 10.049 -4.584 1.00 18.80 C +ATOM 247 O LEU B 11 2.370 10.799 -3.997 1.00 19.49 O +ATOM 248 CB LEU B 11 1.029 10.175 -7.055 1.00 19.63 C +ATOM 249 CG LEU B 11 1.256 11.699 -7.197 1.00 20.75 C +ATOM 250 CD1 LEU B 11 2.726 12.115 -7.052 1.00 20.29 C +ATOM 251 CD2 LEU B 11 0.612 12.011 -8.594 1.00 22.01 C +ATOM 252 N VAL B 12 0.358 9.791 -3.954 1.00 17.21 N +ATOM 253 CA VAL B 12 -0.024 10.278 -2.641 1.00 19.83 C +ATOM 254 C VAL B 12 0.887 9.813 -1.514 1.00 16.10 C +ATOM 255 O VAL B 12 1.394 10.587 -0.634 1.00 18.16 O +ATOM 256 CB VAL B 12 -1.501 9.840 -2.447 1.00 21.87 C +ATOM 257 CG1 VAL B 12 -1.985 10.058 -1.017 1.00 23.59 C +ATOM 258 CG2 VAL B 12 -2.270 10.711 -3.407 1.00 20.90 C +ATOM 259 N GLU B 13 1.242 8.532 -1.489 1.00 19.30 N +ATOM 260 CA GLU B 13 2.116 7.975 -0.483 1.00 22.23 C +ATOM 261 C GLU B 13 3.498 8.672 -0.588 1.00 20.28 C +ATOM 262 O GLU B 13 4.146 9.007 0.399 1.00 21.60 O +ATOM 263 CB GLU B 13 2.286 6.482 -0.648 1.00 26.34 C +ATOM 264 CG GLU B 13 2.584 5.620 0.553 1.00 32.86 C +ATOM 265 CD GLU B 13 1.716 5.751 1.803 1.00 35.60 C +ATOM 266 OE1 GLU B 13 0.477 5.796 1.844 1.00 39.62 O +ATOM 267 OE2 GLU B 13 2.480 5.873 2.795 1.00 36.79 O +ATOM 268 N ALA B 14 3.988 9.015 -1.713 1.00 19.47 N +ATOM 269 CA ALA B 14 5.256 9.664 -2.049 1.00 20.30 C +ATOM 270 C ALA B 14 5.265 11.069 -1.410 1.00 17.88 C +ATOM 271 O ALA B 14 6.182 11.532 -0.722 1.00 19.04 O +ATOM 272 CB ALA B 14 5.548 9.726 -3.548 1.00 20.30 C +ATOM 273 N LEU B 15 4.129 11.741 -1.620 1.00 20.76 N +ATOM 274 CA LEU B 15 3.922 13.072 -1.099 1.00 17.27 C +ATOM 275 C LEU B 15 3.907 13.030 0.440 1.00 16.59 C +ATOM 276 O LEU B 15 4.400 13.938 1.098 1.00 21.06 O +ATOM 277 CB LEU B 15 2.608 13.685 -1.643 1.00 20.28 C +ATOM 278 CG LEU B 15 2.649 14.315 -3.041 1.00 21.81 C +ATOM 279 CD1 LEU B 15 1.189 14.579 -3.436 1.00 22.05 C +ATOM 280 CD2 LEU B 15 3.692 15.406 -3.053 1.00 21.05 C +ATOM 281 N TYR B 16 3.303 12.015 1.002 1.00 18.29 N +ATOM 282 CA TYR B 16 3.271 11.838 2.471 1.00 20.10 C +ATOM 283 C TYR B 16 4.681 11.709 3.040 1.00 21.91 C +ATOM 284 O TYR B 16 4.985 12.420 4.017 1.00 21.80 O +ATOM 285 CB TYR B 16 2.418 10.579 2.788 1.00 20.38 C +ATOM 286 CG TYR B 16 2.248 10.315 4.280 1.00 22.86 C +ATOM 287 CD1 TYR B 16 1.415 11.159 4.988 1.00 24.32 C +ATOM 288 CD2 TYR B 16 2.865 9.273 4.952 1.00 22.56 C +ATOM 289 CE1 TYR B 16 1.125 10.996 6.339 1.00 23.65 C +ATOM 290 CE2 TYR B 16 2.630 9.104 6.318 1.00 19.78 C +ATOM 291 CZ TYR B 16 1.791 9.942 7.008 1.00 21.75 C +ATOM 292 OH TYR B 16 1.613 9.674 8.336 1.00 26.06 O +ATOM 293 N LEU B 17 5.428 10.822 2.398 1.00 21.79 N +ATOM 294 CA LEU B 17 6.854 10.589 2.819 1.00 23.93 C +ATOM 295 C LEU B 17 7.703 11.844 2.658 1.00 23.63 C +ATOM 296 O LEU B 17 8.523 12.316 3.448 1.00 24.37 O +ATOM 297 CB LEU B 17 7.345 9.463 1.867 1.00 26.65 C +ATOM 298 CG LEU B 17 8.827 9.160 2.043 1.00 30.10 C +ATOM 299 CD1 LEU B 17 8.942 8.862 3.546 1.00 32.04 C +ATOM 300 CD2 LEU B 17 9.369 8.014 1.231 1.00 29.03 C +ATOM 301 N VAL B 18 7.661 12.431 1.462 1.00 22.44 N +ATOM 302 CA VAL B 18 8.519 13.630 1.213 1.00 25.49 C +ATOM 303 C VAL B 18 8.117 14.883 1.979 1.00 26.18 C +ATOM 304 O VAL B 18 9.056 15.612 2.399 1.00 30.44 O +ATOM 305 CB VAL B 18 8.563 13.970 -0.266 1.00 24.71 C +ATOM 306 CG1 VAL B 18 9.127 15.394 -0.436 1.00 25.95 C +ATOM 307 CG2 VAL B 18 9.242 12.981 -1.214 1.00 27.24 C +ATOM 308 N CYS B 19 6.829 15.116 2.173 1.00 24.67 N +ATOM 309 CA CYS B 19 6.374 16.362 2.829 1.00 25.57 C +ATOM 310 C CYS B 19 6.443 16.266 4.316 1.00 29.26 C +ATOM 311 O CYS B 19 6.629 17.306 4.963 1.00 31.44 O +ATOM 312 CB CYS B 19 5.056 16.818 2.177 1.00 21.79 C +ATOM 313 SG CYS B 19 5.102 17.105 0.465 1.00 23.67 S +ATOM 314 N GLY B 20 6.356 15.085 4.905 1.00 31.47 N +ATOM 315 CA GLY B 20 6.429 14.931 6.343 1.00 33.91 C +ATOM 316 C GLY B 20 5.406 15.781 7.097 1.00 36.95 C +ATOM 317 O GLY B 20 4.226 15.892 6.738 1.00 36.85 O +ATOM 318 N GLU B 21 5.868 16.405 8.181 1.00 39.55 N +ATOM 319 CA GLU B 21 4.994 17.209 9.044 1.00 41.80 C +ATOM 320 C GLU B 21 4.446 18.434 8.329 1.00 39.01 C +ATOM 321 O GLU B 21 3.456 18.994 8.833 1.00 40.80 O +ATOM 322 CB GLU B 21 5.606 17.529 10.406 1.00 46.74 C +ATOM 323 CG GLU B 21 5.706 16.425 11.462 1.00 51.37 C +ATOM 324 CD GLU B 21 6.746 16.677 12.531 1.00 55.36 C +ATOM 325 OE1 GLU B 21 7.976 16.635 12.360 1.00 57.59 O +ATOM 326 OE2 GLU B 21 6.237 16.965 13.644 1.00 56.34 O +ATOM 327 N ARG B 22 5.048 18.801 7.221 1.00 35.89 N +ATOM 328 CA ARG B 22 4.633 19.948 6.414 1.00 35.34 C +ATOM 329 C ARG B 22 3.217 19.684 5.879 1.00 34.51 C +ATOM 330 O ARG B 22 2.371 20.621 5.800 1.00 35.57 O +ATOM 331 CB ARG B 22 5.500 20.291 5.220 1.00 35.79 C +ATOM 332 CG ARG B 22 6.763 21.083 5.360 1.00 37.56 C +ATOM 333 CD ARG B 22 7.264 21.563 4.027 1.00 39.17 C +ATOM 334 NE ARG B 22 8.209 20.582 3.556 1.00 42.89 N +ATOM 335 CZ ARG B 22 8.713 20.447 2.349 1.00 45.09 C +ATOM 336 NH1 ARG B 22 8.378 21.283 1.368 1.00 46.69 N +ATOM 337 NH2 ARG B 22 9.575 19.438 2.112 1.00 47.04 N +ATOM 338 N GLY B 23 3.035 18.436 5.450 1.00 31.19 N +ATOM 339 CA GLY B 23 1.694 18.118 4.924 1.00 27.48 C +ATOM 340 C GLY B 23 1.738 18.455 3.454 1.00 26.68 C +ATOM 341 O GLY B 23 2.791 18.947 2.981 1.00 24.49 O +ATOM 342 N PHE B 24 0.686 18.290 2.697 1.00 25.59 N +ATOM 343 CA PHE B 24 0.684 18.485 1.268 1.00 22.91 C +ATOM 344 C PHE B 24 -0.771 18.693 0.801 1.00 24.39 C +ATOM 345 O PHE B 24 -1.636 18.616 1.683 1.00 26.13 O +ATOM 346 CB PHE B 24 1.354 17.194 0.730 1.00 25.18 C +ATOM 347 CG PHE B 24 0.573 15.921 0.911 1.00 20.86 C +ATOM 348 CD1 PHE B 24 -0.331 15.565 -0.112 1.00 23.71 C +ATOM 349 CD2 PHE B 24 0.814 15.137 2.052 1.00 20.35 C +ATOM 350 CE1 PHE B 24 -1.066 14.381 0.053 1.00 22.07 C +ATOM 351 CE2 PHE B 24 0.085 13.908 2.163 1.00 17.78 C +ATOM 352 CZ PHE B 24 -0.845 13.579 1.125 1.00 20.93 C +ATOM 353 N PHE B 25 -0.750 19.031 -0.468 1.00 26.24 N +ATOM 354 CA PHE B 25 -2.038 19.270 -1.159 1.00 24.20 C +ATOM 355 C PHE B 25 -2.026 18.414 -2.382 1.00 24.25 C +ATOM 356 O PHE B 25 -1.097 18.326 -3.213 1.00 24.73 O +ATOM 357 CB PHE B 25 -2.268 20.784 -1.323 1.00 24.57 C +ATOM 358 CG PHE B 25 -1.352 21.484 -2.267 1.00 25.28 C +ATOM 359 CD1 PHE B 25 -1.703 21.582 -3.611 1.00 26.84 C +ATOM 360 CD2 PHE B 25 -0.162 22.031 -1.829 1.00 26.25 C +ATOM 361 CE1 PHE B 25 -0.842 22.212 -4.512 1.00 26.33 C +ATOM 362 CE2 PHE B 25 0.706 22.697 -2.709 1.00 26.21 C +ATOM 363 CZ PHE B 25 0.364 22.771 -4.083 1.00 23.32 C +ATOM 364 N TYR B 26 -3.132 17.697 -2.562 1.00 25.04 N +ATOM 365 CA TYR B 26 -3.379 16.853 -3.669 1.00 24.48 C +ATOM 366 C TYR B 26 -4.614 17.278 -4.442 1.00 26.43 C +ATOM 367 O TYR B 26 -5.723 17.096 -3.996 1.00 25.67 O +ATOM 368 CB TYR B 26 -3.357 15.352 -3.298 1.00 24.88 C +ATOM 369 CG TYR B 26 -3.611 14.499 -4.498 1.00 26.22 C +ATOM 370 CD1 TYR B 26 -2.549 14.280 -5.397 1.00 25.22 C +ATOM 371 CD2 TYR B 26 -4.822 13.808 -4.649 1.00 27.78 C +ATOM 372 CE1 TYR B 26 -2.783 13.532 -6.559 1.00 26.58 C +ATOM 373 CE2 TYR B 26 -5.005 13.003 -5.775 1.00 27.16 C +ATOM 374 CZ TYR B 26 -3.977 12.847 -6.684 1.00 28.80 C +ATOM 375 OH TYR B 26 -4.202 12.050 -7.772 1.00 28.09 O +ATOM 376 N THR B 27 -4.361 17.864 -5.585 1.00 25.58 N +ATOM 377 CA THR B 27 -5.381 18.457 -6.483 1.00 29.99 C +ATOM 378 C THR B 27 -5.153 17.984 -7.912 1.00 33.64 C +ATOM 379 O THR B 27 -4.508 18.621 -8.747 1.00 35.83 O +ATOM 380 CB THR B 27 -5.353 20.024 -6.115 1.00 29.26 C +ATOM 381 OG1 THR B 27 -3.994 20.496 -6.266 1.00 33.21 O +ATOM 382 CG2 THR B 27 -5.731 20.441 -4.680 1.00 29.26 C +ATOM 383 N PRO B 28 -5.664 16.814 -8.262 1.00 36.19 N +ATOM 384 CA PRO B 28 -5.509 16.162 -9.559 1.00 39.04 C +ATOM 385 C PRO B 28 -6.216 16.906 -10.670 1.00 42.96 C +ATOM 386 O PRO B 28 -5.886 16.673 -11.853 1.00 44.75 O +ATOM 387 CB PRO B 28 -6.022 14.744 -9.356 1.00 38.03 C +ATOM 388 CG PRO B 28 -7.026 14.916 -8.244 1.00 37.58 C +ATOM 389 CD PRO B 28 -6.425 15.953 -7.335 1.00 36.86 C +ATOM 390 N LYS B 29 -7.140 17.751 -10.261 1.00 45.48 N +ATOM 391 CA LYS B 29 -7.888 18.553 -11.244 1.00 50.29 C +ATOM 392 C LYS B 29 -6.903 19.022 -12.324 1.00 52.41 C +ATOM 393 O LYS B 29 -6.087 19.939 -12.100 1.00 52.75 O +ATOM 394 CB LYS B 29 -8.578 19.774 -10.633 1.00 49.60 C +ATOM 395 N THR B 30 -7.024 18.353 -13.464 1.00 55.42 N +ATOM 396 CA THR B 30 -6.153 18.650 -14.620 1.00 57.92 C +ATOM 397 C THR B 30 -5.998 20.158 -14.819 1.00 59.90 C +ATOM 398 O THR B 30 -6.749 20.746 -15.648 1.00 61.39 O +ATOM 399 CB THR B 30 -6.580 17.859 -15.907 1.00 57.79 C +ATOM 400 OXT THR B 30 -5.129 20.719 -14.107 1.00 61.25 O +TER 401 THR B 30 +ATOM 402 N GLY C 1 -9.365 16.705 14.221 1.00 48.05 N +ATOM 403 CA GLY C 1 -8.537 16.494 13.003 1.00 47.12 C +ATOM 404 C GLY C 1 -8.727 15.016 12.649 1.00 45.76 C +ATOM 405 O GLY C 1 -9.130 14.215 13.501 1.00 47.73 O +ATOM 406 N ILE C 2 -8.404 14.687 11.431 1.00 43.43 N +ATOM 407 CA ILE C 2 -8.514 13.325 10.903 1.00 40.39 C +ATOM 408 C ILE C 2 -7.686 12.341 11.721 1.00 38.69 C +ATOM 409 O ILE C 2 -8.154 11.198 11.875 1.00 36.33 O +ATOM 410 CB ILE C 2 -8.228 13.363 9.359 1.00 38.80 C +ATOM 411 CG1 ILE C 2 -8.769 12.110 8.654 1.00 37.32 C +ATOM 412 CG2 ILE C 2 -6.697 13.609 9.081 1.00 38.76 C +ATOM 413 CD1 ILE C 2 -8.425 12.021 7.143 1.00 33.74 C +ATOM 414 N VAL C 3 -6.543 12.783 12.208 1.00 39.63 N +ATOM 415 CA VAL C 3 -5.638 11.936 13.000 1.00 40.81 C +ATOM 416 C VAL C 3 -6.293 11.512 14.317 1.00 42.54 C +ATOM 417 O VAL C 3 -6.422 10.305 14.611 1.00 42.05 O +ATOM 418 CB VAL C 3 -4.237 12.549 13.244 1.00 40.66 C +ATOM 419 CG1 VAL C 3 -3.252 11.442 13.634 1.00 40.32 C +ATOM 420 CG2 VAL C 3 -3.747 13.411 12.098 1.00 40.23 C +ATOM 421 N GLU C 4 -6.736 12.472 15.101 1.00 44.94 N +ATOM 422 CA GLU C 4 -7.392 12.172 16.388 1.00 47.85 C +ATOM 423 C GLU C 4 -8.682 11.367 16.195 1.00 46.28 C +ATOM 424 O GLU C 4 -8.962 10.413 16.960 1.00 47.27 O +ATOM 425 CB GLU C 4 -7.775 13.424 17.194 1.00 50.57 C +ATOM 426 CG GLU C 4 -6.676 14.294 17.776 1.00 54.75 C +ATOM 427 CD GLU C 4 -5.827 15.113 16.833 1.00 58.00 C +ATOM 428 OE1 GLU C 4 -6.139 16.183 16.289 1.00 59.86 O +ATOM 429 OE2 GLU C 4 -4.680 14.626 16.618 1.00 59.17 O +ATOM 430 N GLN C 5 -9.426 11.783 15.155 1.00 44.17 N +ATOM 431 CA GLN C 5 -10.718 11.134 14.881 1.00 41.76 C +ATOM 432 C GLN C 5 -10.628 9.736 14.313 1.00 41.18 C +ATOM 433 O GLN C 5 -11.288 8.865 14.929 1.00 42.62 O +ATOM 434 CB AGLN C 5 -11.754 11.969 14.131 0.50 41.69 C +ATOM 435 CB BGLN C 5 -11.615 12.093 14.105 0.50 41.52 C +ATOM 436 CG AGLN C 5 -13.035 11.299 13.699 0.50 41.36 C +ATOM 437 CG BGLN C 5 -12.845 11.542 13.430 0.50 41.04 C +ATOM 438 CD AGLN C 5 -13.898 10.513 14.642 0.50 41.05 C +ATOM 439 CD BGLN C 5 -13.716 12.537 12.714 0.50 40.88 C +ATOM 440 OE1AGLN C 5 -14.674 9.613 14.268 0.50 40.86 O +ATOM 441 OE1BGLN C 5 -13.507 12.850 11.551 0.50 40.91 O +ATOM 442 NE2AGLN C 5 -13.862 10.819 15.949 0.50 40.81 N +ATOM 443 NE2BGLN C 5 -14.513 13.279 13.477 0.50 41.32 N +ATOM 444 N CYS C 6 -9.883 9.484 13.258 1.00 38.09 N +ATOM 445 CA CYS C 6 -9.780 8.199 12.599 1.00 35.22 C +ATOM 446 C CYS C 6 -8.651 7.224 12.900 1.00 36.10 C +ATOM 447 O CYS C 6 -8.588 6.128 12.338 1.00 36.00 O +ATOM 448 CB CYS C 6 -9.755 8.556 11.098 1.00 35.06 C +ATOM 449 SG CYS C 6 -11.155 9.651 10.648 1.00 34.84 S +ATOM 450 N CYS C 7 -7.761 7.570 13.804 1.00 37.51 N +ATOM 451 CA CYS C 7 -6.653 6.663 14.144 1.00 39.72 C +ATOM 452 C CYS C 7 -7.023 5.768 15.332 1.00 43.43 C +ATOM 453 O CYS C 7 -6.616 4.584 15.453 1.00 45.63 O +ATOM 454 CB CYS C 7 -5.402 7.544 14.304 1.00 36.40 C +ATOM 455 SG CYS C 7 -4.675 8.132 12.742 1.00 34.46 S +ATOM 456 N THR C 8 -7.814 6.305 16.248 1.00 46.18 N +ATOM 457 CA THR C 8 -8.254 5.615 17.475 1.00 49.24 C +ATOM 458 C THR C 8 -9.479 4.736 17.276 1.00 49.77 C +ATOM 459 O THR C 8 -9.624 3.682 17.911 1.00 51.04 O +ATOM 460 CB THR C 8 -8.556 6.608 18.678 1.00 49.88 C +ATOM 461 OG1 THR C 8 -9.323 7.756 18.196 1.00 51.29 O +ATOM 462 CG2 THR C 8 -7.245 7.044 19.346 1.00 50.83 C +ATOM 463 N SER C 9 -10.324 5.269 16.419 1.00 49.19 N +ATOM 464 CA SER C 9 -11.601 4.650 16.041 1.00 49.59 C +ATOM 465 C SER C 9 -11.590 4.671 14.509 1.00 48.30 C +ATOM 466 O SER C 9 -10.864 5.552 14.003 1.00 50.10 O +ATOM 467 CB SER C 9 -12.797 5.410 16.619 1.00 50.52 C +ATOM 468 OG SER C 9 -12.751 6.831 16.457 1.00 51.64 O +ATOM 469 N ILE C 10 -12.353 3.812 13.894 1.00 45.95 N +ATOM 470 CA ILE C 10 -12.357 3.879 12.405 1.00 44.14 C +ATOM 471 C ILE C 10 -13.492 4.826 11.999 1.00 41.67 C +ATOM 472 O ILE C 10 -14.558 4.967 12.649 1.00 40.29 O +ATOM 473 CB ILE C 10 -12.305 2.434 11.822 1.00 45.36 C +ATOM 474 CG1 ILE C 10 -13.640 1.977 11.215 1.00 46.10 C +ATOM 475 CG2 ILE C 10 -11.826 1.439 12.934 1.00 46.19 C +ATOM 476 CD1 ILE C 10 -13.625 0.781 10.239 1.00 46.03 C +ATOM 477 N CYS C 11 -13.237 5.512 10.913 1.00 37.28 N +ATOM 478 CA CYS C 11 -14.134 6.452 10.290 1.00 34.85 C +ATOM 479 C CYS C 11 -14.601 5.733 9.030 1.00 36.11 C +ATOM 480 O CYS C 11 -13.738 5.032 8.476 1.00 37.21 O +ATOM 481 CB CYS C 11 -13.416 7.708 9.857 1.00 34.23 C +ATOM 482 SG CYS C 11 -12.773 8.576 11.244 1.00 35.67 S +ATOM 483 N SER C 12 -15.813 5.944 8.597 1.00 33.91 N +ATOM 484 CA SER C 12 -16.490 5.439 7.423 1.00 30.40 C +ATOM 485 C SER C 12 -16.119 6.400 6.321 1.00 28.90 C +ATOM 486 O SER C 12 -15.580 7.472 6.584 1.00 28.25 O +ATOM 487 CB SER C 12 -18.032 5.532 7.596 1.00 30.88 C +ATOM 488 OG SER C 12 -18.444 6.898 7.913 1.00 28.11 O +ATOM 489 N LEU C 13 -16.442 6.091 5.103 1.00 28.24 N +ATOM 490 CA LEU C 13 -16.272 6.845 3.891 1.00 28.56 C +ATOM 491 C LEU C 13 -17.109 8.122 4.113 1.00 26.44 C +ATOM 492 O LEU C 13 -16.626 9.139 3.591 1.00 26.91 O +ATOM 493 CB LEU C 13 -16.794 6.023 2.752 1.00 32.01 C +ATOM 494 CG LEU C 13 -16.882 6.429 1.325 1.00 35.34 C +ATOM 495 CD1 LEU C 13 -17.174 5.141 0.510 1.00 36.01 C +ATOM 496 CD2 LEU C 13 -18.046 7.372 0.980 1.00 36.97 C +ATOM 497 N TYR C 14 -18.254 7.998 4.769 1.00 24.69 N +ATOM 498 CA TYR C 14 -19.056 9.218 5.028 1.00 25.17 C +ATOM 499 C TYR C 14 -18.314 10.220 5.883 1.00 25.52 C +ATOM 500 O TYR C 14 -18.402 11.442 5.631 1.00 28.09 O +ATOM 501 CB TYR C 14 -20.343 8.831 5.858 1.00 25.09 C +ATOM 502 CG TYR C 14 -21.009 7.871 4.857 1.00 29.69 C +ATOM 503 CD1 TYR C 14 -21.590 8.372 3.719 1.00 31.37 C +ATOM 504 CD2 TYR C 14 -20.851 6.502 5.014 1.00 30.13 C +ATOM 505 CE1 TYR C 14 -22.131 7.520 2.755 1.00 33.69 C +ATOM 506 CE2 TYR C 14 -21.410 5.652 4.073 1.00 32.23 C +ATOM 507 CZ TYR C 14 -22.015 6.155 2.938 1.00 33.86 C +ATOM 508 OH TYR C 14 -22.556 5.298 2.002 1.00 37.35 O +ATOM 509 N GLN C 15 -17.670 9.815 6.963 1.00 23.49 N +ATOM 510 CA GLN C 15 -16.860 10.635 7.873 1.00 26.64 C +ATOM 511 C GLN C 15 -15.573 11.153 7.221 1.00 27.05 C +ATOM 512 O GLN C 15 -15.257 12.285 7.408 1.00 26.37 O +ATOM 513 CB GLN C 15 -16.513 10.021 9.216 1.00 27.05 C +ATOM 514 CG GLN C 15 -17.661 9.634 10.137 1.00 29.58 C +ATOM 515 CD GLN C 15 -17.243 8.780 11.295 1.00 33.16 C +ATOM 516 OE1 GLN C 15 -16.836 7.635 11.116 1.00 34.28 O +ATOM 517 NE2 GLN C 15 -17.264 9.225 12.563 1.00 34.31 N +ATOM 518 N LEU C 16 -14.932 10.316 6.380 1.00 25.64 N +ATOM 519 CA LEU C 16 -13.714 10.753 5.645 1.00 24.21 C +ATOM 520 C LEU C 16 -14.061 11.778 4.586 1.00 23.20 C +ATOM 521 O LEU C 16 -13.223 12.627 4.294 1.00 25.69 O +ATOM 522 CB LEU C 16 -13.081 9.514 5.051 1.00 24.77 C +ATOM 523 CG LEU C 16 -12.453 8.600 6.120 1.00 27.15 C +ATOM 524 CD1 LEU C 16 -12.238 7.308 5.323 1.00 27.45 C +ATOM 525 CD2 LEU C 16 -11.132 9.171 6.650 1.00 28.21 C +ATOM 526 N GLU C 17 -15.252 11.762 3.954 1.00 23.86 N +ATOM 527 CA GLU C 17 -15.547 12.798 2.972 1.00 26.76 C +ATOM 528 C GLU C 17 -15.529 14.214 3.576 1.00 25.04 C +ATOM 529 O GLU C 17 -15.374 15.148 2.785 1.00 28.07 O +ATOM 530 CB GLU C 17 -16.993 12.712 2.496 1.00 28.90 C +ATOM 531 CG GLU C 17 -17.065 11.946 1.169 1.00 33.96 C +ATOM 532 CD GLU C 17 -18.478 12.130 0.582 1.00 36.93 C +ATOM 533 OE1 GLU C 17 -19.465 12.151 1.290 1.00 37.18 O +ATOM 534 OE2 GLU C 17 -18.451 12.236 -0.666 1.00 39.72 O +ATOM 535 N ASN C 18 -15.672 14.369 4.847 1.00 26.64 N +ATOM 536 CA ASN C 18 -15.632 15.620 5.582 1.00 29.42 C +ATOM 537 C ASN C 18 -14.295 16.311 5.325 1.00 31.01 C +ATOM 538 O ASN C 18 -14.134 17.547 5.373 1.00 32.27 O +ATOM 539 CB ASN C 18 -15.813 15.374 7.111 1.00 27.66 C +ATOM 540 CG ASN C 18 -17.278 15.014 7.380 1.00 29.91 C +ATOM 541 OD1 ASN C 18 -18.152 15.301 6.527 1.00 33.22 O +ATOM 542 ND2 ASN C 18 -17.556 14.247 8.426 1.00 31.90 N +ATOM 543 N TYR C 19 -13.260 15.524 4.993 1.00 30.88 N +ATOM 544 CA TYR C 19 -11.901 16.095 4.776 1.00 30.41 C +ATOM 545 C TYR C 19 -11.508 16.392 3.367 1.00 29.96 C +ATOM 546 O TYR C 19 -10.341 16.804 3.150 1.00 29.02 O +ATOM 547 CB TYR C 19 -10.855 15.192 5.471 1.00 31.85 C +ATOM 548 CG TYR C 19 -11.131 15.018 6.932 1.00 32.47 C +ATOM 549 CD1 TYR C 19 -10.698 15.973 7.867 1.00 34.20 C +ATOM 550 CD2 TYR C 19 -11.841 13.926 7.404 1.00 32.83 C +ATOM 551 CE1 TYR C 19 -10.958 15.822 9.220 1.00 34.07 C +ATOM 552 CE2 TYR C 19 -12.127 13.791 8.757 1.00 34.13 C +ATOM 553 CZ TYR C 19 -11.670 14.719 9.670 1.00 34.63 C +ATOM 554 OH TYR C 19 -11.919 14.539 11.020 1.00 36.47 O +ATOM 555 N CYS C 20 -12.348 16.121 2.395 1.00 27.52 N +ATOM 556 CA CYS C 20 -12.015 16.356 0.991 1.00 29.22 C +ATOM 557 C CYS C 20 -12.143 17.869 0.697 1.00 31.84 C +ATOM 558 O CYS C 20 -12.726 18.485 1.573 1.00 34.93 O +ATOM 559 CB CYS C 20 -12.922 15.613 0.018 1.00 28.97 C +ATOM 560 SG CYS C 20 -13.061 13.803 0.314 1.00 28.10 S +ATOM 561 N ASN C 21 -11.649 18.308 -0.403 1.00 30.66 N +ATOM 562 CA ASN C 21 -11.748 19.685 -0.861 1.00 35.49 C +ATOM 563 C ASN C 21 -13.233 19.941 -1.212 1.00 37.91 C +ATOM 564 O ASN C 21 -13.681 20.996 -0.664 1.00 39.64 O +ATOM 565 CB ASN C 21 -10.866 20.028 -2.033 1.00 37.03 C +ATOM 566 CG ASN C 21 -9.449 20.376 -1.554 1.00 38.78 C +ATOM 567 OD1 ASN C 21 -8.536 20.199 -2.376 1.00 41.30 O +ATOM 568 ND2 ASN C 21 -9.420 20.845 -0.311 1.00 40.88 N +ATOM 569 OXT ASN C 21 -13.787 19.134 -1.951 1.00 38.72 O +TER 570 ASN C 21 +ATOM 571 N PHE D 1 1.402 3.527 22.701 1.00 60.05 N +ATOM 572 CA PHE D 1 0.867 4.724 22.018 1.00 59.81 C +ATOM 573 C PHE D 1 0.181 4.398 20.677 1.00 58.98 C +ATOM 574 O PHE D 1 0.216 3.259 20.155 1.00 60.06 O +ATOM 575 CB PHE D 1 1.970 5.770 21.790 1.00 60.37 C +ATOM 576 CG PHE D 1 2.904 5.678 20.628 1.00 60.47 C +ATOM 577 CD1 PHE D 1 3.630 4.509 20.378 1.00 61.03 C +ATOM 578 CD2 PHE D 1 3.095 6.767 19.785 1.00 60.48 C +ATOM 579 CE1 PHE D 1 4.500 4.437 19.293 1.00 61.56 C +ATOM 580 CE2 PHE D 1 3.975 6.715 18.693 1.00 60.67 C +ATOM 581 CZ PHE D 1 4.685 5.537 18.443 1.00 60.89 C +ATOM 582 N VAL D 2 -0.428 5.460 20.164 1.00 56.04 N +ATOM 583 CA VAL D 2 -1.074 5.422 18.837 1.00 53.98 C +ATOM 584 C VAL D 2 0.075 5.898 17.919 1.00 51.36 C +ATOM 585 O VAL D 2 0.678 6.947 18.269 1.00 51.49 O +ATOM 586 CB VAL D 2 -2.342 6.273 18.742 1.00 54.54 C +ATOM 587 N ASN D 3 0.378 5.145 16.869 1.00 46.87 N +ATOM 588 CA ASN D 3 1.481 5.566 15.957 1.00 41.86 C +ATOM 589 C ASN D 3 0.799 6.321 14.808 1.00 38.39 C +ATOM 590 O ASN D 3 0.333 5.717 13.841 1.00 37.12 O +ATOM 591 CB ASN D 3 2.379 4.439 15.483 1.00 43.27 C +ATOM 592 CG ASN D 3 3.588 4.993 14.744 1.00 44.38 C +ATOM 593 OD1 ASN D 3 3.422 5.817 13.825 1.00 44.70 O +ATOM 594 ND2 ASN D 3 4.809 4.652 15.142 1.00 45.68 N +ATOM 595 N GLN D 4 0.752 7.626 14.981 1.00 35.76 N +ATOM 596 CA GLN D 4 0.099 8.511 14.028 1.00 33.92 C +ATOM 597 C GLN D 4 0.784 8.545 12.677 1.00 30.54 C +ATOM 598 O GLN D 4 0.130 8.930 11.720 1.00 27.67 O +ATOM 599 CB GLN D 4 -0.007 9.924 14.615 1.00 37.48 C +ATOM 600 CG GLN D 4 -0.706 10.009 15.968 1.00 41.31 C +ATOM 601 CD GLN D 4 -1.191 11.427 16.290 1.00 43.22 C +ATOM 602 OE1 GLN D 4 -0.633 12.468 15.887 1.00 42.37 O +ATOM 603 NE2 GLN D 4 -2.315 11.405 17.034 1.00 44.37 N +ATOM 604 N HIS D 5 2.069 8.229 12.632 1.00 26.15 N +ATOM 605 CA HIS D 5 2.801 8.256 11.358 1.00 27.99 C +ATOM 606 C HIS D 5 2.302 7.075 10.545 1.00 23.21 C +ATOM 607 O HIS D 5 2.171 7.086 9.294 1.00 22.91 O +ATOM 608 CB HIS D 5 4.323 8.222 11.629 1.00 29.76 C +ATOM 609 CG HIS D 5 5.105 8.247 10.391 1.00 31.58 C +ATOM 610 ND1 HIS D 5 4.979 9.324 9.532 1.00 34.35 N +ATOM 611 CD2 HIS D 5 5.927 7.349 9.777 1.00 33.56 C +ATOM 612 CE1 HIS D 5 5.717 9.106 8.442 1.00 33.26 C +ATOM 613 NE2 HIS D 5 6.294 7.937 8.567 1.00 33.63 N +ATOM 614 N LEU D 6 2.172 5.908 11.159 1.00 22.13 N +ATOM 615 CA LEU D 6 1.712 4.705 10.387 1.00 23.19 C +ATOM 616 C LEU D 6 0.245 4.962 10.147 1.00 23.22 C +ATOM 617 O LEU D 6 -0.195 4.634 9.053 1.00 21.55 O +ATOM 618 CB LEU D 6 2.053 3.378 11.042 1.00 25.00 C +ATOM 619 CG LEU D 6 3.476 3.050 11.567 1.00 26.11 C +ATOM 620 CD1 LEU D 6 3.409 1.617 12.128 1.00 27.67 C +ATOM 621 CD2 LEU D 6 4.470 3.093 10.446 1.00 27.00 C +ATOM 622 N CYS D 7 -0.531 5.525 11.087 1.00 22.62 N +ATOM 623 CA CYS D 7 -1.931 5.775 10.727 1.00 24.01 C +ATOM 624 C CYS D 7 -2.069 6.772 9.580 1.00 23.02 C +ATOM 625 O CYS D 7 -2.915 6.387 8.775 1.00 23.11 O +ATOM 626 CB CYS D 7 -2.581 6.261 12.039 1.00 25.02 C +ATOM 627 SG CYS D 7 -4.352 6.421 11.650 1.00 30.15 S +ATOM 628 N GLY D 8 -1.276 7.817 9.440 1.00 22.02 N +ATOM 629 CA GLY D 8 -1.366 8.789 8.373 1.00 20.82 C +ATOM 630 C GLY D 8 -1.214 8.104 7.013 1.00 19.97 C +ATOM 631 O GLY D 8 -2.018 8.565 6.234 1.00 18.48 O +ATOM 632 N SER D 9 -0.329 7.157 6.960 1.00 21.40 N +ATOM 633 CA SER D 9 -0.108 6.438 5.727 1.00 20.79 C +ATOM 634 C SER D 9 -1.456 5.857 5.177 1.00 20.98 C +ATOM 635 O SER D 9 -1.779 6.040 3.997 1.00 24.12 O +ATOM 636 CB ASER D 9 0.866 5.251 5.855 0.50 21.27 C +ATOM 637 CB BSER D 9 0.859 5.246 5.833 0.50 23.78 C +ATOM 638 OG ASER D 9 0.987 4.805 4.521 0.50 19.27 O +ATOM 639 OG BSER D 9 2.160 5.760 5.626 0.50 27.33 O +ATOM 640 N HIS D 10 -2.154 5.180 6.082 1.00 20.59 N +ATOM 641 CA HIS D 10 -3.430 4.608 5.743 1.00 20.09 C +ATOM 642 C HIS D 10 -4.445 5.717 5.430 1.00 19.84 C +ATOM 643 O HIS D 10 -5.140 5.594 4.407 1.00 19.64 O +ATOM 644 CB HIS D 10 -3.850 3.778 7.038 1.00 18.89 C +ATOM 645 CG HIS D 10 -3.026 2.515 6.931 1.00 22.69 C +ATOM 646 ND1 HIS D 10 -3.344 1.475 6.097 1.00 24.89 N +ATOM 647 CD2 HIS D 10 -1.987 2.143 7.667 1.00 23.18 C +ATOM 648 CE1 HIS D 10 -2.447 0.531 6.290 1.00 22.91 C +ATOM 649 NE2 HIS D 10 -1.571 0.895 7.255 1.00 25.08 N +ATOM 650 N LEU D 11 -4.587 6.798 6.146 1.00 20.00 N +ATOM 651 CA LEU D 11 -5.552 7.905 5.957 1.00 18.91 C +ATOM 652 C LEU D 11 -5.408 8.574 4.623 1.00 16.41 C +ATOM 653 O LEU D 11 -6.382 8.761 3.893 1.00 20.47 O +ATOM 654 CB LEU D 11 -5.419 8.791 7.179 1.00 20.79 C +ATOM 655 CG LEU D 11 -5.615 8.262 8.543 1.00 24.78 C +ATOM 656 CD1 LEU D 11 -5.499 9.304 9.647 1.00 27.65 C +ATOM 657 CD2 LEU D 11 -6.944 7.534 8.673 1.00 27.24 C +ATOM 658 N VAL D 12 -4.152 8.789 4.206 1.00 15.61 N +ATOM 659 CA VAL D 12 -3.959 9.479 2.926 1.00 16.05 C +ATOM 660 C VAL D 12 -4.468 8.645 1.765 1.00 19.91 C +ATOM 661 O VAL D 12 -5.027 9.041 0.717 1.00 20.13 O +ATOM 662 CB VAL D 12 -2.503 10.072 2.783 1.00 17.31 C +ATOM 663 CG1 VAL D 12 -2.319 11.049 3.937 1.00 22.35 C +ATOM 664 CG2 VAL D 12 -1.517 8.915 2.741 1.00 19.79 C +ATOM 665 N GLU D 13 -4.163 7.308 1.869 1.00 21.11 N +ATOM 666 CA GLU D 13 -4.634 6.409 0.840 1.00 24.01 C +ATOM 667 C GLU D 13 -6.160 6.350 0.836 1.00 21.42 C +ATOM 668 O GLU D 13 -6.746 6.331 -0.297 1.00 22.60 O +ATOM 669 CB GLU D 13 -4.215 4.938 1.107 1.00 26.18 C +ATOM 670 CG GLU D 13 -2.711 4.694 1.148 1.00 35.97 C +ATOM 671 CD GLU D 13 -2.132 3.381 0.700 1.00 40.09 C +ATOM 672 OE1 GLU D 13 -2.758 2.307 0.674 1.00 43.42 O +ATOM 673 OE2 GLU D 13 -0.936 3.541 0.320 1.00 41.16 O +ATOM 674 N ALA D 14 -6.799 6.451 1.945 1.00 19.82 N +ATOM 675 CA ALA D 14 -8.272 6.352 2.069 1.00 20.29 C +ATOM 676 C ALA D 14 -8.817 7.664 1.482 1.00 21.36 C +ATOM 677 O ALA D 14 -9.771 7.647 0.702 1.00 22.81 O +ATOM 678 CB ALA D 14 -8.722 6.111 3.492 1.00 21.36 C +ATOM 679 N LEU D 15 -8.192 8.789 1.786 1.00 23.36 N +ATOM 680 CA LEU D 15 -8.674 10.087 1.158 1.00 21.08 C +ATOM 681 C LEU D 15 -8.482 10.056 -0.317 1.00 20.43 C +ATOM 682 O LEU D 15 -9.306 10.609 -1.039 1.00 22.49 O +ATOM 683 CB LEU D 15 -8.014 11.271 1.910 1.00 21.59 C +ATOM 684 CG LEU D 15 -8.446 11.504 3.324 1.00 22.92 C +ATOM 685 CD1 LEU D 15 -7.714 12.644 4.014 1.00 24.30 C +ATOM 686 CD2 LEU D 15 -9.965 11.778 3.315 1.00 23.92 C +ATOM 687 N TYR D 16 -7.396 9.393 -0.803 1.00 18.72 N +ATOM 688 CA TYR D 16 -7.315 9.338 -2.305 1.00 19.01 C +ATOM 689 C TYR D 16 -8.521 8.683 -3.023 1.00 17.71 C +ATOM 690 O TYR D 16 -9.097 9.234 -4.056 1.00 19.60 O +ATOM 691 CB TYR D 16 -5.926 8.663 -2.534 1.00 21.07 C +ATOM 692 CG TYR D 16 -5.935 8.206 -3.981 1.00 18.71 C +ATOM 693 CD1 TYR D 16 -5.566 9.084 -5.013 1.00 21.64 C +ATOM 694 CD2 TYR D 16 -6.334 6.881 -4.372 1.00 23.45 C +ATOM 695 CE1 TYR D 16 -5.578 8.696 -6.327 1.00 25.64 C +ATOM 696 CE2 TYR D 16 -6.382 6.539 -5.711 1.00 25.65 C +ATOM 697 CZ TYR D 16 -6.014 7.432 -6.685 1.00 24.87 C +ATOM 698 OH TYR D 16 -6.012 7.206 -8.065 1.00 27.15 O +ATOM 699 N LEU D 17 -8.975 7.575 -2.449 1.00 20.09 N +ATOM 700 CA LEU D 17 -10.143 6.809 -2.908 1.00 19.75 C +ATOM 701 C LEU D 17 -11.485 7.598 -2.733 1.00 21.11 C +ATOM 702 O LEU D 17 -12.289 7.726 -3.667 1.00 26.91 O +ATOM 703 CB LEU D 17 -10.284 5.403 -2.233 1.00 19.51 C +ATOM 704 CG LEU D 17 -8.987 4.617 -2.630 1.00 20.24 C +ATOM 705 CD1 LEU D 17 -8.871 3.464 -1.597 1.00 23.75 C +ATOM 706 CD2 LEU D 17 -9.193 4.136 -4.008 1.00 21.34 C +ATOM 707 N VAL D 18 -11.643 8.132 -1.561 1.00 21.50 N +ATOM 708 CA VAL D 18 -12.937 8.805 -1.248 1.00 21.81 C +ATOM 709 C VAL D 18 -13.094 10.176 -1.913 1.00 22.24 C +ATOM 710 O VAL D 18 -14.185 10.528 -2.383 1.00 23.07 O +ATOM 711 CB VAL D 18 -13.076 8.915 0.249 1.00 21.79 C +ATOM 712 CG1 VAL D 18 -14.287 9.836 0.532 1.00 24.82 C +ATOM 713 CG2 VAL D 18 -13.039 7.657 1.041 1.00 22.23 C +ATOM 714 N CYS D 19 -12.028 10.946 -1.867 1.00 21.62 N +ATOM 715 CA CYS D 19 -12.067 12.287 -2.497 1.00 22.66 C +ATOM 716 C CYS D 19 -11.982 12.183 -3.985 1.00 24.34 C +ATOM 717 O CYS D 19 -12.596 13.098 -4.631 1.00 27.01 O +ATOM 718 CB CYS D 19 -10.987 13.142 -1.797 1.00 18.91 C +ATOM 719 SG CYS D 19 -11.072 13.276 -0.062 1.00 23.47 S +ATOM 720 N GLY D 20 -11.304 11.271 -4.657 1.00 25.93 N +ATOM 721 CA GLY D 20 -11.277 11.238 -6.117 1.00 28.33 C +ATOM 722 C GLY D 20 -10.780 12.609 -6.604 1.00 30.52 C +ATOM 723 O GLY D 20 -9.787 13.179 -6.172 1.00 30.04 O +ATOM 724 N GLU D 21 -11.555 13.064 -7.567 1.00 32.59 N +ATOM 725 CA GLU D 21 -11.391 14.323 -8.258 1.00 34.23 C +ATOM 726 C GLU D 21 -11.279 15.618 -7.496 1.00 34.07 C +ATOM 727 O GLU D 21 -10.642 16.586 -7.974 1.00 37.11 O +ATOM 728 CB GLU D 21 -12.698 14.518 -9.099 1.00 36.40 C +ATOM 729 CG GLU D 21 -12.359 14.102 -10.546 1.00 37.33 C +ATOM 730 N ARG D 22 -11.969 15.637 -6.381 1.00 34.22 N +ATOM 731 CA ARG D 22 -11.996 16.839 -5.531 1.00 33.77 C +ATOM 732 C ARG D 22 -10.655 17.077 -4.876 1.00 31.97 C +ATOM 733 O ARG D 22 -10.278 18.230 -4.468 1.00 33.12 O +ATOM 734 CB ARG D 22 -13.131 16.761 -4.514 1.00 36.14 C +ATOM 735 CG ARG D 22 -14.520 16.394 -4.972 1.00 39.63 C +ATOM 736 CD ARG D 22 -15.337 15.658 -3.954 1.00 42.30 C +ATOM 737 NE ARG D 22 -15.606 16.336 -2.712 1.00 44.41 N +ATOM 738 CZ ARG D 22 -16.231 16.042 -1.573 1.00 45.62 C +ATOM 739 NH1 ARG D 22 -16.889 14.931 -1.221 1.00 45.34 N +ATOM 740 NH2 ARG D 22 -16.157 17.010 -0.626 1.00 45.56 N +ATOM 741 N GLY D 23 -9.830 16.031 -4.736 1.00 28.92 N +ATOM 742 CA GLY D 23 -8.548 16.224 -4.076 1.00 27.29 C +ATOM 743 C GLY D 23 -8.809 16.503 -2.596 1.00 24.38 C +ATOM 744 O GLY D 23 -9.902 16.468 -2.049 1.00 27.21 O +ATOM 745 N PHE D 24 -7.728 16.764 -1.904 1.00 25.20 N +ATOM 746 CA PHE D 24 -7.723 17.052 -0.491 1.00 24.11 C +ATOM 747 C PHE D 24 -6.426 17.729 -0.060 1.00 26.17 C +ATOM 748 O PHE D 24 -5.387 17.689 -0.686 1.00 26.26 O +ATOM 749 CB PHE D 24 -7.893 15.768 0.333 1.00 24.46 C +ATOM 750 CG PHE D 24 -6.880 14.672 0.021 1.00 21.90 C +ATOM 751 CD1 PHE D 24 -6.872 13.810 -1.025 1.00 21.94 C +ATOM 752 CD2 PHE D 24 -5.916 14.600 1.010 1.00 22.32 C +ATOM 753 CE1 PHE D 24 -5.863 12.816 -1.159 1.00 24.32 C +ATOM 754 CE2 PHE D 24 -4.878 13.655 0.888 1.00 24.77 C +ATOM 755 CZ PHE D 24 -4.858 12.753 -0.184 1.00 22.56 C +ATOM 756 N PHE D 25 -6.482 18.272 1.138 1.00 27.33 N +ATOM 757 CA PHE D 25 -5.351 18.883 1.797 1.00 29.20 C +ATOM 758 C PHE D 25 -5.045 17.955 2.955 1.00 29.45 C +ATOM 759 O PHE D 25 -5.975 17.586 3.694 1.00 31.05 O +ATOM 760 CB PHE D 25 -5.547 20.332 2.191 1.00 30.33 C +ATOM 761 CG PHE D 25 -5.389 21.318 1.071 1.00 32.71 C +ATOM 762 CD1 PHE D 25 -6.297 21.388 0.033 1.00 34.39 C +ATOM 763 CD2 PHE D 25 -4.265 22.155 1.074 1.00 33.17 C +ATOM 764 CE1 PHE D 25 -6.222 22.305 -1.012 1.00 33.82 C +ATOM 765 CE2 PHE D 25 -4.122 23.096 0.034 1.00 34.72 C +ATOM 766 CZ PHE D 25 -5.106 23.147 -0.973 1.00 34.60 C +ATOM 767 N TYR D 26 -3.786 17.615 3.174 1.00 28.09 N +ATOM 768 CA TYR D 26 -3.471 16.778 4.327 1.00 32.49 C +ATOM 769 C TYR D 26 -2.472 17.555 5.165 1.00 37.73 C +ATOM 770 O TYR D 26 -1.376 17.826 4.665 1.00 37.93 O +ATOM 771 CB TYR D 26 -2.875 15.400 3.932 1.00 32.60 C +ATOM 772 CG TYR D 26 -2.569 14.607 5.186 1.00 31.40 C +ATOM 773 CD1 TYR D 26 -3.640 13.927 5.792 1.00 32.46 C +ATOM 774 CD2 TYR D 26 -1.278 14.507 5.724 1.00 30.37 C +ATOM 775 CE1 TYR D 26 -3.518 13.188 6.967 1.00 31.56 C +ATOM 776 CE2 TYR D 26 -1.128 13.768 6.906 1.00 32.49 C +ATOM 777 CZ TYR D 26 -2.211 13.137 7.504 1.00 33.18 C +ATOM 778 OH TYR D 26 -2.028 12.438 8.652 1.00 34.99 O +ATOM 779 N THR D 27 -2.859 17.900 6.361 1.00 43.31 N +ATOM 780 CA THR D 27 -1.975 18.642 7.278 1.00 49.95 C +ATOM 781 C THR D 27 -1.892 17.758 8.521 1.00 53.53 C +ATOM 782 O THR D 27 -2.899 17.610 9.245 1.00 55.62 O +ATOM 783 CB THR D 27 -2.351 20.119 7.644 1.00 50.43 C +ATOM 784 OG1 THR D 27 -3.779 20.155 7.959 1.00 52.22 O +ATOM 785 CG2 THR D 27 -1.970 21.167 6.588 1.00 50.98 C +ATOM 786 N PRO D 28 -0.720 17.172 8.688 1.00 56.35 N +ATOM 787 CA PRO D 28 -0.487 16.273 9.821 1.00 58.54 C +ATOM 788 C PRO D 28 -0.372 17.015 11.142 1.00 61.21 C +ATOM 789 O PRO D 28 0.499 16.679 11.957 1.00 62.21 O +ATOM 790 CB PRO D 28 0.760 15.501 9.421 1.00 58.40 C +ATOM 791 CG PRO D 28 1.488 16.384 8.457 1.00 57.60 C +ATOM 792 CD PRO D 28 0.454 17.300 7.825 1.00 56.87 C +ATOM 793 N LYS D 29 -1.216 18.000 11.346 1.00 63.09 N +ATOM 794 CA LYS D 29 -1.294 18.826 12.548 1.00 65.63 C +ATOM 795 C LYS D 29 -2.453 19.804 12.362 1.00 67.73 C +ATOM 796 O LYS D 29 -3.605 19.363 12.604 1.00 59.41 O +ATOM 797 CB LYS D 29 -0.026 19.517 13.006 1.00 61.56 C +ATOM 798 N THR D 30 -2.209 21.030 11.951 1.00 61.17 N +ATOM 799 CA THR D 30 -3.328 21.983 11.778 1.00 61.51 C +ATOM 800 C THR D 30 -2.986 23.175 10.887 1.00 64.39 C +ATOM 801 O THR D 30 -3.919 23.640 10.176 1.00 64.82 O +TER 802 THR D 30 +HETATM 803 ZN ZN B 1 0.000 0.000 -7.816 0.33 23.74 ZN +HETATM 804 CL CL B 2 0.000 0.000 -10.129 0.17 44.54 CL +HETATM 805 ZN ZN D 1 0.000 0.000 8.212 0.33 21.78 ZN +HETATM 806 CL CL D 2 0.000 0.000 10.530 0.17 19.75 CL +HETATM 807 NA NA 1 -1.439 -0.291 1.014 0.33 34.25 NA +HETATM 808 O HOH 1 -0.862 1.418 -9.742 0.50 24.60 O +HETATM 809 O HOH 2 0.000 0.000 10.251 0.17 28.81 O +HETATM 810 O HOH 3 0.000 0.000 12.988 0.17 34.51 O +HETATM 811 O HOH 4 0.000 0.000 -2.030 0.33 36.75 O +HETATM 812 O HOH 5 -1.067 2.419 -2.970 1.00 46.64 O +HETATM 813 O HOH 6 0.000 0.000 -15.699 0.33 46.35 O +HETATM 814 O HOH 7 2.865 14.636 4.921 1.00 27.49 O +HETATM 815 O HOH 8 8.670 11.649 6.658 1.00 44.06 O +HETATM 816 O HOH 9 5.678 11.861 7.156 1.00 38.82 O +HETATM 817 O HOH 10 9.143 8.646 7.299 1.00 38.09 O +HETATM 818 O HOH 11 16.535 14.299 8.975 1.00 36.40 O +HETATM 819 O HOH 12 0.282 12.105 9.803 1.00 45.17 O +HETATM 820 O HOH 13 23.446 2.671 10.339 1.00 66.36 O +HETATM 821 O HOH 14 14.062 16.535 10.242 1.00 53.67 O +HETATM 822 O HOH 15 21.023 1.363 12.189 1.00 59.85 O +HETATM 823 O HOH 16 7.997 20.716 11.372 1.00 69.25 O +HETATM 824 O HOH 17 19.829 10.252 13.635 1.00 45.22 O +HETATM 825 O HOH 18 11.347 18.973 11.785 0.50 46.97 O +HETATM 826 O HOH 19 25.424 2.321 12.972 0.50 49.01 O +HETATM 827 O HOH 20 22.175 4.503 14.148 0.50 50.42 O +HETATM 828 O HOH 21 -1.177 18.449 17.984 1.00 65.92 O +HETATM 829 O HOH 22 21.508 3.912 20.082 0.50 44.31 O +HETATM 830 O HOH 23 16.783 5.188 -17.127 1.00 60.87 O +HETATM 831 O HOH 24 13.508 3.599 -15.634 1.00 70.91 O +HETATM 832 O HOH 25 -4.082 12.022 -17.124 1.00 66.02 O +HETATM 833 O HOH 26 13.228 16.191 -15.572 1.00 61.35 O +HETATM 834 O HOH 27 7.971 0.146 -16.256 1.00 67.05 O +HETATM 835 O HOH 28 10.934 5.753 -17.216 0.50 62.03 O +HETATM 836 O HOH 29 13.156 12.720 -12.412 1.00 66.62 O +HETATM 837 O HOH 30 16.765 6.375 -14.171 1.00 65.74 O +HETATM 838 O HOH 31 -3.394 6.029 -12.759 0.50 39.62 O +HETATM 839 O HOH 32 1.716 21.870 -11.523 1.00 51.36 O +HETATM 840 O HOH 33 -2.876 17.218 -11.454 1.00 38.38 O +HETATM 841 O HOH 34 7.427 10.973 -11.055 1.00 31.51 O +HETATM 842 O HOH 35 5.534 23.537 15.354 1.00 61.44 O +HETATM 843 O HOH 36 -2.367 10.325 -9.627 1.00 40.81 O +HETATM 844 O HOH 37 3.139 4.728 -9.961 1.00 28.81 O +HETATM 845 O HOH 38 -1.646 19.800 -8.522 1.00 38.21 O +HETATM 846 O HOH 39 -0.912 22.705 -8.224 1.00 34.90 O +HETATM 847 O HOH 79 -6.653 1.200 6.756 1.00 44.71 O +HETATM 848 O HOH 40 -1.370 18.394 -5.969 1.00 29.73 O +HETATM 849 O HOH 41 18.071 7.216 -5.541 1.00 38.33 O +HETATM 850 O HOH 42 19.317 7.411 -2.045 1.00 46.90 O +HETATM 851 O HOH 43 16.434 10.051 -2.396 1.00 49.66 O +HETATM 852 O HOH 44 4.885 1.390 -7.117 1.00 36.79 O +HETATM 853 O HOH 45 5.533 3.724 -7.399 1.00 27.64 O +HETATM 854 O HOH 46 -7.178 4.073 10.343 0.50 48.21 O +HETATM 855 O HOH 47 7.022 23.733 1.580 1.00 55.44 O +HETATM 856 O HOH 48 11.353 13.526 4.887 1.00 52.06 O +HETATM 857 O HOH 49 5.666 20.045 12.260 1.00 57.30 O +HETATM 858 O HOH 50 3.507 13.937 10.928 1.00 46.62 O +HETATM 859 O HOH 51 -4.317 1.722 3.330 1.00 33.44 O +HETATM 860 O HOH 52 -7.443 4.404 -14.648 1.00 40.92 O +HETATM 861 O HOH 53 3.715 12.237 9.553 1.00 65.28 O +HETATM 862 O HOH 54 -10.547 19.862 11.481 1.00 67.52 O +HETATM 863 O HOH 55 -10.588 5.259 10.256 1.00 40.38 O +HETATM 864 O HOH 56 2.452 9.054 17.477 1.00 44.33 O +HETATM 865 O HOH 57 -8.951 18.934 2.333 1.00 32.50 O +HETATM 866 O HOH 58 -10.533 21.681 2.847 1.00 51.04 O +HETATM 867 O HOH 59 8.836 10.738 -19.407 1.00 55.01 O +HETATM 868 O HOH 60 6.259 10.987 -21.241 1.00 65.08 O +HETATM 869 O HOH 61 11.745 15.702 6.582 1.00 63.45 O +HETATM 870 O HOH 62 -8.127 12.343 -3.976 1.00 42.85 O +HETATM 871 O HOH 63 3.932 2.105 -9.765 1.00 58.52 O +HETATM 872 O HOH 64 1.266 13.020 13.438 1.00 62.35 O +HETATM 873 O HOH 65 -10.082 5.043 7.607 0.50 31.03 O +HETATM 874 O HOH 66 14.360 5.814 -17.082 0.50 41.45 O +HETATM 875 O HOH 67 15.260 13.330 -7.480 1.00 48.76 O +HETATM 876 O HOH 68 6.667 20.465 -9.752 1.00 37.57 O +HETATM 877 O HOH 69 4.882 5.552 -3.840 1.00 35.89 O +HETATM 878 O HOH 70 -8.365 18.133 -7.738 1.00 48.60 O +HETATM 879 O HOH 71 15.853 21.727 0.034 0.50 44.74 O +HETATM 880 O HOH 72 7.647 16.947 -14.832 1.00 48.97 O +HETATM 881 O HOH 73 -8.357 19.324 8.033 0.50 45.46 O +HETATM 882 O HOH 74 -12.602 18.181 14.614 0.50 55.07 O +HETATM 883 O HOH 75 -11.967 14.890 14.530 1.00 64.25 O +HETATM 884 O HOH 76 -16.677 19.112 4.546 1.00 59.28 O +HETATM 885 O HOH 77 -6.235 16.426 7.163 1.00 41.97 O +HETATM 886 O HOH 78 -4.795 16.498 11.593 0.50 49.01 O +HETATM 887 O HOH 80 -7.576 3.160 5.503 1.00 47.20 O +HETATM 888 O HOH 81 18.527 2.877 -20.558 1.00 62.56 O +HETATM 889 O HOH 82 -9.964 1.666 4.528 1.00 42.60 O +HETATM 890 O HOH 83 -6.966 11.207 -8.280 1.00 45.44 O +HETATM 891 O HOH 84 -1.988 -1.806 3.532 1.00 46.53 O +HETATM 892 O HOH 85 15.995 10.079 -9.353 1.00 60.94 O +HETATM 893 O HOH 86 14.602 4.532 -10.476 1.00 42.45 O +HETATM 894 O HOH 87 10.275 2.299 -12.220 1.00 67.33 O +HETATM 895 O HOH 88 8.645 20.348 16.085 1.00 68.40 O +HETATM 896 O HOH 89 11.909 21.691 4.640 1.00 62.55 O +HETATM 897 O HOH 90 15.653 18.242 -0.147 1.00 64.24 O +HETATM 898 O HOH 91 14.225 19.534 -10.895 1.00 47.96 O +HETATM 899 O HOH 92 -15.026 16.684 9.840 1.00 53.61 O +HETATM 900 O HOH 93 -8.254 3.134 13.813 1.00 57.23 O +HETATM 901 O HOH 94 -9.603 1.630 15.643 1.00 59.26 O +HETATM 902 O HOH 95 -7.605 16.987 5.269 1.00 66.70 O +HETATM 903 O HOH 96 -12.357 19.051 -9.668 1.00 57.15 O +HETATM 904 O HOH 97 1.059 16.704 18.876 0.50 46.42 O +HETATM 905 O HOH 98 -3.827 20.328 -12.713 1.00 58.11 O +HETATM 906 O HOH 99 -5.771 22.305 -19.455 1.00 55.82 O +HETATM 907 O HOH 100 -8.074 10.963 -5.923 1.00 37.47 O +HETATM 908 O HOH 101 3.604 23.698 -13.992 1.00 55.56 O +HETATM 909 O HOH 102 2.514 3.793 -2.435 1.00 35.44 O +HETATM 910 O HOH 103 1.102 3.918 -0.887 0.50 32.54 O +HETATM 911 O HOH 104 -6.264 3.245 3.405 1.00 43.20 O +HETATM 912 O HOH 105 17.438 20.966 -5.483 1.00 47.12 O +HETATM 913 O HOH 106 16.890 23.910 -8.363 1.00 64.89 O +HETATM 914 O HOH 107 5.218 19.832 -15.657 1.00 60.86 O +HETATM 915 O HOH 108 -15.126 22.057 -9.186 0.50 37.83 O +HETATM 916 O HOH 109 -7.434 17.424 9.553 1.00 53.59 O +HETATM 917 O HOH 110 -9.334 21.316 -5.873 1.00 60.77 O +HETATM 918 O HOH 111 -4.592 21.769 -10.048 1.00 51.46 O +HETATM 919 O HOH 112 -2.055 13.949 -17.796 1.00 49.46 O +HETATM 920 O HOH 113 15.427 16.022 -13.707 1.00 54.54 O +HETATM 921 O HOH 114 18.006 16.772 -16.347 0.50 43.19 O +HETATM 922 O HOH 115 8.826 19.865 -16.035 0.50 40.86 O +HETATM 923 O HOH 116 10.124 9.372 -10.093 1.00 52.93 O +HETATM 924 O HOH 117 13.338 11.259 -8.385 0.50 48.61 O +HETATM 925 O HOH 118 9.646 17.794 5.129 1.00 59.00 O +HETATM 926 O HOH 119 6.581 23.436 -0.738 1.00 41.15 O +HETATM 927 O HOH 120 14.799 19.851 11.354 0.50 51.56 O +HETATM 928 O HOH 121 -4.174 9.465 17.486 1.00 52.65 O +HETATM 929 O HOH 122 -5.071 15.200 -15.427 1.00 56.54 O +HETATM 930 O HOH 123 -10.984 19.572 -17.083 1.00 58.95 O +CONECT 43 76 +CONECT 49 223 +CONECT 76 43 +CONECT 154 313 +CONECT 223 49 +CONECT 313 154 +CONECT 449 482 +CONECT 455 627 +CONECT 482 449 +CONECT 560 719 +CONECT 627 455 +CONECT 719 560 +MASTER 310 0 5 8 2 0 0 6 926 4 12 10 +END diff --git a/BuiltInMolecules/Nanotube.pdb b/BuiltInMolecules/Nanotube.pdb new file mode 100644 index 0000000..fa87e42 --- /dev/null +++ b/BuiltInMolecules/Nanotube.pdb @@ -0,0 +1,583 @@ +HEADER UNNAMED +COMPND UNNAMED +SOURCE Tube3.pdb +SOURCE Created by Mol2Mol 5.1.1 +SOURCE 15:44:55, Sunday, Oct 08, 2006 +REMARK 6 This file contains the CA atoms only. +HETATM 1 C 1 -3.480 -9.473 -1.870 1.00 0.00 C +HETATM 2 C 1 2.167 10.019 -6.391 1.00 0.00 C +HETATM 3 C 1 1.521 9.745 4.298 1.00 0.00 C +HETATM 4 C 1 6.905 8.678 -2.826 1.00 0.00 C +HETATM 5 C 1 4.180 8.781 -5.890 1.00 0.00 C +HETATM 6 C 1 -1.720 -8.381 2.568 1.00 0.00 C +HETATM 7 C 1 6.786 -7.155 0.225 1.00 0.00 C +HETATM 8 C 1 -1.770 -6.963 -5.531 1.00 0.00 C +HETATM 9 C 1 0.140 7.336 3.970 1.00 0.00 C +HETATM 10 C 1 1.513 7.325 4.237 1.00 0.00 C +HETATM 11 C 1 2.136 5.175 -6.526 1.00 0.00 C +HETATM 12 C 1 3.508 5.159 -6.254 1.00 0.00 C +HETATM 13 C 1 1.433 3.967 -6.562 1.00 0.00 C +HETATM 14 C 1 2.124 2.752 -6.601 1.00 0.00 C +HETATM 15 C 1 3.495 2.737 -6.326 1.00 0.00 C +HETATM 16 C 1 4.145 3.936 -6.023 1.00 0.00 C +HETATM 17 C 1 5.306 3.907 -5.244 1.00 0.00 C +HETATM 18 C 1 4.131 1.514 -6.094 1.00 0.00 C +HETATM 19 C 1 5.788 2.679 -4.782 1.00 0.00 C +HETATM 20 C 1 5.290 1.484 -5.310 1.00 0.00 C +HETATM 21 C 1 1.446 6.390 -6.487 1.00 0.00 C +HETATM 22 C 1 0.063 3.966 -6.281 1.00 0.00 C +HETATM 23 C 1 3.483 0.313 -6.401 1.00 0.00 C +HETATM 24 C 1 2.111 0.329 -6.677 1.00 0.00 C +HETATM 25 C 1 1.421 1.544 -6.638 1.00 0.00 C +HETATM 26 C 1 0.040 -0.882 -6.429 1.00 0.00 C +HETATM 27 C 1 1.410 -0.879 -6.713 1.00 0.00 C +HETATM 28 C 1 5.808 5.101 -4.718 1.00 0.00 C +HETATM 29 C 1 5.325 6.330 -5.178 1.00 0.00 C +HETATM 30 C 1 4.160 6.359 -5.954 1.00 0.00 C +HETATM 31 C 1 6.563 2.642 -3.618 1.00 0.00 C +HETATM 32 C 1 6.844 3.834 -2.945 1.00 0.00 C +HETATM 33 C 1 4.118 -0.909 -6.165 1.00 0.00 C +HETATM 34 C 1 5.275 -0.938 -5.377 1.00 0.00 C +HETATM 35 C 1 5.771 0.256 -4.847 1.00 0.00 C +HETATM 36 C 1 6.588 5.064 -3.557 1.00 0.00 C +HETATM 37 C 1 6.820 1.411 -3.006 1.00 0.00 C +HETATM 38 C 1 6.542 0.219 -3.681 1.00 0.00 C +HETATM 39 C 1 6.871 6.256 -2.884 1.00 0.00 C +HETATM 40 C 1 7.153 6.219 -1.514 1.00 0.00 C +HETATM 41 C 1 7.142 4.989 -0.847 1.00 0.00 C +HETATM 42 C 1 7.124 3.796 -1.575 1.00 0.00 C +HETATM 43 C 1 7.116 2.567 -0.909 1.00 0.00 C +HETATM 44 C 1 7.099 1.373 -1.636 1.00 0.00 C +HETATM 45 C 1 6.883 4.958 0.526 1.00 0.00 C +HETATM 46 C 1 6.611 3.733 1.143 1.00 0.00 C +HETATM 47 C 1 6.859 2.535 0.466 1.00 0.00 C +HETATM 48 C 1 7.092 0.145 -0.970 1.00 0.00 C +HETATM 49 C 1 6.838 0.112 0.405 1.00 0.00 C +HETATM 50 C 1 6.797 -1.012 -3.068 1.00 0.00 C +HETATM 51 C 1 7.073 -1.048 -1.697 1.00 0.00 C +HETATM 52 C 1 6.634 6.155 1.203 1.00 0.00 C +HETATM 53 C 1 5.867 6.132 2.373 1.00 0.00 C +HETATM 54 C 1 5.369 4.913 2.841 1.00 0.00 C +HETATM 55 C 1 5.849 3.709 2.315 1.00 0.00 C +HETATM 56 C 1 6.592 1.309 1.084 1.00 0.00 C +HETATM 57 C 1 5.355 2.490 2.785 1.00 0.00 C +HETATM 58 C 1 5.833 1.287 2.259 1.00 0.00 C +HETATM 59 C 1 5.341 0.067 2.731 1.00 0.00 C +HETATM 60 C 1 5.816 -1.137 2.201 1.00 0.00 C +HETATM 61 C 1 6.570 -1.113 1.022 1.00 0.00 C +HETATM 62 C 1 0.050 1.542 -6.356 1.00 0.00 C +HETATM 63 C 1 -0.595 0.325 -6.119 1.00 0.00 C +HETATM 64 C 1 -0.585 2.748 -6.045 1.00 0.00 C +HETATM 65 C 1 -0.573 5.172 -5.973 1.00 0.00 C +HETATM 66 C 1 -1.746 0.306 -5.324 1.00 0.00 C +HETATM 67 C 1 -1.738 2.729 -5.254 1.00 0.00 C +HETATM 68 C 1 -2.226 1.505 -4.789 1.00 0.00 C +HETATM 69 C 1 -2.220 3.929 -4.721 1.00 0.00 C +HETATM 70 C 1 -1.729 5.153 -5.185 1.00 0.00 C +HETATM 71 C 1 -2.230 -0.919 -4.855 1.00 0.00 C +HETATM 72 C 1 -2.984 -0.948 -3.677 1.00 0.00 C +HETATM 73 C 1 -3.240 0.246 -2.998 1.00 0.00 C +HETATM 74 C 1 -2.984 1.475 -3.613 1.00 0.00 C +HETATM 75 C 1 -3.240 2.669 -2.934 1.00 0.00 C +HETATM 76 C 1 -2.982 3.898 -3.549 1.00 0.00 C +HETATM 77 C 1 -3.241 5.093 -2.870 1.00 0.00 C +HETATM 78 C 1 -2.981 6.322 -3.484 1.00 0.00 C +HETATM 79 C 1 -2.215 6.353 -4.654 1.00 0.00 C +HETATM 80 C 1 4.191 0.055 3.527 1.00 0.00 C +HETATM 81 C 1 3.556 1.264 3.826 1.00 0.00 C +HETATM 82 C 1 4.213 4.901 3.629 1.00 0.00 C +HETATM 83 C 1 3.566 3.688 3.875 1.00 0.00 C +HETATM 84 C 1 3.576 6.110 3.926 1.00 0.00 C +HETATM 85 C 1 1.503 4.902 4.183 1.00 0.00 C +HETATM 86 C 1 2.195 3.687 4.156 1.00 0.00 C +HETATM 87 C 1 2.186 1.265 4.108 1.00 0.00 C +HETATM 88 C 1 3.545 -1.160 3.775 1.00 0.00 C +HETATM 89 C 1 2.175 -1.160 4.059 1.00 0.00 C +HETATM 90 C 1 1.484 0.055 4.086 1.00 0.00 C +HETATM 91 C 1 4.201 2.478 3.577 1.00 0.00 C +HETATM 92 C 1 1.494 2.478 4.133 1.00 0.00 C +HETATM 93 C 1 -3.495 0.211 -1.622 1.00 0.00 C +HETATM 94 C 1 -3.498 2.634 -1.560 1.00 0.00 C +HETATM 95 C 1 -3.491 1.404 -0.895 1.00 0.00 C +HETATM 96 C 1 -3.494 3.827 -0.831 1.00 0.00 C +HETATM 97 C 1 -3.502 5.057 -1.495 1.00 0.00 C +HETATM 98 C 1 -3.487 -1.019 -0.957 1.00 0.00 C +HETATM 99 C 1 -3.211 -1.054 0.414 1.00 0.00 C +HETATM 100 C 1 -2.946 0.141 1.088 1.00 0.00 C +HETATM 101 C 1 -3.213 1.369 0.476 1.00 0.00 C +HETATM 102 C 1 -3.214 3.792 0.540 1.00 0.00 C +HETATM 103 C 1 -2.946 2.563 1.151 1.00 0.00 C +HETATM 104 C 1 -3.499 6.250 -0.767 1.00 0.00 C +HETATM 105 C 1 -3.218 6.214 0.604 1.00 0.00 C +HETATM 106 C 1 -2.947 4.987 1.214 1.00 0.00 C +HETATM 107 C 1 -2.169 4.956 2.376 1.00 0.00 C +HETATM 108 C 1 -2.171 2.534 2.315 1.00 0.00 C +HETATM 109 C 1 -1.678 3.731 2.839 1.00 0.00 C +HETATM 110 C 1 -2.176 0.110 2.256 1.00 0.00 C +HETATM 111 C 1 -1.684 1.309 2.781 1.00 0.00 C +HETATM 112 C 1 -1.674 6.155 2.900 1.00 0.00 C +HETATM 113 C 1 -0.509 6.133 3.674 1.00 0.00 C +HETATM 114 C 1 0.131 4.913 3.912 1.00 0.00 C +HETATM 115 C 1 -0.517 3.710 3.618 1.00 0.00 C +HETATM 116 C 1 0.122 2.490 3.859 1.00 0.00 C +HETATM 117 C 1 -0.525 1.287 3.565 1.00 0.00 C +HETATM 118 C 1 -1.688 -1.114 2.723 1.00 0.00 C +HETATM 119 C 1 -0.533 -1.136 3.512 1.00 0.00 C +HETATM 120 C 1 0.113 0.067 3.809 1.00 0.00 C +HETATM 121 C 1 2.206 6.111 4.206 1.00 0.00 C +HETATM 122 C 1 0.074 6.388 -6.207 1.00 0.00 C +HETATM 123 C 1 -2.947 -2.282 1.028 1.00 0.00 C +HETATM 124 C 1 -2.179 -2.313 2.197 1.00 0.00 C +HETATM 125 C 1 -3.239 -2.177 -3.060 1.00 0.00 C +HETATM 126 C 1 -3.492 -2.213 -1.685 1.00 0.00 C +HETATM 127 C 1 -0.604 -2.099 -6.190 1.00 0.00 C +HETATM 128 C 1 -1.752 -2.118 -5.392 1.00 0.00 C +HETATM 129 C 1 3.473 -2.110 -6.474 1.00 0.00 C +HETATM 130 C 1 2.101 -2.094 -6.752 1.00 0.00 C +HETATM 131 C 1 6.521 -2.203 -3.744 1.00 0.00 C +HETATM 132 C 1 5.755 -2.166 -4.914 1.00 0.00 C +HETATM 133 C 1 6.816 -2.311 0.344 1.00 0.00 C +HETATM 134 C 1 7.068 -2.278 -1.032 1.00 0.00 C +HETATM 135 C 1 1.474 -2.368 4.037 1.00 0.00 C +HETATM 136 C 1 0.104 -2.357 3.757 1.00 0.00 C +HETATM 137 C 1 5.327 -2.357 2.676 1.00 0.00 C +HETATM 138 C 1 4.179 -2.369 3.475 1.00 0.00 C +HETATM 139 C 1 4.108 -3.331 -6.237 1.00 0.00 C +HETATM 140 C 1 5.261 -3.360 -5.446 1.00 0.00 C +HETATM 141 C 1 0.031 -3.305 -6.504 1.00 0.00 C +HETATM 142 C 1 1.400 -3.303 -6.789 1.00 0.00 C +HETATM 143 C 1 -2.986 -3.372 -3.740 1.00 0.00 C +HETATM 144 C 1 -2.235 -3.342 -4.921 1.00 0.00 C +HETATM 145 C 1 -3.210 -3.477 0.352 1.00 0.00 C +HETATM 146 C 1 -3.486 -3.442 -1.019 1.00 0.00 C +HETATM 147 C 1 7.051 -3.471 -1.759 1.00 0.00 C +HETATM 148 C 1 6.775 -3.433 -3.130 1.00 0.00 C +HETATM 149 C 1 5.802 -3.560 2.144 1.00 0.00 C +HETATM 150 C 1 6.552 -3.536 0.964 1.00 0.00 C +HETATM 151 C 1 2.166 -3.583 4.011 1.00 0.00 C +HETATM 152 C 1 3.535 -3.583 3.726 1.00 0.00 C +HETATM 153 C 1 -1.695 -3.537 2.668 1.00 0.00 C +HETATM 154 C 1 -0.542 -3.560 3.459 1.00 0.00 C +HETATM 155 C 1 6.797 -4.734 0.284 1.00 0.00 C +HETATM 156 C 1 7.047 -4.700 -1.093 1.00 0.00 C +HETATM 157 C 1 6.504 -4.625 -3.808 1.00 0.00 C +HETATM 158 C 1 5.741 -4.589 -4.980 1.00 0.00 C +HETATM 159 C 1 3.463 -4.532 -6.548 1.00 0.00 C +HETATM 160 C 1 2.092 -4.517 -6.828 1.00 0.00 C +HETATM 161 C 1 -0.613 -4.523 -6.262 1.00 0.00 C +HETATM 162 C 1 -1.759 -4.541 -5.459 1.00 0.00 C +HETATM 163 C 1 -3.241 -4.600 -3.123 1.00 0.00 C +HETATM 164 C 1 -3.492 -4.635 -1.746 1.00 0.00 C +HETATM 165 C 1 -2.949 -4.705 0.968 1.00 0.00 C +HETATM 166 C 1 -2.186 -4.736 2.140 1.00 0.00 C +HETATM 167 C 1 0.093 -4.780 3.709 1.00 0.00 C +HETATM 168 C 1 1.463 -4.792 3.989 1.00 0.00 C +HETATM 169 C 1 4.169 -4.792 3.424 1.00 0.00 C +HETATM 170 C 1 5.316 -4.780 2.621 1.00 0.00 C +HETATM 171 C 1 -2.170 7.379 2.440 1.00 0.00 C +HETATM 172 C 1 -2.952 7.408 1.280 1.00 0.00 C +HETATM 173 C 1 4.227 7.324 3.685 1.00 0.00 C +HETATM 174 C 1 5.387 7.335 2.901 1.00 0.00 C +HETATM 175 C 1 6.911 7.380 0.588 1.00 0.00 C +HETATM 176 C 1 7.175 7.411 -0.786 1.00 0.00 C +HETATM 177 C 1 6.616 7.486 -3.498 1.00 0.00 C +HETATM 178 C 1 5.833 7.523 -4.658 1.00 0.00 C +HETATM 179 C 1 3.524 7.581 -6.186 1.00 0.00 C +HETATM 180 C 1 2.150 7.597 -6.454 1.00 0.00 C +HETATM 181 C 1 -0.563 7.595 -5.903 1.00 0.00 C +HETATM 182 C 1 -1.723 7.576 -5.119 1.00 0.00 C +HETATM 183 C 1 -3.246 7.516 -2.806 1.00 0.00 C +HETATM 184 C 1 -3.508 7.480 -1.432 1.00 0.00 C +HETATM 185 C 1 -2.244 -5.765 -4.988 1.00 0.00 C +HETATM 186 C 1 -2.992 -5.795 -3.805 1.00 0.00 C +HETATM 187 C 1 -3.490 -5.864 -1.079 1.00 0.00 C +HETATM 188 C 1 -3.217 -5.900 0.293 1.00 0.00 C +HETATM 189 C 1 -1.707 -5.960 2.615 1.00 0.00 C +HETATM 190 C 1 -0.555 -5.983 3.411 1.00 0.00 C +HETATM 191 C 1 2.156 -6.007 3.966 1.00 0.00 C +HETATM 192 C 1 3.526 -6.007 3.680 1.00 0.00 C +HETATM 193 C 1 5.792 -5.983 2.089 1.00 0.00 C +HETATM 194 C 1 6.538 -5.958 0.905 1.00 0.00 C +HETATM 195 C 1 7.035 -5.893 -1.822 1.00 0.00 C +HETATM 196 C 1 6.761 -5.855 -3.194 1.00 0.00 C +HETATM 197 C 1 5.251 -5.783 -5.518 1.00 0.00 C +HETATM 198 C 1 4.101 -5.754 -6.312 1.00 0.00 C +HETATM 199 C 1 0.020 -5.729 -6.579 1.00 0.00 C +HETATM 200 C 1 1.390 -5.726 -6.868 1.00 0.00 C +HETATM 201 C 1 -0.625 -6.945 -6.338 1.00 0.00 C +HETATM 202 C 1 -3.500 -7.057 -1.809 1.00 0.00 C +HETATM 203 C 1 -3.249 -7.022 -3.186 1.00 0.00 C +HETATM 204 C 1 -2.960 -7.127 0.912 1.00 0.00 C +HETATM 205 C 1 -2.199 -7.158 2.088 1.00 0.00 C +HETATM 206 C 1 3.455 -6.954 -6.628 1.00 0.00 C +HETATM 207 C 1 2.083 -6.939 -6.910 1.00 0.00 C +HETATM 208 C 1 5.735 -7.011 -5.051 1.00 0.00 C +HETATM 209 C 1 6.494 -7.047 -3.875 1.00 0.00 C +HETATM 210 C 1 7.035 -7.122 -1.154 1.00 0.00 C +HETATM 211 C 1 4.162 -7.216 3.378 1.00 0.00 C +HETATM 212 C 1 5.307 -7.203 2.570 1.00 0.00 C +HETATM 213 C 1 1.452 -7.215 3.947 1.00 0.00 C +HETATM 214 C 1 0.080 -7.203 3.664 1.00 0.00 C +HETATM 215 C 1 -0.572 -8.405 3.366 1.00 0.00 C +HETATM 216 C 1 -3.501 -8.285 -1.138 1.00 0.00 C +HETATM 217 C 1 -3.229 -8.320 0.233 1.00 0.00 C +HETATM 218 C 1 -3.002 -8.216 -3.874 1.00 0.00 C +HETATM 219 C 1 -2.259 -8.186 -5.057 1.00 0.00 C +HETATM 220 C 1 0.010 -8.150 -6.661 1.00 0.00 C +HETATM 221 C 1 5.245 -8.204 -5.593 1.00 0.00 C +HETATM 222 C 1 4.098 -8.175 -6.392 1.00 0.00 C +HETATM 223 C 1 6.754 -8.276 -3.258 1.00 0.00 C +HETATM 224 C 1 7.025 -8.314 -1.887 1.00 0.00 C +HETATM 225 C 1 6.529 -8.380 0.851 1.00 0.00 C +HETATM 226 C 1 5.787 -8.404 2.035 1.00 0.00 C +HETATM 227 C 1 3.516 -8.429 3.638 1.00 0.00 C +HETATM 228 C 1 2.149 -8.429 3.926 1.00 0.00 C +HETATM 229 C 1 5.349 8.752 -5.120 1.00 0.00 C +HETATM 230 C 1 7.187 8.640 -1.454 1.00 0.00 C +HETATM 231 C 1 1.459 8.812 -6.419 1.00 0.00 C +HETATM 232 C 1 0.085 8.811 -6.140 1.00 0.00 C +HETATM 233 C 1 -2.212 8.775 -4.590 1.00 0.00 C +HETATM 234 C 1 -2.985 8.745 -3.422 1.00 0.00 C +HETATM 235 C 1 -3.510 8.672 -0.701 1.00 0.00 C +HETATM 236 C 1 -3.228 8.637 0.670 1.00 0.00 C +HETATM 237 C 1 -1.674 8.577 2.966 1.00 0.00 C +HETATM 238 C 1 -0.505 8.555 3.737 1.00 0.00 C +HETATM 239 C 1 2.217 8.533 4.264 1.00 0.00 C +HETATM 240 C 1 3.590 8.533 3.984 1.00 0.00 C +HETATM 241 C 1 5.889 8.555 2.436 1.00 0.00 C +HETATM 242 C 1 6.662 8.577 1.268 1.00 0.00 C +HETATM 243 C 1 4.246 9.745 3.744 1.00 0.00 C +HETATM 244 C 1 5.407 9.756 2.966 1.00 0.00 C +HETATM 245 C 1 -2.961 9.830 1.351 1.00 0.00 C +HETATM 246 C 1 -2.175 9.800 2.507 1.00 0.00 C +HETATM 247 C 1 -3.254 9.937 -2.743 1.00 0.00 C +HETATM 248 C 1 -3.520 9.901 -1.370 1.00 0.00 C +HETATM 249 C 1 -0.557 10.016 -5.837 1.00 0.00 C +HETATM 250 C 1 -1.718 9.998 -5.058 1.00 0.00 C +HETATM 251 C 1 3.540 10.002 -6.126 1.00 0.00 C +HETATM 252 C 1 7.211 9.832 -0.722 1.00 0.00 C +HETATM 253 C 1 6.944 9.800 0.650 1.00 0.00 C +HETATM 254 C 1 5.863 9.944 -4.598 1.00 0.00 C +HETATM 255 C 1 6.649 9.907 -3.444 1.00 0.00 C +HETATM 256 C 1 4.143 -9.631 3.304 1.00 0.00 C +HETATM 257 C 1 5.280 -9.618 2.500 1.00 0.00 C +HETATM 258 C 1 0.078 -9.619 3.592 1.00 0.00 C +HETATM 259 C 1 1.442 -9.631 3.874 1.00 0.00 C +HETATM 260 C 1 -2.946 -9.542 0.845 1.00 0.00 C +HETATM 261 C 1 -2.190 -9.573 2.014 1.00 0.00 C +HETATM 262 C 1 -3.231 -9.438 -3.240 1.00 0.00 C +HETATM 263 C 1 6.747 -9.569 0.155 1.00 0.00 C +HETATM 264 C 1 6.996 -9.537 -1.216 1.00 0.00 C +HETATM 265 C 1 6.460 -9.462 -3.932 1.00 0.00 C +HETATM 266 C 1 5.705 -9.426 -5.102 1.00 0.00 C +HETATM 267 C 1 3.437 -9.371 -6.681 1.00 0.00 C +HETATM 268 C 1 2.074 -9.356 -6.962 1.00 0.00 C +HETATM 269 C 1 1.378 -8.147 -6.951 1.00 0.00 C +HETATM 270 C 1 -1.761 -9.380 -5.582 1.00 0.00 C +HETATM 271 C 1 -0.626 -9.362 -6.387 1.00 0.00 C +HETATM 272 C 1 1.472 11.227 -6.323 1.00 0.00 C +HETATM 273 C 1 0.106 11.226 -6.047 1.00 0.00 C +HETATM 274 C 1 -2.190 11.191 -4.508 1.00 0.00 C +HETATM 275 C 1 -2.960 11.161 -3.348 1.00 0.00 C +HETATM 276 C 1 -3.490 11.088 -0.638 1.00 0.00 C +HETATM 277 C 1 -3.210 11.053 0.726 1.00 0.00 C +HETATM 278 C 1 -1.653 10.993 3.010 1.00 0.00 C +HETATM 279 C 1 -0.488 10.972 3.773 1.00 0.00 C +HETATM 280 C 1 0.148 9.758 4.034 1.00 0.00 C +HETATM 281 C 1 2.228 10.949 4.293 1.00 0.00 C +HETATM 282 C 1 3.593 10.949 4.016 1.00 0.00 C +HETATM 283 C 1 5.892 10.971 2.478 1.00 0.00 C +HETATM 284 C 1 6.663 10.993 1.318 1.00 0.00 C +HETATM 285 C 1 7.193 11.057 -1.393 1.00 0.00 C +HETATM 286 C 1 6.911 11.094 -2.756 1.00 0.00 C +HETATM 287 C 1 5.353 11.166 -5.039 1.00 0.00 C +HETATM 288 C 1 4.188 11.196 -5.802 1.00 0.00 C +CONECT 1 216 262 +CONECT 2 231 251 272 +CONECT 3 239 280 281 +CONECT 4 177 230 255 +CONECT 5 179 229 251 +CONECT 6 205 215 261 +CONECT 7 194 210 225 +CONECT 8 185 201 219 +CONECT 9 10 113 238 +CONECT 10 9 121 239 +CONECT 11 12 13 21 +CONECT 12 11 16 30 +CONECT 13 11 14 22 +CONECT 14 13 15 25 +CONECT 15 14 16 18 +CONECT 16 12 15 17 +CONECT 17 16 19 28 +CONECT 18 15 20 23 +CONECT 19 17 20 31 +CONECT 20 18 19 35 +CONECT 21 11 122 180 +CONECT 22 13 64 65 +CONECT 23 18 24 33 +CONECT 24 23 25 27 +CONECT 25 14 24 62 +CONECT 26 27 63 127 +CONECT 27 24 26 130 +CONECT 28 17 29 36 +CONECT 29 28 30 178 +CONECT 30 12 29 179 +CONECT 31 19 32 37 +CONECT 32 31 36 42 +CONECT 33 23 34 129 +CONECT 34 33 35 132 +CONECT 35 20 34 38 +CONECT 36 28 32 39 +CONECT 37 31 38 44 +CONECT 38 35 37 50 +CONECT 39 36 40 177 +CONECT 40 39 41 176 +CONECT 41 40 42 45 +CONECT 42 32 41 43 +CONECT 43 42 44 47 +CONECT 44 37 43 48 +CONECT 45 41 46 52 +CONECT 46 45 47 55 +CONECT 47 43 46 56 +CONECT 48 44 49 51 +CONECT 49 48 56 61 +CONECT 50 38 51 131 +CONECT 51 48 50 134 +CONECT 52 45 53 175 +CONECT 53 52 54 174 +CONECT 54 53 55 82 +CONECT 55 46 54 57 +CONECT 56 47 49 58 +CONECT 57 55 58 91 +CONECT 58 56 57 59 +CONECT 59 58 60 80 +CONECT 60 59 61 137 +CONECT 61 49 60 133 +CONECT 62 25 63 64 +CONECT 63 26 62 66 +CONECT 64 22 62 67 +CONECT 65 22 70 122 +CONECT 66 63 68 71 +CONECT 67 64 68 69 +CONECT 68 66 67 74 +CONECT 69 67 70 76 +CONECT 70 65 69 79 +CONECT 71 66 72 128 +CONECT 72 71 73 125 +CONECT 73 72 74 93 +CONECT 74 68 73 75 +CONECT 75 74 76 94 +CONECT 76 69 75 77 +CONECT 77 76 78 97 +CONECT 78 77 79 183 +CONECT 79 70 78 182 +CONECT 80 59 81 88 +CONECT 81 80 87 91 +CONECT 82 54 83 84 +CONECT 83 82 86 91 +CONECT 84 82 121 173 +CONECT 85 86 114 121 +CONECT 86 83 85 92 +CONECT 87 81 90 92 +CONECT 88 80 89 138 +CONECT 89 88 90 135 +CONECT 90 87 89 120 +CONECT 91 57 81 83 +CONECT 92 86 87 116 +CONECT 93 73 95 98 +CONECT 94 75 95 96 +CONECT 95 93 94 101 +CONECT 96 94 97 102 +CONECT 97 77 96 104 +CONECT 98 93 99 126 +CONECT 99 98 100 123 +CONECT 100 99 101 110 +CONECT 101 95 100 103 +CONECT 102 96 103 106 +CONECT 103 101 102 108 +CONECT 104 97 105 184 +CONECT 105 104 106 172 +CONECT 106 102 105 107 +CONECT 107 106 109 112 +CONECT 108 103 109 111 +CONECT 109 107 108 115 +CONECT 110 100 111 118 +CONECT 111 108 110 117 +CONECT 112 107 113 171 +CONECT 113 9 112 114 +CONECT 114 85 113 115 +CONECT 115 109 114 116 +CONECT 116 92 115 117 +CONECT 117 111 116 120 +CONECT 118 110 119 124 +CONECT 119 118 120 136 +CONECT 120 90 117 119 +CONECT 121 10 84 85 +CONECT 122 21 65 181 +CONECT 123 99 124 145 +CONECT 124 118 123 153 +CONECT 125 72 126 143 +CONECT 126 98 125 146 +CONECT 127 26 128 141 +CONECT 128 71 127 144 +CONECT 129 33 130 139 +CONECT 130 27 129 142 +CONECT 131 50 132 148 +CONECT 132 34 131 140 +CONECT 133 61 134 150 +CONECT 134 51 133 147 +CONECT 135 89 136 151 +CONECT 136 119 135 154 +CONECT 137 60 138 149 +CONECT 138 88 137 152 +CONECT 139 129 140 159 +CONECT 140 132 139 158 +CONECT 141 127 142 161 +CONECT 142 130 141 160 +CONECT 143 125 144 163 +CONECT 144 128 143 162 +CONECT 145 123 146 165 +CONECT 146 126 145 164 +CONECT 147 134 148 156 +CONECT 148 131 147 157 +CONECT 149 137 150 170 +CONECT 150 133 149 155 +CONECT 151 135 152 168 +CONECT 152 138 151 169 +CONECT 153 124 154 166 +CONECT 154 136 153 167 +CONECT 155 150 156 194 +CONECT 156 147 155 195 +CONECT 157 148 158 196 +CONECT 158 140 157 197 +CONECT 159 139 160 198 +CONECT 160 142 159 200 +CONECT 161 141 162 199 +CONECT 162 144 161 185 +CONECT 163 143 164 186 +CONECT 164 146 163 187 +CONECT 165 145 166 188 +CONECT 166 153 165 189 +CONECT 167 154 168 190 +CONECT 168 151 167 191 +CONECT 169 152 170 192 +CONECT 170 149 169 193 +CONECT 171 112 172 237 +CONECT 172 105 171 236 +CONECT 173 84 174 240 +CONECT 174 53 173 241 +CONECT 175 52 176 242 +CONECT 176 40 175 230 +CONECT 177 4 39 178 +CONECT 178 29 177 229 +CONECT 179 5 30 180 +CONECT 180 21 179 231 +CONECT 181 122 182 232 +CONECT 182 79 181 233 +CONECT 183 78 184 234 +CONECT 184 104 183 235 +CONECT 185 8 162 186 +CONECT 186 163 185 203 +CONECT 187 164 188 202 +CONECT 188 165 187 204 +CONECT 189 166 190 205 +CONECT 190 167 189 214 +CONECT 191 168 192 213 +CONECT 192 169 191 211 +CONECT 193 170 194 212 +CONECT 194 7 155 193 +CONECT 195 156 196 210 +CONECT 196 157 195 209 +CONECT 197 158 198 208 +CONECT 198 159 197 206 +CONECT 199 161 200 201 +CONECT 200 160 199 207 +CONECT 201 8 199 220 +CONECT 202 187 203 216 +CONECT 203 186 202 218 +CONECT 204 188 205 217 +CONECT 205 6 189 204 +CONECT 206 198 207 222 +CONECT 207 200 206 269 +CONECT 208 197 209 221 +CONECT 209 196 208 223 +CONECT 210 7 195 224 +CONECT 211 192 212 227 +CONECT 212 193 211 226 +CONECT 213 191 214 228 +CONECT 214 190 213 215 +CONECT 215 6 214 258 +CONECT 216 1 202 217 +CONECT 217 204 216 260 +CONECT 218 203 219 262 +CONECT 219 8 218 270 +CONECT 220 201 269 271 +CONECT 221 208 222 266 +CONECT 222 206 221 267 +CONECT 223 209 224 265 +CONECT 224 210 223 264 +CONECT 225 7 226 263 +CONECT 226 212 225 257 +CONECT 227 211 228 256 +CONECT 228 213 227 259 +CONECT 229 5 178 254 +CONECT 230 4 176 252 +CONECT 231 2 180 232 +CONECT 232 181 231 249 +CONECT 233 182 234 250 +CONECT 234 183 233 247 +CONECT 235 184 236 248 +CONECT 236 172 235 245 +CONECT 237 171 238 246 +CONECT 238 9 237 280 +CONECT 239 3 10 240 +CONECT 240 173 239 243 +CONECT 241 174 242 244 +CONECT 242 175 241 253 +CONECT 243 240 244 282 +CONECT 244 241 243 283 +CONECT 245 236 246 277 +CONECT 246 237 245 278 +CONECT 247 234 248 275 +CONECT 248 235 247 276 +CONECT 249 232 250 273 +CONECT 250 233 249 274 +CONECT 251 2 5 288 +CONECT 252 230 253 285 +CONECT 253 242 252 284 +CONECT 254 229 255 287 +CONECT 255 4 254 286 +CONECT 256 227 257 +CONECT 257 226 256 +CONECT 258 215 259 +CONECT 259 228 258 +CONECT 260 217 261 +CONECT 261 6 260 +CONECT 262 1 218 +CONECT 263 225 264 +CONECT 264 224 263 +CONECT 265 223 266 +CONECT 266 221 265 +CONECT 267 222 268 +CONECT 268 267 269 +CONECT 269 207 220 268 +CONECT 270 219 271 +CONECT 271 220 270 +CONECT 272 2 273 +CONECT 273 249 272 +CONECT 274 250 275 +CONECT 275 247 274 +CONECT 276 248 277 +CONECT 277 245 276 +CONECT 278 246 279 +CONECT 279 278 280 +CONECT 280 3 238 279 +CONECT 281 3 282 +CONECT 282 243 281 +CONECT 283 244 284 +CONECT 284 253 283 +CONECT 285 252 286 +CONECT 286 255 285 +CONECT 287 254 288 +CONECT 288 251 287 +END diff --git a/BuiltInMolecules/TheoreticalBearing.pdb b/BuiltInMolecules/TheoreticalBearing.pdb new file mode 100644 index 0000000..8325fb4 --- /dev/null +++ b/BuiltInMolecules/TheoreticalBearing.pdb @@ -0,0 +1,414 @@ +COMPND SmallBearing.pdb +HETATM 1 C -2.937 -5.893 1.219 +HETATM 2 C -2.553 -5.098 -0.109 +HETATM 3 C -2.947 -5.889 -1.437 +HETATM 4 Si -4.839 -5.890 1.063 +HETATM 5 Si -4.846 -5.890 -1.270 +HETATM 6 O -3.107 -3.761 -0.102 +HETATM 7 C -5.203 -4.021 1.234 +HETATM 8 C -4.524 -3.488 -0.104 +HETATM 9 C -5.204 -4.022 -1.441 +HETATM 10 Si -6.928 -3.219 1.063 +HETATM 11 Si -6.930 -3.223 -1.270 +HETATM 12 O -4.445 -2.045 -0.105 +HETATM 13 C -6.464 -1.375 1.224 +HETATM 14 C -5.610 -1.189 -0.109 +HETATM 15 C -6.464 -1.382 -1.440 +HETATM 16 Si -7.658 0.106 1.054 +HETATM 17 Si -7.661 0.096 -1.280 +HETATM 18 O -4.918 0.080 -0.109 +HETATM 19 C -6.439 1.568 1.212 +HETATM 20 C -5.591 1.359 -0.120 +HETATM 21 C -6.441 1.553 -1.454 +HETATM 22 Si -6.877 3.417 1.035 +HETATM 23 Si -6.881 3.403 -1.300 +HETATM 24 O -4.414 2.200 -0.127 +HETATM 25 C -5.140 4.196 1.197 +HETATM 26 C -4.470 3.643 -0.137 +HETATM 27 C -5.145 4.178 -1.477 +HETATM 28 Si -4.745 6.057 1.014 +HETATM 29 Si -4.752 6.042 -1.320 +HETATM 30 O -3.049 3.895 -0.141 +HETATM 31 C -2.843 6.031 1.169 +HETATM 32 C -2.472 5.222 -0.155 +HETATM 33 C -2.852 6.013 -1.487 +HETATM 34 Si -1.659 7.516 0.993 +HETATM 35 Si -1.669 7.500 -1.339 +HETATM 36 O -1.081 4.822 -0.158 +HETATM 37 C 0.029 6.633 1.163 +HETATM 38 C 0.015 5.763 -0.170 +HETATM 39 C 0.015 6.616 -1.513 +HETATM 40 Si 1.729 7.487 0.977 +HETATM 41 Si 1.716 7.474 -1.355 +HETATM 42 O 1.095 4.804 -0.169 +HETATM 43 C 2.891 5.983 1.140 +HETATM 44 C 2.492 5.181 -0.180 +HETATM 45 C 2.874 5.967 -1.515 +HETATM 46 Si 4.791 5.976 0.967 +HETATM 47 Si 4.776 5.964 -1.366 +HETATM 48 O 3.047 3.843 -0.174 +HETATM 49 C 5.154 4.107 1.145 +HETATM 50 C 4.464 3.568 -0.184 +HETATM 51 C 5.133 4.094 -1.530 +HETATM 52 Si 6.878 3.303 0.964 +HETATM 53 Si 6.859 3.292 -1.369 +HETATM 54 O 4.384 2.125 -0.175 +HETATM 55 C 6.415 1.460 1.142 +HETATM 56 C 5.548 1.267 -0.182 +HETATM 57 C 6.390 1.450 -1.523 +HETATM 58 Si 7.607 -0.022 0.970 +HETATM 59 Si 7.585 -0.030 -1.364 +HETATM 60 O 4.854 -0.002 -0.166 +HETATM 61 C 6.388 -1.482 1.152 +HETATM 62 C 5.526 -1.281 -0.173 +HETATM 63 C 6.360 -1.486 -1.515 +HETATM 64 Si 6.825 -3.332 0.982 +HETATM 65 Si 6.800 -3.335 -1.351 +HETATM 66 O 4.348 -2.118 -0.158 +HETATM 67 C 5.090 -4.109 1.171 +HETATM 68 C 4.404 -3.563 -0.158 +HETATM 69 C 5.061 -4.107 -1.503 +HETATM 70 Si 4.692 -5.971 1.004 +HETATM 71 Si 4.670 -5.969 -1.330 +HETATM 72 O 2.983 -3.812 -0.139 +HETATM 73 C 2.792 -5.940 1.184 +HETATM 74 C 2.406 -5.139 -0.140 +HETATM 75 C 2.770 -5.935 -1.474 +HETATM 76 Si 1.608 -7.428 1.026 +HETATM 77 Si 1.591 -7.423 -1.306 +HETATM 78 O 1.017 -4.737 -0.126 +HETATM 79 C -0.077 -6.545 1.213 +HETATM 80 C -0.078 -5.678 -0.123 +HETATM 81 C -0.094 -6.536 -1.464 +HETATM 82 Si -1.779 -7.400 1.048 +HETATM 83 Si -1.790 -7.396 -1.284 +HETATM 84 O -1.157 -4.719 -0.111 +HETATM 191 H -2.676 -5.387 2.169 +HETATM 192 H -2.695 -5.384 -2.390 +HETATM 193 H -5.617 -6.842 1.879 +HETATM 195 H -5.627 -6.840 -2.087 +HETATM 197 H -4.714 -3.634 2.154 +HETATM 199 H -4.717 -3.635 -2.362 +HETATM 198 H -8.038 -3.745 1.881 +HETATM 200 H -8.038 -3.752 -2.089 +HETATM 201 H -5.880 -1.256 2.160 +HETATM 203 H -5.884 -1.270 -2.378 +HETATM 202 H -8.889 0.119 1.869 +HETATM 204 H -8.891 0.100 -2.096 +HETATM 205 H -5.853 1.446 2.146 +HETATM 207 H -5.857 1.423 -2.389 +HETATM 206 H -7.979 3.966 1.850 +HETATM 208 H -7.984 3.939 -2.119 +HETATM 209 H -4.659 3.808 2.121 +HETATM 211 H -4.668 3.779 -2.397 +HETATM 210 H -5.508 7.027 1.824 +HETATM 212 H -5.521 7.000 -2.141 +HETATM 213 H -2.589 5.526 2.122 +HETATM 215 H -2.605 5.498 -2.436 +HETATM 214 H -1.912 8.723 1.804 +HETATM 216 H -1.930 8.698 -2.164 +HETATM 217 H 0.028 6.010 2.083 +HETATM 219 H 0.005 5.982 -2.426 +HETATM 218 H 2.011 8.689 1.785 +HETATM 220 H 1.989 8.666 -2.182 +HETATM 221 H 2.641 5.485 2.097 +HETATM 223 H 2.613 5.459 -2.463 +HETATM 222 H 5.579 6.930 1.772 +HETATM 224 H 5.552 6.908 -2.193 +HETATM 225 H 4.673 3.725 2.071 +HETATM 227 H 4.638 3.702 -2.444 +HETATM 226 H 7.996 3.833 1.770 +HETATM 228 H 7.963 3.813 -2.200 +HETATM 229 H 5.843 1.348 2.085 +HETATM 231 H 5.800 1.332 -2.455 +HETATM 230 H 8.846 -0.032 1.772 +HETATM 232 H 8.807 -0.045 -2.193 +HETATM 233 H 5.811 -1.353 2.091 +HETATM 235 H 5.765 -1.360 -2.443 +HETATM 234 H 7.936 -3.876 1.787 +HETATM 236 H 7.891 -3.880 -2.183 +HETATM 237 H 4.621 -3.716 2.100 +HETATM 239 H 4.572 -3.713 -2.420 +HETATM 238 H 5.464 -6.937 1.810 +HETATM 240 H 5.425 -6.931 -2.156 +HETATM 241 H 2.548 -5.427 2.135 +HETATM 243 H 2.511 -5.423 -2.421 +HETATM 242 H 1.872 -8.633 1.837 +HETATM 244 H 1.841 -8.621 -2.131 +HETATM 245 H -0.065 -5.920 2.132 +HETATM 246 H -0.094 -5.904 -2.379 +HETATM 194 H -2.052 -8.600 1.863 +HETATM 196 H -2.070 -8.590 -2.106 +HETATM 113 C -2.127 1.223 -2.564 +HETATM 114 C -2.113 -1.171 -2.551 +HETATM 115 C -0.060 2.432 -2.582 +HETATM 116 C 2.020 1.247 -2.588 +HETATM 117 C 2.034 -1.147 -2.575 +HETATM 118 C -0.033 -2.356 -2.555 +HETATM 119 C -1.359 -0.732 -3.825 +HETATM 120 C -1.367 0.780 -3.834 +HETATM 121 C -0.063 1.543 -3.845 +HETATM 122 C 1.251 0.795 -3.849 +HETATM 123 C 1.259 -0.717 -3.841 +HETATM 124 C -0.046 -1.481 -3.828 +HETATM 125 S -3.500 0.018 -2.756 +HETATM 126 S -1.790 3.016 -2.777 +HETATM 127 S 1.661 3.036 -2.796 +HETATM 128 S 3.404 0.057 -2.796 +HETATM 129 S 1.695 -2.942 -2.764 +HETATM 130 S -1.756 -2.961 -2.743 +HETATM 131 C -2.100 1.249 2.317 +HETATM 132 C -0.032 2.458 2.298 +HETATM 133 C -2.085 -1.145 2.330 +HETATM 134 C -0.004 -2.331 2.323 +HETATM 135 C 2.063 -1.123 2.305 +HETATM 136 C 2.050 1.272 2.293 +HETATM 137 C -0.019 1.583 3.570 +HETATM 138 C -1.324 0.820 3.583 +HETATM 139 C -1.315 -0.693 3.591 +HETATM 140 C -0.002 -1.442 3.586 +HETATM 141 C 1.303 -0.678 3.575 +HETATM 142 C 1.295 0.834 3.567 +HETATM 143 S -1.760 3.046 2.502 +HETATM 144 S -3.471 0.045 2.532 +HETATM 145 S -1.727 -2.937 2.529 +HETATM 146 S 1.727 -2.918 2.509 +HETATM 147 S 3.437 0.083 2.491 +HETATM 148 S 1.693 3.064 2.481 +HETATM 149 C 1.286 0.824 1.024 +HETATM 150 C -0.034 1.575 1.028 +HETATM 151 C -1.346 0.809 1.040 +HETATM 152 C -1.337 -0.710 1.048 +HETATM 153 C -0.017 -1.461 1.044 +HETATM 154 C 1.295 -0.695 1.032 +HETATM 155 O -0.046 2.400 -0.142 +HETATM 156 O 1.996 1.236 -0.147 +HETATM 157 O 2.010 -1.112 -0.134 +HETATM 158 O -2.074 1.214 -0.123 +HETATM 159 O -2.061 -1.134 -0.110 +HETATM 160 O -0.019 -2.298 -0.116 +HETATM 161 C -0.030 -1.474 -1.285 +HETATM 162 C -1.351 -0.722 -1.282 +HETATM 163 C -1.360 0.797 -1.290 +HETATM 164 C -0.048 1.563 -1.302 +HETATM 165 C 1.272 0.811 -1.306 +HETATM 166 C 1.281 -0.708 -1.297 +HETATM 167 H -1.931 -1.070 -4.734 +HETATM 168 H -1.943 1.100 -4.746 +HETATM 169 H -0.072 2.196 -4.762 +HETATM 170 H 1.812 1.121 -4.768 +HETATM 171 H 1.824 -1.048 -4.756 +HETATM 172 H -0.047 -2.144 -4.737 +HETATM 173 H -0.017 2.247 4.479 +HETATM 174 H -1.890 1.151 4.498 +HETATM 175 H -1.878 -1.020 4.509 +HETATM 176 H 0.007 -2.096 4.502 +HETATM 177 H 1.880 -1.000 4.487 +HETATM 178 H 1.868 1.172 4.475 +CONECT 1 2 4 82 191 +CONECT 2 1 3 6 84 +CONECT 3 2 5 83 192 +CONECT 4 1 5 7 193 +CONECT 5 3 4 9 195 +CONECT 6 2 8 +CONECT 7 4 8 10 197 +CONECT 8 6 7 9 12 +CONECT 9 5 8 11 199 +CONECT 10 7 11 13 198 +CONECT 11 9 10 15 200 +CONECT 12 8 14 +CONECT 13 10 14 16 201 +CONECT 14 12 13 15 18 +CONECT 15 11 14 17 203 +CONECT 16 13 17 19 202 +CONECT 17 15 16 21 204 +CONECT 18 14 20 +CONECT 19 16 20 22 205 +CONECT 20 18 19 21 24 +CONECT 21 17 20 23 207 +CONECT 22 19 23 25 206 +CONECT 23 21 22 27 208 +CONECT 24 20 26 +CONECT 25 22 26 28 209 +CONECT 26 24 25 27 30 +CONECT 27 23 26 29 211 +CONECT 28 25 29 31 210 +CONECT 29 27 28 33 212 +CONECT 30 26 32 +CONECT 31 28 32 34 213 +CONECT 32 30 31 33 36 +CONECT 33 29 32 35 215 +CONECT 34 31 35 37 214 +CONECT 35 33 34 39 216 +CONECT 36 32 38 +CONECT 37 34 38 40 217 +CONECT 38 36 37 39 42 +CONECT 39 35 38 41 219 +CONECT 40 37 41 43 218 +CONECT 41 39 40 45 220 +CONECT 42 38 44 +CONECT 43 40 44 46 221 +CONECT 44 42 43 45 48 +CONECT 45 41 44 47 223 +CONECT 46 43 47 49 222 +CONECT 47 45 46 51 224 +CONECT 48 44 50 +CONECT 49 46 50 52 225 +CONECT 50 48 49 51 54 +CONECT 51 47 50 53 227 +CONECT 52 49 53 55 226 +CONECT 53 51 52 57 228 +CONECT 54 50 56 +CONECT 55 52 56 58 229 +CONECT 56 54 55 57 60 +CONECT 57 53 56 59 231 +CONECT 58 55 59 61 230 +CONECT 59 57 58 63 232 +CONECT 60 56 62 +CONECT 61 58 62 64 233 +CONECT 62 60 61 63 66 +CONECT 63 59 62 65 235 +CONECT 64 61 65 67 234 +CONECT 65 63 64 69 236 +CONECT 66 62 68 +CONECT 67 64 68 70 237 +CONECT 68 66 67 69 72 +CONECT 69 65 68 71 239 +CONECT 70 67 71 73 238 +CONECT 71 69 70 75 240 +CONECT 72 68 74 +CONECT 73 70 74 76 241 +CONECT 74 72 73 75 78 +CONECT 75 71 74 77 243 +CONECT 76 73 77 79 242 +CONECT 77 75 76 81 244 +CONECT 78 74 80 +CONECT 79 76 80 82 245 +CONECT 80 78 79 81 84 +CONECT 81 77 80 83 246 +CONECT 82 1 79 83 194 +CONECT 83 3 81 82 196 +CONECT 84 2 80 +CONECT 191 1 +CONECT 192 3 +CONECT 193 4 +CONECT 195 5 +CONECT 197 7 +CONECT 199 9 +CONECT 198 10 +CONECT 200 11 +CONECT 201 13 +CONECT 203 15 +CONECT 202 16 +CONECT 204 17 +CONECT 205 19 +CONECT 207 21 +CONECT 206 22 +CONECT 208 23 +CONECT 209 25 +CONECT 211 27 +CONECT 210 28 +CONECT 212 29 +CONECT 213 31 +CONECT 215 33 +CONECT 214 34 +CONECT 216 35 +CONECT 217 37 +CONECT 219 39 +CONECT 218 40 +CONECT 220 41 +CONECT 221 43 +CONECT 223 45 +CONECT 222 46 +CONECT 224 47 +CONECT 225 49 +CONECT 227 51 +CONECT 226 52 +CONECT 228 53 +CONECT 229 55 +CONECT 231 57 +CONECT 230 58 +CONECT 232 59 +CONECT 233 61 +CONECT 235 63 +CONECT 234 64 +CONECT 236 65 +CONECT 237 67 +CONECT 239 69 +CONECT 238 70 +CONECT 240 71 +CONECT 241 73 +CONECT 243 75 +CONECT 242 76 +CONECT 244 77 +CONECT 245 79 +CONECT 246 81 +CONECT 194 82 +CONECT 196 83 +CONECT 113 120 125 126 163 +CONECT 114 119 125 130 162 +CONECT 115 121 126 127 164 +CONECT 116 122 127 128 165 +CONECT 117 123 128 129 166 +CONECT 118 124 129 130 161 +CONECT 119 114 120 124 167 +CONECT 120 113 119 121 168 +CONECT 121 115 120 122 169 +CONECT 122 116 121 123 170 +CONECT 123 117 122 124 171 +CONECT 124 118 119 123 172 +CONECT 125 113 114 +CONECT 126 113 115 +CONECT 127 115 116 +CONECT 128 116 117 +CONECT 129 117 118 +CONECT 130 114 118 +CONECT 131 138 143 144 151 +CONECT 132 137 143 148 150 +CONECT 133 139 144 145 152 +CONECT 134 140 145 146 153 +CONECT 135 141 146 147 154 +CONECT 136 142 147 148 149 +CONECT 137 132 138 142 173 +CONECT 138 131 137 139 174 +CONECT 139 133 138 140 175 +CONECT 140 134 139 141 176 +CONECT 141 135 140 142 177 +CONECT 142 136 137 141 178 +CONECT 143 131 132 +CONECT 144 131 133 +CONECT 145 133 134 +CONECT 146 134 135 +CONECT 147 135 136 +CONECT 148 132 136 +CONECT 149 136 150 154 156 +CONECT 150 132 149 151 155 +CONECT 151 131 150 152 158 +CONECT 152 133 151 153 159 +CONECT 153 134 152 154 160 +CONECT 154 135 149 153 157 +CONECT 155 150 164 +CONECT 156 149 165 +CONECT 157 154 166 +CONECT 158 151 163 +CONECT 159 152 162 +CONECT 160 153 161 +CONECT 161 118 160 162 166 +CONECT 162 114 159 161 163 +CONECT 163 113 158 162 164 +CONECT 164 115 155 163 165 +CONECT 165 116 156 164 166 +CONECT 166 117 157 161 165 +CONECT 167 119 +CONECT 168 120 +CONECT 169 121 +CONECT 170 122 +CONECT 171 123 +CONECT 172 124 +CONECT 173 137 +CONECT 174 138 +CONECT 175 139 +CONECT 176 140 +CONECT 177 141 +CONECT 178 142 +END \ No newline at end of file diff --git a/BuiltInMolecules/TheoreticalXenonPump.pdb.gz b/BuiltInMolecules/TheoreticalXenonPump.pdb.gz new file mode 100644 index 0000000000000000000000000000000000000000..00e81f4dbac1aba9b5541894706fbc0b629b51da GIT binary patch literal 137201 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 literal 0 HcmV?d00001 diff --git a/BuiltInMolecules/TransferRNA.pdb b/BuiltInMolecules/TransferRNA.pdb new file mode 100644 index 0000000..08cad2d --- /dev/null +++ b/BuiltInMolecules/TransferRNA.pdb @@ -0,0 +1,2565 @@ +HEADER AMINO-ACID TRANSPORT 06-NOV-87 4TRA +TITLE RESTRAINED REFINEMENT OF TWO CRYSTALLINE FORMS OF YEAST +TITLE 2 ASPARTIC ACID AND PHENYLALANINE TRANSFER RNA CRYSTALS +COMPND MOL_ID: 1; +COMPND 2 MOLECULE: TRNAPHE; +COMPND 3 CHAIN: A +SOURCE MOL_ID: 1; +SOURCE 2 ORGANISM_SCIENTIFIC: SACCHAROMYCES CEREVISIAE; +SOURCE 3 ORGANISM_COMMON: BAKER'S YEAST; +SOURCE 4 GENUS: SACCHAROMYCES; +SOURCE 5 SPECIES: CEREVISIAE +KEYWDS T-RNA, SINGLE STRAND, LOOPS +EXPDTA X-RAY DIFFRACTION +AUTHOR E.WESTHOF,P.DUMAS,D.MORAS +REVDAT 1 06-NOV-87 4TRA 0 +JRNL AUTH E.WESTHOF,P.DUMAS,D.MORAS +JRNL TITL RESTRAINED REFINEMENT OF TWO CRYSTALLINE FORMS OF +JRNL TITL 2 YEAST ASPARTIC ACID AND PHENYLALANINE TRANSFER RNA +JRNL TITL 3 CRYSTALS. +JRNL REF ACTA CRYSTALLOGR., SECT.A V. 44 112 1988 +JRNL REFN ASTM ACACEQ DK ISSN 0108-7673 +REMARK 1 +REMARK 1 REFERENCE 1 +REMARK 1 AUTH E.WESTHOF,P.DUMAS,D.MORAS +REMARK 1 TITL HYDRATION OF TRANSFER RNA MOLECULES. A +REMARK 1 TITL 2 CRYSTALLOGRAPHIC STUDY +REMARK 1 REF BIOCHIMIE V. 70 145 1988 +REMARK 1 REFN ASTM BICMBE FR ISSN 0300-9084 +REMARK 1 REFERENCE 2 +REMARK 1 AUTH J.L.SUSSMAN,S.R.HOLBROOK,R.W.WARRANT,G.M.CHURCH, +REMARK 1 AUTH 2 S.-H.KIM +REMARK 1 TITL CRYSTAL STRUCTURE OF YEAST PHENYLALANINE T-RNA. +REMARK 1 TITL 2 I.CRYSTALLOGRAPHIC REFINEMENT +REMARK 1 REF J.MOL.BIOL. V. 123 607 1978 +REMARK 1 REFN ASTM JMOBAK UK ISSN 0022-2836 +REMARK 1 REFERENCE 3 +REMARK 1 AUTH S.R.HOLBROOK,J.L.SUSSMAN,R.W.WARRANT,S.-H.KIM +REMARK 1 TITL CRYSTAL STRUCTURE OF YEAST PHENYLALANINE T-RNA. +REMARK 1 TITL 2 II.STRUCTURAL FEATURES AND FUNCTIONAL IMPLICATIONS +REMARK 1 REF J.MOL.BIOL. V. 123 631 1978 +REMARK 1 REFN ASTM JMOBAK UK ISSN 0022-2836 +REMARK 1 REFERENCE 4 +REMARK 1 AUTH S.-H.KIM +REMARK 1 TITL THREE DIMENSIONAL STRUCTURE OF T-RNA AND ITS +REMARK 1 TITL 2 FUNCTIONAL IMPLICATIONS +REMARK 1 REF ADV.ENZYMOL.RELAT.AREAS V. 46 279 1978 +REMARK 1 REF 2 MOL. BIOL. +REMARK 1 REFN ASTM AERAAD US ISSN 0065-258X +REMARK 1 REFERENCE 5 +REMARK 1 AUTH A.RICH,S.H.KIM +REMARK 1 TITL THE THREE-DIMENSIONAL STRUCTURE OF TRANSFER RNA +REMARK 1 REF SCI.AM. V. 238 52 1978 +REMARK 1 REFN ASTM SCAMAC US ISSN 0036-8733 +REMARK 1 REFERENCE 6 +REMARK 1 AUTH S.-H.KIM +REMARK 1 TITL CRYSTAL STRUCTURE OF YEAST PHENYLALANINE T-RNA, +REMARK 1 TITL 2 ITS CORRELATION TO THE SOLUTION STRUCTURE AND THE +REMARK 1 TITL 3 FUNCTIONAL IMPLICATIONS +REMARK 1 REF TRANSFER RNA 1978 +REMARK 1 PUBL MIT PRESS, CAMBRIDGE, MASSACHUSETTS +REMARK 1 REFN ISBN 0262-01056-9 +REMARK 1 REFERENCE 7 +REMARK 1 AUTH S.R.HOLBROOK,J.L.SUSSMAN,R.W.WARRANT,G.M.CHURCH, +REMARK 1 AUTH 2 S.-H.KIM +REMARK 1 TITL RNA-LIGAND INTERACTIONS. (1) MAGNESIUM BINDING +REMARK 1 TITL 2 SITES IN YEAST T-RNA-PHE +REMARK 1 REF NUCLEIC ACIDS RES. V. 4 2811 1977 +REMARK 1 REFN ASTM NARHAD UK ISSN 0305-1048 +REMARK 1 REFERENCE 8 +REMARK 1 AUTH J.L.SUSSMAN,S.-H.KIM +REMARK 1 TITL IDEALIZED ATOMIC COORDINATES OF YEAST +REMARK 1 TITL 2 PHENYLALANINE TRANSFER RNA +REMARK 1 REF BIOCHEM.BIOPHYS.RES.COMM. V. 68 89 1976 +REMARK 1 REFN ASTM BBRCA9 US ISSN 0006-291X +REMARK 1 REFERENCE 9 +REMARK 1 AUTH J.L.SUSSMAN,S.-H.KIM +REMARK 1 TITL THREE-DIMENSIONAL STRUCTURE OF A TRANSFER RNA IN +REMARK 1 TITL 2 TWO CRYSTAL FORMS +REMARK 1 REF SCIENCE V. 192 853 1976 +REMARK 1 REFN ASTM SCIEAS US ISSN 0036-8075 +REMARK 1 +REMARK 1 THE COORDINATES IN THIS ENTRY HAVE BEEN GENERATED FROM +REMARK 1 A NEW REFINEMENT OF PROTEIN DATA BANK ENTRY 6TNA. THE +REMARK 1 PAPER CITED ON THE *JRNL* RECORDS ABOVE GIVES A COMPARISON +REMARK 1 OF THE TWO ENTRIES AND A RATIONALE FOR THE NEW REFINEMENT +REMARK 1 BY THE RESTRAINED LEAST-SQUARES PROGRAMS *NUCLIN* AND +REMARK 1 *NUCLSQ* BASED ON *PROLSQ* (J.KONNERT, W.HENDRICKSON) +REMARK 1 AND REAL-SPACE FITTING WITH *FRODO* (T.A.JONES). +REMARK 1 THE RMS DEVIATION FROM IDEALITY OF THE BOND LENGTHS IS +REMARK 1 0.008 ANGSTROMS. THE RMS DEVIATION FROM IDEALITY OF THE +REMARK 1 BOND ANGLE DISTANCES IS 0.014 ANGSTROMS. ATOMS WITH THERMAL +REMARK 1 FACTORS WHICH CALCULATE LESS THAN 0.00 ARE ASSIGNED THIS +REMARK 1 VALUE. THIS IS THE LOWEST VALUE ALLOWED BY THE REFINEMENT +REMARK 1 PROGRAM. +REMARK 2 +REMARK 2 THE AXIAL SYSTEM OF THIS ENTRY IS ORTHOGONAL AND HAS ITS +REMARK 2 Y-AXIS APPROXIMATELY PERPENDICULAR TO THE BEST MOLECULAR +REMARK 2 PLANE. +REMARK 3 +REMARK 3 THE MODIFIED PURINE (Y-BASE) IN THIS TRNA IS COMMONLY +REMARK 3 CALLED *WYBUTOSINE*. THE SYSTEMATIC NAME FOR THE BASE PART +REMARK 3 OF THIS NUCLEOTIDE IS 1H-IMIDAZO(1,2-ALPHA)PURINE-7- +REMARK 3 BUTANOIC ACID,4,9-DIHYDRO-ALPHA-((METHOXYCARBONYL)AMINO)- +REMARK 3 4,6-DIMETHYL-9-OXO-METHYL ESTER. IN REALITY THE PRECEEDING +REMARK 3 IS A HIGHLY MODIFIED GUANOSINE. IT IS LINKED TO THE RIBOSE +REMARK 3 VIA THE STANDARD N9-C1* BOND. +REMARK 4 +REMARK 4 THE ASSIGNMENTS OF THE HETATM RESIDUES WITH SERIAL NUMBERS +REMARK 4 5 THROUGH 68 AS WATER MOLECULES ARE TENTATIVE BECAUSE IT +REMARK 4 CANNOT BE DISTINGUISHED WHICH ARE BOUND WATERS AND WHICH +REMARK 4 ARE BOUND IONS. +REMARK 2 +REMARK 2 RESOLUTION. 3.00 ANGSTROMS. +REMARK 3 +REMARK 3 REFINEMENT. +REMARK 3 PROGRAM : NUCLSQ +REMARK 3 AUTHORS : WESTHOF,DUMAS,MORAS +REMARK 3 +REMARK 3 DATA USED IN REFINEMENT. +REMARK 3 RESOLUTION RANGE HIGH (ANGSTROMS) : 3.00 +REMARK 3 RESOLUTION RANGE LOW (ANGSTROMS) : 10.00 +REMARK 3 DATA CUTOFF (SIGMA(F)) : 2.000 +REMARK 3 COMPLETENESS FOR RANGE (%) : NULL +REMARK 3 NUMBER OF REFLECTIONS : 4508 +REMARK 3 +REMARK 3 FIT TO DATA USED IN REFINEMENT. +REMARK 3 CROSS-VALIDATION METHOD : NULL +REMARK 3 FREE R VALUE TEST SET SELECTION : NULL +REMARK 3 R VALUE (WORKING + TEST SET) : 0.172 +REMARK 3 R VALUE (WORKING SET) : NULL +REMARK 3 FREE R VALUE : NULL +REMARK 3 FREE R VALUE TEST SET SIZE (%) : NULL +REMARK 3 FREE R VALUE TEST SET COUNT : NULL +REMARK 3 +REMARK 3 FIT/AGREEMENT OF MODEL WITH ALL DATA. +REMARK 3 R VALUE (WORKING + TEST SET, NO CUTOFF) : NULL +REMARK 3 R VALUE (WORKING SET, NO CUTOFF) : NULL +REMARK 3 FREE R VALUE (NO CUTOFF) : NULL +REMARK 3 FREE R VALUE TEST SET SIZE (%, NO CUTOFF) : NULL +REMARK 3 FREE R VALUE TEST SET COUNT (NO CUTOFF) : NULL +REMARK 3 TOTAL NUMBER OF REFLECTIONS (NO CUTOFF) : NULL +REMARK 3 +REMARK 3 NUMBER OF NON-HYDROGEN ATOMS USED IN REFINEMENT. +REMARK 3 PROTEIN ATOMS : 0 +REMARK 3 NUCLEIC ACID ATOMS : 1652 +REMARK 3 HETEROGEN ATOMS : 23 +REMARK 3 SOLVENT ATOMS : 104 +REMARK 3 +REMARK 3 B VALUES. +REMARK 3 FROM WILSON PLOT (A**2) : NULL +REMARK 3 MEAN B VALUE (OVERALL, A**2) : NULL +REMARK 3 OVERALL ANISOTROPIC B VALUE. +REMARK 3 B11 (A**2) : NULL +REMARK 3 B22 (A**2) : NULL +REMARK 3 B33 (A**2) : NULL +REMARK 3 B12 (A**2) : NULL +REMARK 3 B13 (A**2) : NULL +REMARK 3 B23 (A**2) : NULL +REMARK 3 +REMARK 3 ESTIMATED COORDINATE ERROR. +REMARK 3 ESD FROM LUZZATI PLOT (A) : NULL +REMARK 3 ESD FROM SIGMAA (A) : NULL +REMARK 3 LOW RESOLUTION CUTOFF (A) : NULL +REMARK 3 +REMARK 3 RMS DEVIATIONS FROM IDEAL VALUES. +REMARK 3 DISTANCE RESTRAINTS. RMS SIGMA +REMARK 3 SUGAR-BASE BOND DISTANCE (A) : NULL ; NULL +REMARK 3 SUGAR-BASE BOND ANGLE DISTANCE (A) : NULL ; NULL +REMARK 3 PHOSPHATE BONDS DISTANCE (A) : NULL ; NULL +REMARK 3 PHOSPHATE BOND ANGLE, H-BOND (A) : NULL ; NULL +REMARK 3 +REMARK 3 PLANE RESTRAINT (A) : NULL ; NULL +REMARK 3 CHIRAL-CENTER RESTRAINT (A**3) : NULL ; NULL +REMARK 3 +REMARK 3 NON-BONDED CONTACT RESTRAINTS. +REMARK 3 SINGLE TORSION CONTACT (A) : NULL ; NULL +REMARK 3 MULTIPLE TORSION CONTACT (A) : NULL ; NULL +REMARK 3 +REMARK 3 ISOTROPIC THERMAL FACTOR RESTRAINTS. RMS SIGMA +REMARK 3 SUGAR-BASE BONDS (A**2) : NULL ; NULL +REMARK 3 SUGAR-BASE ANGLES (A**2) : NULL ; NULL +REMARK 3 PHOSPHATE BONDS (A**2) : NULL ; NULL +REMARK 3 PHOSPHATE BOND ANGLE, H-BOND (A**2) : NULL ; NULL +REMARK 3 +REMARK 3 OTHER REFINEMENT REMARKS: NULL +REMARK 4 +REMARK 4 4TRA COMPLIES WITH FORMAT V. 3.0, 1-DEC-2006 +REMARK 4 +REMARK 4 THIS IS THE REMEDIATED VERSION OF THIS PDB ENTRY. +REMARK 4 REMEDIATED DATA FILE REVISION 3.100 (2007-03-18) +REMARK 200 +REMARK 200 EXPERIMENTAL DETAILS +REMARK 200 EXPERIMENT TYPE : X-RAY DIFFRACTION +REMARK 200 DATE OF DATA COLLECTION : NULL +REMARK 200 TEMPERATURE (KELVIN) : NULL +REMARK 200 PH : NULL +REMARK 200 NUMBER OF CRYSTALS USED : NULL +REMARK 200 +REMARK 200 SYNCHROTRON (Y/N) : NULL +REMARK 200 RADIATION SOURCE : NULL +REMARK 200 BEAMLINE : NULL +REMARK 200 X-RAY GENERATOR MODEL : NULL +REMARK 200 MONOCHROMATIC OR LAUE (M/L) : NULL +REMARK 200 WAVELENGTH OR RANGE (A) : NULL +REMARK 200 MONOCHROMATOR : NULL +REMARK 200 OPTICS : NULL +REMARK 200 +REMARK 200 DETECTOR TYPE : NULL +REMARK 200 DETECTOR MANUFACTURER : NULL +REMARK 200 INTENSITY-INTEGRATION SOFTWARE : NULL +REMARK 200 DATA SCALING SOFTWARE : NULL +REMARK 200 +REMARK 200 NUMBER OF UNIQUE REFLECTIONS : NULL +REMARK 200 RESOLUTION RANGE HIGH (A) : NULL +REMARK 200 RESOLUTION RANGE LOW (A) : NULL +REMARK 200 REJECTION CRITERIA (SIGMA(I)) : NULL +REMARK 200 +REMARK 200 OVERALL. +REMARK 200 COMPLETENESS FOR RANGE (%) : NULL +REMARK 200 DATA REDUNDANCY : NULL +REMARK 200 R MERGE (I) : NULL +REMARK 200 R SYM (I) : NULL +REMARK 200 FOR THE DATA SET : NULL +REMARK 200 +REMARK 200 IN THE HIGHEST RESOLUTION SHELL. +REMARK 200 HIGHEST RESOLUTION SHELL, RANGE HIGH (A) : NULL +REMARK 200 HIGHEST RESOLUTION SHELL, RANGE LOW (A) : NULL +REMARK 200 COMPLETENESS FOR SHELL (%) : NULL +REMARK 200 DATA REDUNDANCY IN SHELL : NULL +REMARK 200 R MERGE FOR SHELL (I) : NULL +REMARK 200 R SYM FOR SHELL (I) : NULL +REMARK 200 FOR SHELL : NULL +REMARK 200 +REMARK 200 DIFFRACTION PROTOCOL: NULL +REMARK 200 METHOD USED TO DETERMINE THE STRUCTURE: NULL +REMARK 200 SOFTWARE USED: NULL +REMARK 200 STARTING MODEL: NULL +REMARK 200 +REMARK 200 REMARK: NULL +REMARK 280 +REMARK 280 CRYSTAL +REMARK 280 SOLVENT CONTENT, VS (%): 58.84 +REMARK 280 MATTHEWS COEFFICIENT, VM (ANGSTROMS**3/DA): 2.99 +REMARK 280 +REMARK 280 CRYSTALLIZATION CONDITIONS: NULL +REMARK 290 +REMARK 290 CRYSTALLOGRAPHIC SYMMETRY +REMARK 290 SYMMETRY OPERATORS FOR SPACE GROUP: P 21 2 21 +REMARK 290 +REMARK 290 SYMOP SYMMETRY +REMARK 290 NNNMMM OPERATOR +REMARK 290 1555 X,Y,Z +REMARK 290 2555 -X,Y,-Z +REMARK 290 3555 1/2-X,-Y,1/2+Z +REMARK 290 4555 1/2+X,-Y,1/2-Z +REMARK 290 +REMARK 290 WHERE NNN -> OPERATOR NUMBER +REMARK 290 MMM -> TRANSLATION VECTOR +REMARK 290 +REMARK 290 CRYSTALLOGRAPHIC SYMMETRY TRANSFORMATIONS +REMARK 290 THE FOLLOWING TRANSFORMATIONS OPERATE ON THE ATOM/HETATM +REMARK 290 RECORDS IN THIS ENTRY TO PRODUCE CRYSTALLOGRAPHICALLY +REMARK 290 RELATED MOLECULES. +REMARK 290 SMTRY1 1 1.000000 0.000000 0.000000 0.00000 +REMARK 290 SMTRY2 1 0.000000 1.000000 0.000000 0.00000 +REMARK 290 SMTRY3 1 0.000000 0.000000 1.000000 0.00000 +REMARK 290 SMTRY1 2 -0.620236 -0.699553 -0.354730 0.00000 +REMARK 290 SMTRY2 2 -0.699893 0.289252 0.653755 0.00000 +REMARK 290 SMTRY3 2 -0.354342 0.652724 -0.669016 0.00000 +REMARK 290 SMTRY1 3 -0.896877 -0.153844 0.414393 32.64117 +REMARK 290 SMTRY2 3 -0.154329 -0.769764 -0.620161 -19.25288 +REMARK 290 SMTRY3 3 0.414750 -0.618744 0.666641 72.91287 +REMARK 290 SMTRY1 4 0.517112 0.853397 -0.059664 32.64117 +REMARK 290 SMTRY2 4 0.854222 -0.519488 -0.033594 -19.25288 +REMARK 290 SMTRY3 4 -0.060407 -0.033980 -0.997624 72.91287 +REMARK 290 +REMARK 290 REMARK: NULL +REMARK 300 +REMARK 300 BIOMOLECULE: 1 +REMARK 300 THIS ENTRY CONTAINS THE CRYSTALLOGRAPHIC ASYMMETRIC UNIT +REMARK 300 WHICH CONSISTS OF 1 CHAIN(S). SEE REMARK 350 FOR +REMARK 300 INFORMATION ON GENERATING THE BIOLOGICAL MOLECULE(S). +REMARK 350 +REMARK 350 GENERATING THE BIOMOLECULE +REMARK 350 COORDINATES FOR A COMPLETE MULTIMER REPRESENTING THE KNOWN +REMARK 350 BIOLOGICALLY SIGNIFICANT OLIGOMERIZATION STATE OF THE +REMARK 350 MOLECULE CAN BE GENERATED BY APPLYING BIOMT TRANSFORMATIONS +REMARK 350 GIVEN BELOW. BOTH NON-CRYSTALLOGRAPHIC AND +REMARK 350 CRYSTALLOGRAPHIC OPERATIONS ARE GIVEN. +REMARK 350 +REMARK 350 BIOMOLECULE: 1 +REMARK 350 APPLY THE FOLLOWING TO CHAINS: A +REMARK 350 BIOMT1 1 1.000000 0.000000 0.000000 0.00000 +REMARK 350 BIOMT2 1 0.000000 1.000000 0.000000 0.00000 +REMARK 350 BIOMT3 1 0.000000 0.000000 1.000000 0.00000 +SEQRES 1 A 76 G C G G A U U U A 2MG C U C +SEQRES 2 A 76 A G H2U H2U G G G A G A G C M2G +SEQRES 3 A 76 C C A G A OMC U OMG A A YG A PSU +SEQRES 4 A 76 5MC U G G A G 7MG U C 5MC U G U +SEQRES 5 A 76 G 5MU PSU C G 1MA U C C A C A G +SEQRES 6 A 76 A A U U C G C A C C A +MODRES 4TRA 2MG A 10 G 2N-METHYLGUANOSINE-5'-MONOPHOSPHATE +MODRES 4TRA H2U A 16 U 5,6-DIHYDROURIDINE-5'-MONOPHOSPHATE +MODRES 4TRA H2U A 17 U 5,6-DIHYDROURIDINE-5'-MONOPHOSPHATE +MODRES 4TRA M2G A 26 G N2-DIMETHYLGUANOSINE-5'-MONOPHOSPHATE +MODRES 4TRA OMC A 32 C O2'-METHYLYCYTIDINE-5'-MONOPHOSPHATE +MODRES 4TRA OMG A 34 G O2'-METHYLGUANOSINE-5'-MONOPHOSPHATE +MODRES 4TRA YG A 37 G WYBUTOSINE +MODRES 4TRA PSU A 39 U PSEUDOURIDINE-5'-MONOPHOSPHATE +MODRES 4TRA 5MC A 40 C 5-METHYLCYTIDINE-5'-MONOPHOSPHATE +MODRES 4TRA 7MG A 46 G +MODRES 4TRA 5MC A 49 C 5-METHYLCYTIDINE-5'-MONOPHOSPHATE +MODRES 4TRA 5MU A 54 U 5-METHYLURIDINE 5'-MONOPHOSPHATE +MODRES 4TRA PSU A 55 U PSEUDOURIDINE-5'-MONOPHOSPHATE +MODRES 4TRA 1MA A 58 A +HET 2MG A 10 24 +HET H2U A 16 20 +HET H2U A 17 20 +HET M2G A 26 25 +HET OMC A 32 21 +HET OMG A 34 24 +HET YG A 37 39 +HET PSU A 39 20 +HET 5MC A 40 21 +HET 7MG A 46 24 +HET 5MC A 49 21 +HET 5MU A 54 21 +HET PSU A 55 20 +HET 1MA A 58 23 +HET MG 77 1 +HET MG 78 1 +HET MG 79 1 +HET MG 80 1 +HETNAM 2MG 2N-METHYLGUANOSINE-5'-MONOPHOSPHATE +HETNAM H2U 5,6-DIHYDROURIDINE-5'-MONOPHOSPHATE +HETNAM M2G N2-DIMETHYLGUANOSINE-5'-MONOPHOSPHATE +HETNAM OMC O2'-METHYLYCYTIDINE-5'-MONOPHOSPHATE +HETNAM OMG O2'-METHYLGUANOSINE-5'-MONOPHOSPHATE +HETNAM YG WYBUTOSINE +HETNAM PSU PSEUDOURIDINE-5'-MONOPHOSPHATE +HETNAM 5MC 5-METHYLCYTIDINE-5'-MONOPHOSPHATE +HETNAM 7MG 7N-METHYL-8-HYDROGUANOSINE-5'-MONOPHOSPHATE +HETNAM 5MU 5-METHYLURIDINE 5'-MONOPHOSPHATE +HETNAM 1MA 6-HYDRO-1-METHYLADENOSINE-5'-MONOPHOSPHATE +HETNAM MG MAGNESIUM ION +HETSYN YG Y-BASE; 1H-IMIDAZO(1,2-ALPHA)PURINE-7-BUTANOIC ACID,4, +HETSYN 2 YG 9-DIHYDRO-ALPHA-((METHOXYCARBONYL)AMINO)-4,6-DIMETHYL- +HETSYN 3 YG 9-OXO-METHYL ESTER +FORMUL 1 2MG C11 H16 N5 O8 P +FORMUL 1 H2U 2(C9 H15 N2 O9 P) +FORMUL 1 M2G C12 H18 N5 O8 P +FORMUL 1 OMC C10 H16 N3 O8 P +FORMUL 1 OMG C11 H16 N5 O8 P +FORMUL 1 YG C21 H29 N6 O12 P +FORMUL 1 PSU 2(C9 H13 N2 O9 P) +FORMUL 1 5MC 2(C10 H16 N3 O8 P) +FORMUL 1 7MG C11 H18 N5 O8 P +FORMUL 1 5MU C10 H15 N2 O9 P +FORMUL 1 1MA C11 H18 N5 O7 P +FORMUL 2 MG 4(MG 2+) +FORMUL 6 HOH *123(H2 O) +LINK P 2MG A 10 O3' A A 9 +LINK O3' 2MG A 10 P C A 11 +LINK P H2U A 16 O3' G A 15 +LINK O3' H2U A 16 P H2U A 17 +LINK O3' H2U A 17 P G A 18 +LINK P M2G A 26 O3' C A 25 +LINK O3' M2G A 26 P C A 27 +LINK P OMC A 32 O3' A A 31 +LINK O3' OMC A 32 P U A 33 +LINK P OMG A 34 O3' U A 33 +LINK O3' OMG A 34 P A A 35 +LINK P YG A 37 O3' A A 36 +LINK O3' YG A 37 P A A 38 +LINK P PSU A 39 O3' A A 38 +LINK O3' PSU A 39 P 5MC A 40 +LINK O3' 5MC A 40 P U A 41 +LINK P 7MG A 46 O3' G A 45 +LINK O3' 7MG A 46 P U A 47 +LINK P 5MC A 49 O3' C A 48 +LINK O3' 5MC A 49 P U A 50 +LINK P 5MU A 54 O3' G A 53 +LINK O3' 5MU A 54 P PSU A 55 +LINK O3' PSU A 55 P C A 56 +LINK P 1MA A 58 O3' G A 57 +LINK O3' 1MA A 58 P U A 59 +LINK MG MG 77 O HOH 188 +LINK MG MG 77 O HOH 186 +LINK MG MG 77 O HOH 189 +LINK MG MG 77 O HOH 185 +LINK MG MG 77 O HOH 187 +LINK MG MG 78 O HOH 190 +LINK MG MG 78 O HOH 192 +LINK MG MG 78 O HOH 191 +LINK MG MG 79 O HOH 193 +LINK MG MG 79 O HOH 195 +LINK MG MG 79 O HOH 198 +LINK MG MG 79 O HOH 196 +LINK MG MG 79 O HOH 197 +LINK MG MG 79 O HOH 194 +LINK MG MG 80 O HOH 203 +LINK MG MG 80 O HOH 199 +LINK MG MG 80 O HOH 202 +LINK MG MG 80 O HOH 200 +LINK MG MG 80 O HOH 201 +CRYST1 33.000 56.000 161.000 90.00 90.00 90.00 P 21 2 21 4 +ORIGX1 1.000000 0.000000 0.000000 0.00000 +ORIGX2 0.000000 1.000000 0.000000 0.00000 +ORIGX3 0.000000 0.000000 1.000000 0.00000 +SCALE1 0.026394 0.014847 -0.001038 -0.30448 +SCALE2 -0.007782 0.014335 0.007269 -0.06000 +SCALE3 0.001411 -0.002105 0.005670 -0.00907 +ATOM 1 OP3 G A 1 23.310 31.674 59.557 0.55 43.73 O +ATOM 2 P G A 1 23.956 32.681 60.656 0.55 44.71 P +ATOM 3 OP1 G A 1 25.382 32.748 60.220 0.55 44.92 O +ATOM 4 OP2 G A 1 23.290 34.019 60.607 0.55 42.38 O +ATOM 5 O5' G A 1 23.713 31.950 62.046 0.86 35.51 O +ATOM 6 C5' G A 1 23.820 32.681 63.290 1.00 33.29 C +ATOM 7 C4' G A 1 23.204 31.787 64.357 1.00 34.35 C +ATOM 8 O4' G A 1 21.841 32.113 64.535 1.00 30.75 O +ATOM 9 C3' G A 1 23.197 30.297 63.985 1.00 35.86 C +ATOM 10 O3' G A 1 24.453 29.684 64.244 1.00 40.42 O +ATOM 11 C2' G A 1 22.058 29.763 64.858 1.00 31.69 C +ATOM 12 O2' G A 1 22.581 29.549 66.183 1.00 28.80 O +ATOM 13 C1' G A 1 21.092 30.921 64.842 1.00 29.94 C +ATOM 14 N9 G A 1 20.026 30.758 63.840 1.00 27.08 N +ATOM 15 C8 G A 1 20.056 31.118 62.515 1.00 28.25 C +ATOM 16 N7 G A 1 18.947 30.859 61.868 1.00 28.56 N +ATOM 17 C5 G A 1 18.127 30.286 62.838 1.00 25.31 C +ATOM 18 C6 G A 1 16.802 29.785 62.773 1.00 26.75 C +ATOM 19 O6 G A 1 16.065 29.757 61.787 1.00 25.45 O +ATOM 20 N1 G A 1 16.322 29.291 63.953 1.00 25.73 N +ATOM 21 C2 G A 1 17.065 29.279 65.101 1.00 25.07 C +ATOM 22 N2 G A 1 16.455 28.762 66.167 1.00 26.29 N +ATOM 23 N3 G A 1 18.314 29.729 65.214 1.00 23.17 N +ATOM 24 C4 G A 1 18.780 30.218 64.050 1.00 23.19 C +ATOM 25 P C A 2 25.056 28.470 63.387 1.00 49.37 P +ATOM 26 OP1 C A 2 26.485 28.740 63.000 1.00 43.76 O +ATOM 27 OP2 C A 2 24.150 28.397 62.208 1.00 44.42 O +ATOM 28 O5' C A 2 24.952 27.239 64.406 1.00 39.87 O +ATOM 29 C5' C A 2 24.243 26.058 63.953 1.00 37.41 C +ATOM 30 C4' C A 2 23.111 25.799 64.923 1.00 37.41 C +ATOM 31 O4' C A 2 22.158 26.851 64.939 1.00 36.47 O +ATOM 32 C3' C A 2 22.288 24.557 64.535 1.00 37.29 C +ATOM 33 O3' C A 2 22.974 23.343 64.890 1.00 40.61 O +ATOM 34 C2' C A 2 20.982 24.821 65.262 1.00 36.80 C +ATOM 35 O2' C A 2 21.178 24.461 66.636 1.00 39.77 O +ATOM 36 C1' C A 2 20.849 26.311 65.149 1.00 35.09 C +ATOM 37 N1 C A 2 19.989 26.631 63.985 1.00 32.55 N +ATOM 38 C2 C A 2 18.823 25.912 63.808 1.00 31.22 C +ATOM 39 O2 C A 2 18.490 25.024 64.583 1.00 29.90 O +ATOM 40 N3 C A 2 18.047 26.215 62.725 1.00 30.98 N +ATOM 41 C4 C A 2 18.394 27.188 61.836 1.00 30.39 C +ATOM 42 N4 C A 2 17.594 27.441 60.801 1.00 29.45 N +ATOM 43 C5 C A 2 19.596 27.924 62.030 1.00 30.32 C +ATOM 44 C6 C A 2 20.342 27.610 63.096 1.00 30.25 C +ATOM 45 P G A 3 23.497 22.443 63.630 1.00 48.40 P +ATOM 46 OP1 G A 3 24.793 21.774 63.953 1.00 40.08 O +ATOM 47 OP2 G A 3 23.587 23.444 62.531 1.00 39.94 O +ATOM 48 O5' G A 3 22.301 21.381 63.484 1.00 37.08 O +ATOM 49 C5' G A 3 21.642 21.077 64.729 1.00 35.89 C +ATOM 50 C4' G A 3 20.216 20.672 64.438 1.00 37.55 C +ATOM 51 O4' G A 3 19.406 21.813 64.163 1.00 33.08 O +ATOM 52 C3' G A 3 20.056 19.784 63.193 1.00 35.85 C +ATOM 53 O3' G A 3 20.386 18.429 63.468 1.00 36.61 O +ATOM 54 C2' G A 3 18.597 20.014 62.822 1.00 33.59 C +ATOM 55 O2' G A 3 17.814 19.177 63.662 1.00 32.67 O +ATOM 56 C1' G A 3 18.400 21.482 63.193 1.00 32.42 C +ATOM 57 N9 G A 3 18.470 22.319 61.981 1.00 32.53 N +ATOM 58 C8 G A 3 19.446 23.180 61.561 1.00 32.25 C +ATOM 59 N7 G A 3 19.177 23.775 60.430 1.00 30.37 N +ATOM 60 C5 G A 3 17.927 23.275 60.074 1.00 29.28 C +ATOM 61 C6 G A 3 17.095 23.534 58.959 1.00 29.92 C +ATOM 62 O6 G A 3 17.308 24.293 58.005 1.00 28.16 O +ATOM 63 N1 G A 3 15.912 22.837 58.959 1.00 30.27 N +ATOM 64 C2 G A 3 15.579 21.971 59.961 1.00 33.02 C +ATOM 65 N2 G A 3 14.390 21.369 59.799 1.00 35.92 N +ATOM 66 N3 G A 3 16.319 21.690 61.028 1.00 28.38 N +ATOM 67 C4 G A 3 17.478 22.381 61.028 1.00 30.04 C +ATOM 68 P G A 4 21.215 17.513 62.450 1.00 47.69 P +ATOM 69 OP1 G A 4 22.161 16.596 63.177 1.00 46.13 O +ATOM 70 OP2 G A 4 21.925 18.474 61.561 1.00 43.36 O +ATOM 71 O5' G A 4 20.059 16.681 61.723 1.00 37.46 O +ATOM 72 C5' G A 4 20.173 16.534 60.284 1.00 31.08 C +ATOM 73 C4' G A 4 18.767 16.534 59.719 1.00 25.17 C +ATOM 74 O4' G A 4 18.144 17.788 59.929 1.00 22.23 O +ATOM 75 C3' G A 4 18.707 16.321 58.215 1.00 25.88 C +ATOM 76 O3' G A 4 18.850 14.949 57.828 1.00 27.06 O +ATOM 77 C2' G A 4 17.348 16.917 57.860 1.00 26.15 C +ATOM 78 O2' G A 4 16.378 15.916 58.167 1.00 27.28 O +ATOM 79 C1' G A 4 17.278 18.075 58.846 1.00 23.13 C +ATOM 80 N9 G A 4 17.694 19.289 58.119 1.00 19.06 N +ATOM 81 C8 G A 4 18.750 20.115 58.393 1.00 19.00 C +ATOM 82 N7 G A 4 18.873 21.111 57.553 1.00 17.81 N +ATOM 83 C5 G A 4 17.817 20.925 56.664 1.00 18.66 C +ATOM 84 C6 G A 4 17.408 21.678 55.516 1.00 20.16 C +ATOM 85 O6 G A 4 17.927 22.702 55.064 1.00 19.43 O +ATOM 86 N1 G A 4 16.305 21.172 54.886 1.00 18.14 N +ATOM 87 C2 G A 4 15.666 20.048 55.306 1.00 17.90 C +ATOM 88 N2 G A 4 14.603 19.694 54.579 1.00 19.56 N +ATOM 89 N3 G A 4 16.005 19.312 56.357 1.00 16.17 N +ATOM 90 C4 G A 4 17.088 19.806 56.987 1.00 16.65 C +ATOM 91 P A A 5 20.089 14.640 56.809 1.00 28.41 P +ATOM 92 OP1 A A 5 20.439 13.184 56.906 1.00 32.32 O +ATOM 93 OP2 A A 5 21.169 15.505 57.343 1.00 26.68 O +ATOM 94 O5' A A 5 19.526 15.016 55.371 1.00 16.70 O +ATOM 95 C5' A A 5 18.627 14.066 54.724 1.00 16.63 C +ATOM 96 C4' A A 5 17.628 14.887 53.949 1.00 16.22 C +ATOM 97 O4' A A 5 17.358 16.090 54.644 1.00 17.33 O +ATOM 98 C3' A A 5 18.124 15.337 52.575 1.00 17.89 C +ATOM 99 O3' A A 5 18.011 14.330 51.573 1.00 15.55 O +ATOM 100 C2' A A 5 17.248 16.562 52.300 1.00 13.53 C +ATOM 101 O2' A A 5 16.005 16.073 51.815 1.00 11.51 O +ATOM 102 C1' A A 5 17.131 17.130 53.690 1.00 14.94 C +ATOM 103 N9 A A 5 18.161 18.170 53.836 1.00 14.40 N +ATOM 104 C8 A A 5 19.193 18.181 54.757 1.00 11.88 C +ATOM 105 N7 A A 5 19.963 19.233 54.660 1.00 12.69 N +ATOM 106 C5 A A 5 19.400 19.958 53.609 1.00 13.64 C +ATOM 107 C6 A A 5 19.770 21.195 53.028 1.00 17.30 C +ATOM 108 N6 A A 5 20.812 21.909 53.415 1.00 15.64 N +ATOM 109 N1 A A 5 18.967 21.605 51.993 1.00 18.26 N +ATOM 110 C2 A A 5 17.894 20.875 51.589 1.00 17.81 C +ATOM 111 N3 A A 5 17.491 19.705 52.090 1.00 14.01 N +ATOM 112 C4 A A 5 18.291 19.312 53.108 1.00 12.30 C +ATOM 113 P U A 6 19.410 13.853 50.910 1.00 17.85 P +ATOM 114 OP1 U A 6 19.257 12.441 50.425 1.00 23.85 O +ATOM 115 OP2 U A 6 20.412 14.027 51.993 1.00 8.17 O +ATOM 116 O5' U A 6 19.583 14.865 49.682 1.00 6.78 O +ATOM 117 C5' U A 6 18.500 14.893 48.728 1.00 5.01 C +ATOM 118 C4' U A 6 18.600 16.219 48.001 1.00 6.97 C +ATOM 119 O4' U A 6 18.487 17.299 48.922 1.00 7.21 O +ATOM 120 C3' U A 6 19.939 16.444 47.306 1.00 8.35 C +ATOM 121 O3' U A 6 20.003 15.753 46.062 1.00 6.95 O +ATOM 122 C2' U A 6 19.979 17.962 47.193 1.00 1.29 C +ATOM 123 O2' U A 6 19.200 18.305 46.062 1.00 1.41 O +ATOM 124 C1' U A 6 19.303 18.401 48.486 1.00 2.21 C +ATOM 125 N1 U A 6 20.302 18.778 49.504 1.00 1.08 N +ATOM 126 C2 U A 6 21.115 19.862 49.197 1.00 2.33 C +ATOM 127 O2 U A 6 20.999 20.470 48.130 1.00 0.00 O +ATOM 128 N3 U A 6 22.051 20.234 50.118 1.00 0.00 N +ATOM 129 C4 U A 6 22.201 19.581 51.298 1.00 0.00 C +ATOM 130 O4 U A 6 23.094 20.003 52.090 1.00 1.46 O +ATOM 131 C5 U A 6 21.355 18.468 51.605 1.00 2.95 C +ATOM 132 C6 U A 6 20.442 18.114 50.684 1.00 0.36 C +ATOM 133 P U A 7 21.388 15.078 45.577 1.00 8.92 P +ATOM 134 OP1 U A 7 21.145 14.027 44.542 1.00 13.89 O +ATOM 135 OP2 U A 7 21.998 14.550 46.821 1.00 12.32 O +ATOM 136 O5' U A 7 22.125 16.360 44.946 1.00 15.19 O +ATOM 137 C5' U A 7 21.368 17.018 43.896 1.00 10.20 C +ATOM 138 C4' U A 7 22.038 18.328 43.557 1.00 3.40 C +ATOM 139 O4' U A 7 22.174 19.126 44.736 1.00 3.48 O +ATOM 140 C3' U A 7 23.470 18.181 42.991 1.00 0.00 C +ATOM 141 O3' U A 7 23.693 19.160 41.940 1.00 3.91 O +ATOM 142 C2' U A 7 24.336 18.440 44.203 1.00 0.00 C +ATOM 143 O2' U A 7 25.595 18.896 43.815 1.00 0.00 O +ATOM 144 C1' U A 7 23.547 19.486 44.946 1.00 0.00 C +ATOM 145 N1 U A 7 23.937 19.486 46.353 1.00 0.00 N +ATOM 146 C2 U A 7 24.699 20.543 46.821 1.00 0.00 C +ATOM 147 O2 U A 7 25.036 21.448 46.062 1.00 0.00 O +ATOM 148 N3 U A 7 25.056 20.543 48.130 1.00 0.00 N +ATOM 149 C4 U A 7 24.689 19.548 48.987 1.00 0.00 C +ATOM 150 O4 U A 7 25.059 19.615 50.199 1.00 0.00 O +ATOM 151 C5 U A 7 23.903 18.463 48.502 1.00 4.66 C +ATOM 152 C6 U A 7 23.560 18.479 47.193 1.00 1.45 C +ATOM 153 P U A 8 23.384 18.637 40.437 1.00 6.43 P +ATOM 154 OP1 U A 8 23.787 19.683 39.435 1.00 10.99 O +ATOM 155 OP2 U A 8 21.938 18.333 40.502 1.00 10.90 O +ATOM 156 O5' U A 8 24.333 17.355 40.292 1.00 6.66 O +ATOM 157 C5' U A 8 25.705 17.591 39.872 1.00 3.30 C +ATOM 158 C4' U A 8 26.118 16.439 38.999 1.00 0.00 C +ATOM 159 O4' U A 8 25.945 15.224 39.710 1.00 0.00 O +ATOM 160 C3' U A 8 25.286 16.264 37.738 1.00 0.00 C +ATOM 161 O3' U A 8 25.662 17.102 36.639 1.00 0.00 O +ATOM 162 C2' U A 8 25.519 14.780 37.431 1.00 0.00 C +ATOM 163 O2' U A 8 26.795 14.730 36.801 1.00 0.00 O +ATOM 164 C1' U A 8 25.572 14.184 38.805 1.00 0.00 C +ATOM 165 N1 U A 8 24.240 13.673 39.177 1.00 0.25 N +ATOM 166 C2 U A 8 23.733 12.576 38.498 1.00 0.00 C +ATOM 167 O2 U A 8 24.380 12.031 37.609 1.00 0.53 O +ATOM 168 N3 U A 8 22.498 12.127 38.870 1.00 0.00 N +ATOM 169 C4 U A 8 21.765 12.717 39.855 1.00 2.78 C +ATOM 170 O4 U A 8 20.622 12.228 40.114 1.00 1.19 O +ATOM 171 C5 U A 8 22.298 13.853 40.534 1.00 0.41 C +ATOM 172 C6 U A 8 23.514 14.280 40.163 1.00 0.00 C +ATOM 173 P A A 9 24.553 17.681 35.621 1.00 0.00 P +ATOM 174 OP1 A A 9 24.400 19.165 35.815 1.00 12.98 O +ATOM 175 OP2 A A 9 23.317 16.950 35.977 1.00 6.30 O +ATOM 176 O5' A A 9 25.092 17.361 34.167 1.00 0.00 O +ATOM 177 C5' A A 9 26.245 18.092 33.649 1.00 3.31 C +ATOM 178 C4' A A 9 25.962 18.198 32.162 1.00 4.34 C +ATOM 179 O4' A A 9 25.992 16.894 31.597 1.00 6.42 O +ATOM 180 C3' A A 9 24.583 18.789 31.855 1.00 6.32 C +ATOM 181 O3' A A 9 24.616 19.576 30.643 1.00 10.36 O +ATOM 182 C2' A A 9 23.743 17.546 31.645 1.00 3.67 C +ATOM 183 O2' A A 9 22.611 17.799 30.869 1.00 7.65 O +ATOM 184 C1' A A 9 24.739 16.619 30.966 1.00 2.02 C +ATOM 185 N9 A A 9 24.223 15.247 31.128 1.00 0.81 N +ATOM 186 C8 A A 9 23.204 14.814 31.936 1.00 1.30 C +ATOM 187 N7 A A 9 22.961 13.532 31.839 1.00 2.89 N +ATOM 188 C5 A A 9 23.877 13.088 30.886 1.00 3.22 C +ATOM 189 C6 A A 9 24.120 11.806 30.352 1.00 1.42 C +ATOM 190 N6 A A 9 23.454 10.715 30.692 1.00 0.00 N +ATOM 191 N1 A A 9 25.122 11.750 29.415 1.00 5.85 N +ATOM 192 C2 A A 9 25.825 12.858 29.059 1.00 5.07 C +ATOM 193 N3 A A 9 25.649 14.094 29.528 1.00 5.13 N +ATOM 194 C4 A A 9 24.656 14.139 30.449 1.00 5.09 C +HETATM 195 P 2MG A 10 25.129 21.094 30.562 1.00 9.42 P +HETATM 196 OP1 2MG A 10 25.615 21.420 29.172 1.00 0.00 O +HETATM 197 OP2 2MG A 10 26.212 21.150 31.580 1.00 8.19 O +HETATM 198 O5' 2MG A 10 23.873 22.004 30.934 1.00 7.35 O +HETATM 199 C5' 2MG A 10 23.823 23.343 30.320 1.00 13.13 C +HETATM 200 C4' 2MG A 10 22.401 23.517 29.835 1.00 7.32 C +HETATM 201 O4' 2MG A 10 22.244 22.960 28.558 1.00 3.56 O +HETATM 202 C3' 2MG A 10 21.385 22.786 30.724 1.00 2.05 C +HETATM 203 O3' 2MG A 10 21.085 23.534 31.872 1.00 0.00 O +HETATM 204 C2' 2MG A 10 20.216 22.612 29.754 1.00 7.47 C +HETATM 205 O2' 2MG A 10 19.506 23.843 29.770 1.00 12.40 O +HETATM 206 C1' 2MG A 10 20.929 22.387 28.445 1.00 9.59 C +HETATM 207 N9 2MG A 10 21.072 20.959 28.122 1.00 12.51 N +HETATM 208 C8 2MG A 10 22.258 20.301 27.863 1.00 10.30 C +HETATM 209 N7 2MG A 10 22.108 19.031 27.605 1.00 11.42 N +HETATM 210 C5 2MG A 10 20.732 18.834 27.685 1.00 13.46 C +HETATM 211 C6 2MG A 10 19.936 17.670 27.508 1.00 15.52 C +HETATM 212 O6 2MG A 10 20.339 16.534 27.233 1.00 12.63 O +HETATM 213 N1 2MG A 10 18.590 17.861 27.685 1.00 16.10 N +HETATM 214 C2 2MG A 10 18.074 19.081 27.993 1.00 16.77 C +HETATM 215 N2 2MG A 10 16.738 19.160 28.138 1.00 12.38 N +HETATM 216 CM2 2MG A 10 16.382 20.543 28.461 1.00 11.08 C +HETATM 217 N3 2MG A 10 18.767 20.211 28.170 1.00 14.81 N +HETATM 218 C4 2MG A 10 20.089 20.009 28.009 1.00 11.85 C +ATOM 219 P C A 11 20.382 23.106 33.229 1.00 4.01 P +ATOM 220 OP1 C A 11 20.229 24.259 34.199 1.00 1.00 O +ATOM 221 OP2 C A 11 21.252 22.055 33.827 1.00 7.38 O +ATOM 222 O5' C A 11 18.950 22.640 32.728 1.00 6.52 O +ATOM 223 C5' C A 11 17.847 23.590 32.906 1.00 11.92 C +ATOM 224 C4' C A 11 16.622 22.702 32.857 1.00 6.89 C +ATOM 225 O4' C A 11 16.638 21.959 31.661 1.00 11.16 O +ATOM 226 C3' C A 11 16.582 21.667 33.972 1.00 5.09 C +ATOM 227 O3' C A 11 16.095 22.241 35.168 1.00 6.14 O +ATOM 228 C2' C A 11 15.689 20.599 33.342 1.00 11.39 C +ATOM 229 O2' C A 11 14.340 21.026 33.520 1.00 16.75 O +ATOM 230 C1' C A 11 16.082 20.655 31.888 1.00 10.47 C +ATOM 231 N1 C A 11 17.088 19.638 31.548 1.00 13.53 N +ATOM 232 C2 C A 11 16.632 18.356 31.306 1.00 16.96 C +ATOM 233 O2 C A 11 15.432 18.086 31.419 1.00 14.24 O +ATOM 234 N3 C A 11 17.554 17.406 30.966 1.00 20.48 N +ATOM 235 C4 C A 11 18.883 17.692 30.853 1.00 16.84 C +ATOM 236 N4 C A 11 19.723 16.709 30.530 1.00 14.05 N +ATOM 237 C5 C A 11 19.340 19.014 31.096 1.00 13.92 C +ATOM 238 C6 C A 11 18.417 19.936 31.419 1.00 13.87 C +ATOM 239 P U A 12 15.822 21.420 36.510 1.00 6.00 P +ATOM 240 OP1 U A 12 14.950 22.162 37.480 1.00 11.43 O +ATOM 241 OP2 U A 12 17.191 21.223 37.060 1.00 6.75 O +ATOM 242 O5' U A 12 15.089 20.093 36.025 1.00 6.44 O +ATOM 243 C5' U A 12 13.660 19.952 36.203 1.00 6.76 C +ATOM 244 C4' U A 12 13.377 18.468 36.268 1.00 7.30 C +ATOM 245 O4' U A 12 13.834 17.844 35.088 1.00 6.64 O +ATOM 246 C3' U A 12 14.107 17.732 37.383 1.00 4.70 C +ATOM 247 O3' U A 12 13.444 17.839 38.643 1.00 3.80 O +ATOM 248 C2' U A 12 14.130 16.298 36.849 1.00 7.59 C +ATOM 249 O2' U A 12 12.848 15.759 37.156 1.00 7.28 O +ATOM 250 C1' U A 12 14.310 16.512 35.379 1.00 13.71 C +ATOM 251 N1 U A 12 15.719 16.388 34.958 1.00 20.72 N +ATOM 252 C2 U A 12 16.122 15.157 34.457 1.00 18.94 C +ATOM 253 O2 U A 12 15.336 14.218 34.377 1.00 15.19 O +ATOM 254 N3 U A 12 17.421 15.033 34.053 1.00 19.01 N +ATOM 255 C4 U A 12 18.314 16.062 34.134 1.00 17.34 C +ATOM 256 O4 U A 12 19.493 15.837 33.730 1.00 21.87 O +ATOM 257 C5 U A 12 17.881 17.316 34.651 1.00 17.38 C +ATOM 258 C6 U A 12 16.605 17.428 35.039 1.00 18.19 C +ATOM 259 P C A 13 14.033 17.068 39.904 1.00 2.89 P +ATOM 260 OP1 C A 13 13.114 17.147 41.084 1.00 3.98 O +ATOM 261 OP2 C A 13 15.346 17.737 40.130 1.00 5.42 O +ATOM 262 O5' C A 13 14.187 15.567 39.387 1.00 4.80 O +ATOM 263 C5' C A 13 15.016 14.713 40.211 1.00 2.46 C +ATOM 264 C4' C A 13 15.233 13.437 39.419 1.00 1.93 C +ATOM 265 O4' C A 13 15.729 13.718 38.126 1.00 0.00 O +ATOM 266 C3' C A 13 16.282 12.520 40.049 1.00 3.55 C +ATOM 267 O3' C A 13 15.736 11.773 41.132 1.00 1.36 O +ATOM 268 C2' C A 13 16.722 11.671 38.853 1.00 3.74 C +ATOM 269 O2' C A 13 15.749 10.642 38.724 1.00 8.08 O +ATOM 270 C1' C A 13 16.645 12.678 37.722 1.00 3.48 C +ATOM 271 N1 C A 13 17.961 13.257 37.415 1.00 4.60 N +ATOM 272 C2 C A 13 18.813 12.481 36.639 1.00 5.47 C +ATOM 273 O2 C A 13 18.480 11.356 36.268 1.00 3.94 O +ATOM 274 N3 C A 13 20.033 13.004 36.316 1.00 10.84 N +ATOM 275 C4 C A 13 20.419 14.246 36.736 1.00 8.88 C +ATOM 276 N4 C A 13 21.625 14.696 36.381 1.00 4.12 N +ATOM 277 C5 C A 13 19.530 15.033 37.512 1.00 9.81 C +ATOM 278 C6 C A 13 18.334 14.505 37.819 1.00 7.86 C +ATOM 279 P A A 14 16.652 10.912 42.134 1.00 0.00 P +ATOM 280 OP1 A A 14 15.885 10.462 43.330 1.00 11.31 O +ATOM 281 OP2 A A 14 17.701 11.891 42.522 1.00 7.15 O +ATOM 282 O5' A A 14 17.061 9.704 41.181 1.00 3.36 O +ATOM 283 C5' A A 14 17.574 8.478 41.730 1.00 6.69 C +ATOM 284 C4' A A 14 17.724 7.466 40.615 1.00 5.08 C +ATOM 285 O4' A A 14 18.057 8.051 39.371 1.00 0.00 O +ATOM 286 C3' A A 14 18.877 6.482 40.874 1.00 2.72 C +ATOM 287 O3' A A 14 18.534 5.481 41.843 1.00 1.71 O +ATOM 288 C2' A A 14 19.157 5.965 39.484 1.00 7.69 C +ATOM 289 O2' A A 14 18.154 4.970 39.225 1.00 6.46 O +ATOM 290 C1' A A 14 18.927 7.185 38.643 1.00 0.00 C +ATOM 291 N9 A A 14 20.216 7.859 38.385 1.00 0.33 N +ATOM 292 C8 A A 14 20.559 9.141 38.757 1.00 3.70 C +ATOM 293 N7 A A 14 21.771 9.495 38.385 1.00 1.53 N +ATOM 294 C5 A A 14 22.258 8.366 37.754 1.00 0.00 C +ATOM 295 C6 A A 14 23.510 8.118 37.140 1.00 4.25 C +ATOM 296 N6 A A 14 24.503 8.995 37.076 1.00 4.25 N +ATOM 297 N1 A A 14 23.647 6.870 36.591 1.00 1.32 N +ATOM 298 C2 A A 14 22.634 5.965 36.639 1.00 0.64 C +ATOM 299 N3 A A 14 21.442 6.139 37.205 1.00 0.00 N +ATOM 300 C4 A A 14 21.318 7.365 37.738 1.00 0.00 C +ATOM 301 P G A 15 19.456 5.605 43.185 1.00 1.67 P +ATOM 302 OP1 G A 15 18.750 5.049 44.365 1.00 17.45 O +ATOM 303 OP2 G A 15 19.756 7.050 43.266 1.00 9.28 O +ATOM 304 O5' G A 15 20.719 4.722 42.765 1.00 10.27 O +ATOM 305 C5' G A 15 20.689 4.188 41.423 1.00 7.86 C +ATOM 306 C4' G A 15 22.128 4.127 40.954 1.00 6.80 C +ATOM 307 O4' G A 15 22.331 5.127 39.952 1.00 10.07 O +ATOM 308 C3' G A 15 23.187 4.419 41.989 1.00 2.79 C +ATOM 309 O3' G A 15 23.544 3.317 42.846 0.90 9.63 O +ATOM 310 C2' G A 15 24.356 4.852 41.116 1.00 6.46 C +ATOM 311 O2' G A 15 24.956 3.654 40.647 1.00 6.00 O +ATOM 312 C1' G A 15 23.657 5.611 40.017 1.00 9.74 C +ATOM 313 N9 G A 15 23.780 7.028 40.389 1.00 12.21 N +ATOM 314 C8 G A 15 22.894 7.781 41.116 1.00 14.58 C +ATOM 315 N7 G A 15 23.287 9.018 41.310 1.00 16.29 N +ATOM 316 C5 G A 15 24.520 9.080 40.664 1.00 15.66 C +ATOM 317 C6 G A 15 25.449 10.148 40.518 1.00 16.84 C +ATOM 318 O6 G A 15 25.336 11.300 40.954 1.00 14.57 O +ATOM 319 N1 G A 15 26.575 9.816 39.791 1.00 14.07 N +ATOM 320 C2 G A 15 26.771 8.568 39.290 1.00 11.60 C +ATOM 321 N2 G A 15 27.917 8.399 38.611 1.00 15.41 N +ATOM 322 N3 G A 15 25.938 7.539 39.403 1.00 9.72 N +ATOM 323 C4 G A 15 24.833 7.865 40.098 1.00 12.76 C +HETATM 324 P H2U A 16 24.193 3.778 44.268 0.90 13.88 P +HETATM 325 OP1 H2U A 16 23.147 4.318 45.189 0.90 12.56 O +HETATM 326 OP2 H2U A 16 25.156 4.829 43.847 0.90 15.63 O +HETATM 327 O5' H2U A 16 24.853 2.457 44.849 0.90 22.09 O +HETATM 328 C5' H2U A 16 24.090 1.226 44.898 0.76 25.26 C +HETATM 329 C4' H2U A 16 24.723 0.399 46.013 0.76 29.19 C +HETATM 330 O4' H2U A 16 24.126 0.759 47.242 0.76 29.89 O +HETATM 331 C3' H2U A 16 24.526 -1.113 45.835 0.76 31.53 C +HETATM 332 O3' H2U A 16 25.715 -1.838 46.256 0.69 29.35 O +HETATM 333 C1' H2U A 16 23.560 -0.416 47.872 0.76 32.84 C +HETATM 334 C2' H2U A 16 23.327 -1.377 46.724 0.76 31.38 C +HETATM 335 O2' H2U A 16 23.304 -2.727 47.112 0.76 34.76 O +HETATM 336 N1 H2U A 16 22.344 -0.039 48.583 0.61 37.16 N +HETATM 337 C2 H2U A 16 22.504 0.663 49.730 0.61 41.18 C +HETATM 338 O2 H2U A 16 23.573 1.006 50.199 0.61 43.75 O +HETATM 339 N3 H2U A 16 21.332 1.006 50.393 0.61 42.30 N +HETATM 340 C4 H2U A 16 20.059 0.708 50.005 0.61 42.03 C +HETATM 341 O4 H2U A 16 19.103 1.074 50.684 0.61 43.82 O +HETATM 342 C5 H2U A 16 19.933 -0.062 48.745 0.61 40.18 C +HETATM 343 C6 H2U A 16 21.169 -0.905 48.486 0.61 38.03 C +HETATM 344 P H2U A 17 26.761 -2.254 45.092 0.69 28.84 P +HETATM 345 OP1 H2U A 17 27.298 -1.035 44.397 0.69 34.24 O +HETATM 346 OP2 H2U A 17 25.912 -3.036 44.155 0.69 26.72 O +HETATM 347 O5' H2U A 17 27.924 -3.064 45.787 0.57 26.88 O +HETATM 348 C5' H2U A 17 28.440 -2.833 47.112 0.57 23.53 C +HETATM 349 C4' H2U A 17 29.932 -2.541 46.999 0.57 19.51 C +HETATM 350 O4' H2U A 17 30.649 -3.739 46.741 0.57 20.05 O +HETATM 351 C3' H2U A 17 30.295 -1.580 45.852 0.57 15.39 C +HETATM 352 O3' H2U A 17 31.395 -0.737 46.256 1.00 10.60 O +HETATM 353 C1' H2U A 17 31.421 -3.615 45.528 0.57 16.72 C +HETATM 354 C2' H2U A 17 30.729 -2.524 44.753 0.57 14.10 C +HETATM 355 O2' H2U A 17 31.561 -1.878 43.831 0.57 12.70 O +HETATM 356 N1 H2U A 17 31.468 -4.897 44.833 0.96 11.89 N +HETATM 357 C2 H2U A 17 30.808 -5.037 43.670 0.96 15.22 C +HETATM 358 O2 H2U A 17 29.949 -4.284 43.250 0.96 22.01 O +HETATM 359 N3 H2U A 17 31.025 -6.229 42.991 0.96 17.46 N +HETATM 360 C4 H2U A 17 31.931 -7.191 43.314 0.96 20.71 C +HETATM 361 O4 H2U A 17 32.021 -8.197 42.619 0.96 24.03 O +HETATM 362 C5 H2U A 17 32.767 -6.926 44.510 0.96 22.14 C +HETATM 363 C6 H2U A 17 32.021 -6.066 45.512 0.96 17.90 C +ATOM 364 P G A 18 31.381 0.140 47.581 1.00 6.79 P +ATOM 365 OP1 G A 18 31.721 -0.646 48.809 1.00 9.37 O +ATOM 366 OP2 G A 18 29.986 0.663 47.629 1.00 12.80 O +ATOM 367 O5' G A 18 32.494 1.254 47.274 1.00 3.89 O +ATOM 368 C5' G A 18 32.161 2.237 46.256 1.00 6.76 C +ATOM 369 C4' G A 18 33.297 2.187 45.237 1.00 5.75 C +ATOM 370 O4' G A 18 34.373 2.963 45.755 1.00 6.67 O +ATOM 371 C3' G A 18 33.836 0.787 44.979 1.00 0.00 C +ATOM 372 O3' G A 18 34.366 0.618 43.654 1.00 0.00 O +ATOM 373 C2' G A 18 35.029 0.725 45.916 1.00 0.00 C +ATOM 374 O2' G A 18 35.898 -0.270 45.464 1.00 2.08 O +ATOM 375 C1' G A 18 35.538 2.142 45.835 1.00 0.00 C +ATOM 376 N9 G A 18 36.401 2.412 46.999 1.00 0.00 N +ATOM 377 C8 G A 18 36.384 1.872 48.244 1.00 0.00 C +ATOM 378 N7 G A 18 37.320 2.333 49.035 1.00 0.00 N +ATOM 379 C5 G A 18 38.010 3.250 48.244 1.00 0.00 C +ATOM 380 C6 G A 18 39.129 4.082 48.518 1.00 0.00 C +ATOM 381 O6 G A 18 39.756 4.166 49.569 1.00 1.20 O +ATOM 382 N1 G A 18 39.529 4.857 47.452 1.00 0.00 N +ATOM 383 C2 G A 18 38.903 4.829 46.256 1.00 0.00 C +ATOM 384 N2 G A 18 39.406 5.644 45.318 1.00 0.00 N +ATOM 385 N3 G A 18 37.854 4.070 45.949 1.00 0.38 N +ATOM 386 C4 G A 18 37.457 3.306 46.983 1.00 3.99 C +ATOM 387 P G A 19 33.790 0.748 42.183 1.00 0.00 P +ATOM 388 OP1 G A 19 32.687 1.743 42.070 1.00 1.97 O +ATOM 389 OP2 G A 19 33.247 -0.613 41.908 1.00 1.44 O +ATOM 390 O5' G A 19 35.072 1.130 41.310 1.00 0.00 O +ATOM 391 C5' G A 19 35.129 2.209 40.373 1.00 0.00 C +ATOM 392 C4' G A 19 36.364 2.080 39.500 1.00 0.00 C +ATOM 393 O4' G A 19 37.427 1.495 40.227 1.00 8.34 O +ATOM 394 C3' G A 19 36.181 1.158 38.272 1.00 0.00 C +ATOM 395 O3' G A 19 36.901 1.709 37.156 1.00 1.83 O +ATOM 396 C2' G A 19 36.784 -0.163 38.757 1.00 0.00 C +ATOM 397 O2' G A 19 37.114 -0.905 37.609 1.00 2.96 O +ATOM 398 C1' G A 19 37.917 0.281 39.581 1.00 0.00 C +ATOM 399 N9 G A 19 38.300 -0.568 40.712 1.00 0.00 N +ATOM 400 C8 G A 19 37.504 -1.451 41.391 1.00 0.00 C +ATOM 401 N7 G A 19 38.110 -2.052 42.377 1.00 0.00 N +ATOM 402 C5 G A 19 39.399 -1.529 42.361 1.00 0.00 C +ATOM 403 C6 G A 19 40.532 -1.777 43.169 1.00 0.00 C +ATOM 404 O6 G A 19 40.621 -2.558 44.122 1.00 0.00 O +ATOM 405 N1 G A 19 41.644 -1.051 42.829 1.00 1.20 N +ATOM 406 C2 G A 19 41.658 -0.163 41.795 1.00 0.99 C +ATOM 407 N2 G A 19 42.820 0.467 41.585 1.00 0.00 N +ATOM 408 N3 G A 19 40.618 0.107 40.987 1.00 5.20 N +ATOM 409 C4 G A 19 39.526 -0.607 41.326 1.00 1.71 C +ATOM 410 P G A 20 36.408 3.115 36.526 1.00 2.55 P +ATOM 411 OP1 G A 20 37.547 3.918 35.960 1.00 1.87 O +ATOM 412 OP2 G A 20 35.755 3.806 37.658 1.00 0.00 O +ATOM 413 O5' G A 20 35.452 2.659 35.346 1.00 0.00 O +ATOM 414 C5' G A 20 36.095 2.530 34.037 1.00 0.00 C +ATOM 415 C4' G A 20 34.942 2.254 33.100 1.00 0.00 C +ATOM 416 O4' G A 20 34.076 1.310 33.698 1.00 0.00 O +ATOM 417 C3' G A 20 34.073 3.469 32.809 1.00 0.00 C +ATOM 418 O3' G A 20 34.629 4.329 31.807 1.00 1.61 O +ATOM 419 C2' G A 20 32.757 2.805 32.405 1.00 0.01 C +ATOM 420 O2' G A 20 32.874 2.412 31.047 1.00 4.44 O +ATOM 421 C1' G A 20 32.737 1.597 33.326 1.00 0.00 C +ATOM 422 N9 G A 20 31.894 1.923 34.490 1.00 0.00 N +ATOM 423 C8 G A 20 32.134 1.720 35.815 1.00 0.00 C +ATOM 424 N7 G A 20 31.168 2.136 36.607 1.00 0.00 N +ATOM 425 C5 G A 20 30.219 2.642 35.718 1.00 2.19 C +ATOM 426 C6 G A 20 28.946 3.238 35.928 1.00 0.00 C +ATOM 427 O6 G A 20 28.377 3.441 37.011 1.00 0.00 O +ATOM 428 N1 G A 20 28.297 3.621 34.781 1.00 0.00 N +ATOM 429 C2 G A 20 28.837 3.446 33.552 1.00 0.60 C +ATOM 430 N2 G A 20 28.084 3.874 32.518 1.00 0.15 N +ATOM 431 N3 G A 20 30.022 2.895 33.294 1.00 0.00 N +ATOM 432 C4 G A 20 30.655 2.519 34.425 1.00 0.00 C +ATOM 433 P A A 21 35.508 5.605 32.276 1.00 0.00 P +ATOM 434 OP1 A A 21 36.168 6.268 31.112 1.00 0.00 O +ATOM 435 OP2 A A 21 36.508 4.976 33.197 1.00 2.01 O +ATOM 436 O5' A A 21 34.479 6.555 33.019 1.00 0.00 O +ATOM 437 C5' A A 21 33.500 7.326 32.308 1.00 0.00 C +ATOM 438 C4' A A 21 32.154 7.162 32.971 1.00 1.08 C +ATOM 439 O4' A A 21 32.064 7.933 34.150 1.00 2.40 O +ATOM 440 C3' A A 21 30.985 7.652 32.098 1.00 0.00 C +ATOM 441 O3' A A 21 30.609 6.690 31.144 1.00 5.12 O +ATOM 442 C2' A A 21 29.902 7.916 33.148 1.00 0.00 C +ATOM 443 O2' A A 21 29.316 6.651 33.423 1.00 0.54 O +ATOM 444 C1' A A 21 30.715 8.427 34.312 1.00 2.79 C +ATOM 445 N9 A A 21 30.729 9.900 34.377 1.00 0.00 N +ATOM 446 C8 A A 21 31.578 10.772 33.746 1.00 0.00 C +ATOM 447 N7 A A 21 31.335 12.031 34.021 1.00 4.38 N +ATOM 448 C5 A A 21 30.246 11.986 34.878 1.00 3.49 C +ATOM 449 C6 A A 21 29.509 13.009 35.524 1.00 0.00 C +ATOM 450 N6 A A 21 29.759 14.297 35.411 1.00 0.47 N +ATOM 451 N1 A A 21 28.487 12.576 36.316 1.00 0.25 N +ATOM 452 C2 A A 21 28.200 11.250 36.461 1.00 1.02 C +ATOM 453 N3 A A 21 28.853 10.249 35.896 1.00 5.26 N +ATOM 454 C4 A A 21 29.866 10.676 35.104 1.00 1.73 C +ATOM 455 P G A 22 29.383 6.640 30.158 1.00 0.00 P +ATOM 456 OP1 G A 22 29.393 5.318 29.415 1.00 12.63 O +ATOM 457 OP2 G A 22 29.589 7.786 29.237 1.00 8.98 O +ATOM 458 O5' G A 22 28.047 6.735 31.015 1.00 1.57 O +ATOM 459 C5' G A 22 26.844 6.842 30.191 1.00 5.53 C +ATOM 460 C4' G A 22 25.709 6.319 31.031 1.00 0.85 C +ATOM 461 O4' G A 22 25.889 6.786 32.356 1.00 5.66 O +ATOM 462 C3' G A 22 24.323 6.825 30.643 1.00 0.74 C +ATOM 463 O3' G A 22 23.727 6.094 29.560 1.00 3.90 O +ATOM 464 C2' G A 22 23.573 6.668 31.952 1.00 0.67 C +ATOM 465 O2' G A 22 23.247 5.279 32.049 1.00 0.00 O +ATOM 466 C1' G A 22 24.613 7.016 32.971 1.00 2.41 C +ATOM 467 N9 G A 22 24.566 8.433 33.375 1.00 7.09 N +ATOM 468 C8 G A 22 25.605 9.333 33.310 1.00 10.11 C +ATOM 469 N7 G A 22 25.302 10.530 33.746 1.00 10.90 N +ATOM 470 C5 G A 22 23.970 10.406 34.134 1.00 4.07 C +ATOM 471 C6 G A 22 23.070 11.345 34.700 1.00 5.94 C +ATOM 472 O6 G A 22 23.287 12.531 34.975 1.00 4.48 O +ATOM 473 N1 G A 22 21.818 10.839 34.958 1.00 12.00 N +ATOM 474 C2 G A 22 21.482 9.552 34.700 1.00 8.56 C +ATOM 475 N2 G A 22 20.222 9.220 35.007 1.00 10.54 N +ATOM 476 N3 G A 22 22.288 8.624 34.167 1.00 10.69 N +ATOM 477 C4 G A 22 23.514 9.125 33.924 1.00 8.12 C +ATOM 478 P A A 23 22.847 6.909 28.477 1.00 5.94 P +ATOM 479 OP1 A A 23 22.881 6.274 27.120 1.00 0.73 O +ATOM 480 OP2 A A 23 23.493 8.253 28.477 1.00 12.20 O +ATOM 481 O5' A A 23 21.385 6.898 29.092 1.00 0.00 O +ATOM 482 C5' A A 23 20.709 5.616 29.269 1.00 6.09 C +ATOM 483 C4' A A 23 19.446 5.931 30.045 1.00 6.73 C +ATOM 484 O4' A A 23 19.783 6.527 31.274 1.00 5.74 O +ATOM 485 C3' A A 23 18.517 6.938 29.350 1.00 5.97 C +ATOM 486 O3' A A 23 17.708 6.359 28.332 1.00 1.13 O +ATOM 487 C2' A A 23 17.738 7.472 30.546 1.00 7.79 C +ATOM 488 O2' A A 23 16.728 6.510 30.837 1.00 11.50 O +ATOM 489 C1' A A 23 18.787 7.477 31.629 1.00 10.64 C +ATOM 490 N9 A A 23 19.363 8.832 31.710 1.00 15.20 N +ATOM 491 C8 A A 23 20.599 9.226 31.257 1.00 15.68 C +ATOM 492 N7 A A 23 20.849 10.491 31.451 1.00 18.24 N +ATOM 493 C5 A A 23 19.703 10.969 32.065 1.00 12.32 C +ATOM 494 C6 A A 23 19.360 12.262 32.534 1.00 15.50 C +ATOM 495 N6 A A 23 20.149 13.318 32.437 1.00 18.17 N +ATOM 496 N1 A A 23 18.121 12.351 33.100 1.00 20.60 N +ATOM 497 C2 A A 23 17.295 11.278 33.213 1.00 20.47 C +ATOM 498 N3 A A 23 17.561 10.041 32.793 1.00 21.86 N +ATOM 499 C4 A A 23 18.784 9.957 32.243 1.00 17.48 C +ATOM 500 P G A 24 17.501 6.983 26.861 1.00 5.70 P +ATOM 501 OP1 G A 24 17.378 5.920 25.795 1.00 8.31 O +ATOM 502 OP2 G A 24 18.624 7.905 26.603 1.00 4.78 O +ATOM 503 O5' G A 24 16.075 7.702 27.007 1.00 11.85 O +ATOM 504 C5' G A 24 15.213 7.073 28.009 1.00 15.16 C +ATOM 505 C4' G A 24 14.407 8.208 28.591 1.00 13.30 C +ATOM 506 O4' G A 24 15.000 8.731 29.754 1.00 13.68 O +ATOM 507 C3' G A 24 14.303 9.406 27.637 1.00 14.60 C +ATOM 508 O3' G A 24 13.364 9.158 26.603 1.00 14.10 O +ATOM 509 C2' G A 24 13.967 10.519 28.607 1.00 17.86 C +ATOM 510 O2' G A 24 12.568 10.418 28.882 1.00 9.54 O +ATOM 511 C1' G A 24 14.773 10.125 29.819 1.00 17.94 C +ATOM 512 N9 G A 24 16.045 10.867 29.754 1.00 19.56 N +ATOM 513 C8 G A 24 17.278 10.344 29.447 1.00 24.47 C +ATOM 514 N7 G A 24 18.244 11.233 29.447 1.00 25.68 N +ATOM 515 C5 G A 24 17.598 12.419 29.787 1.00 25.21 C +ATOM 516 C6 G A 24 18.091 13.740 29.948 1.00 27.48 C +ATOM 517 O6 G A 24 19.257 14.134 29.819 1.00 26.09 O +ATOM 518 N1 G A 24 17.128 14.656 30.288 1.00 30.45 N +ATOM 519 C2 G A 24 15.819 14.319 30.449 1.00 27.68 C +ATOM 520 N2 G A 24 15.006 15.337 30.789 1.00 27.86 N +ATOM 521 N3 G A 24 15.306 13.099 30.304 1.00 24.78 N +ATOM 522 C4 G A 24 16.249 12.200 29.964 1.00 21.58 C +ATOM 523 P C A 25 12.541 10.131 25.665 1.00 23.08 P +ATOM 524 OP1 C A 25 11.465 10.817 26.473 1.00 24.45 O +ATOM 525 OP2 C A 25 11.948 9.220 24.631 1.00 26.26 O +ATOM 526 O5' C A 25 13.467 11.244 25.003 1.00 18.54 O +ATOM 527 C5' C A 25 12.704 12.329 24.372 1.00 21.82 C +ATOM 528 C4' C A 25 12.468 13.392 25.423 1.00 21.60 C +ATOM 529 O4' C A 25 13.247 13.099 26.570 1.00 20.29 O +ATOM 530 C3' C A 25 12.901 14.803 25.019 1.00 21.88 C +ATOM 531 O3' C A 25 11.952 15.511 24.211 1.00 16.04 O +ATOM 532 C2' C A 25 13.127 15.466 26.376 1.00 20.93 C +ATOM 533 O2' C A 25 11.838 15.877 26.845 1.00 17.33 O +ATOM 534 C1' C A 25 13.677 14.319 27.168 1.00 17.48 C +ATOM 535 N1 C A 25 15.149 14.325 27.136 1.00 13.20 N +ATOM 536 C2 C A 25 15.826 15.489 27.427 1.00 17.05 C +ATOM 537 O2 C A 25 15.206 16.501 27.750 1.00 16.74 O +ATOM 538 N3 C A 25 17.195 15.455 27.378 1.00 18.39 N +ATOM 539 C4 C A 25 17.874 14.330 27.023 1.00 16.80 C +ATOM 540 N4 C A 25 19.203 14.330 26.974 1.00 12.35 N +ATOM 541 C5 C A 25 17.161 13.139 26.700 1.00 17.02 C +ATOM 542 C6 C A 25 15.822 13.189 26.764 1.00 12.57 C +HETATM 543 P M2G A 26 12.321 15.702 22.643 1.00 29.87 P +HETATM 544 OP1 M2G A 26 11.072 15.663 21.819 1.00 33.15 O +HETATM 545 OP2 M2G A 26 13.211 14.544 22.368 1.00 23.48 O +HETATM 546 O5' M2G A 26 13.047 17.113 22.562 1.00 22.58 O +HETATM 547 C5' M2G A 26 12.741 18.131 23.564 1.00 24.79 C +HETATM 548 C4' M2G A 26 14.000 18.963 23.694 1.00 20.77 C +HETATM 549 O4' M2G A 26 14.820 18.401 24.712 1.00 18.03 O +HETATM 550 C3' M2G A 26 14.866 18.997 22.449 1.00 18.83 C +HETATM 551 O3' M2G A 26 14.417 19.947 21.496 1.00 16.54 O +HETATM 552 C2' M2G A 26 16.242 19.328 23.031 1.00 20.38 C +HETATM 553 O2' M2G A 26 16.299 20.734 23.209 1.00 22.35 O +HETATM 554 C1' M2G A 26 16.195 18.586 24.372 1.00 23.07 C +HETATM 555 N9 M2G A 26 16.925 17.316 24.227 1.00 24.84 N +HETATM 556 C8 M2G A 26 16.435 16.045 24.114 1.00 23.72 C +HETATM 557 N7 M2G A 26 17.365 15.123 24.001 1.00 23.35 N +HETATM 558 C5 M2G A 26 18.554 15.848 24.065 1.00 21.52 C +HETATM 559 C6 M2G A 26 19.913 15.432 24.001 1.00 22.80 C +HETATM 560 O6 M2G A 26 20.339 14.274 23.887 1.00 22.96 O +HETATM 561 N1 M2G A 26 20.825 16.456 24.097 1.00 20.51 N +HETATM 562 C2 M2G A 26 20.442 17.749 24.227 1.00 23.90 C +HETATM 563 N2 M2G A 26 21.375 18.693 24.308 1.00 25.45 N +HETATM 564 N3 M2G A 26 19.193 18.198 24.292 1.00 22.42 N +HETATM 565 C4 M2G A 26 18.297 17.192 24.194 1.00 21.82 C +HETATM 566 CM1 M2G A 26 20.815 20.009 24.453 1.00 21.30 C +HETATM 567 CM2 M2G A 26 22.721 18.316 24.259 1.00 24.30 C +ATOM 568 P C A 27 14.347 19.744 19.895 1.00 20.07 P +ATOM 569 OP1 C A 27 13.314 20.616 19.249 1.00 15.88 O +ATOM 570 OP2 C A 27 13.997 18.305 19.766 1.00 25.96 O +ATOM 571 O5' C A 27 15.822 20.138 19.427 1.00 16.34 O +ATOM 572 C5' C A 27 16.592 20.981 20.332 1.00 15.72 C +ATOM 573 C4' C A 27 18.024 20.903 19.863 1.00 17.30 C +ATOM 574 O4' C A 27 18.797 20.155 20.784 1.00 18.55 O +ATOM 575 C3' C A 27 18.207 20.183 18.522 1.00 18.37 C +ATOM 576 O3' C A 27 17.911 21.021 17.406 1.00 23.90 O +ATOM 577 C2' C A 27 19.673 19.739 18.619 1.00 17.05 C +ATOM 578 O2' C A 27 20.476 20.858 18.247 1.00 19.18 O +ATOM 579 C1' C A 27 19.806 19.424 20.089 1.00 14.80 C +ATOM 580 N1 C A 27 19.623 17.979 20.283 1.00 20.01 N +ATOM 581 C2 C A 27 20.662 17.142 19.928 1.00 18.21 C +ATOM 582 O2 C A 27 21.705 17.591 19.459 1.00 16.44 O +ATOM 583 N3 C A 27 20.479 15.798 20.105 1.00 17.00 N +ATOM 584 C4 C A 27 19.323 15.292 20.623 1.00 21.09 C +ATOM 585 N4 C A 27 19.203 13.971 20.784 1.00 23.04 N +ATOM 586 C5 C A 27 18.260 16.169 20.994 1.00 18.01 C +ATOM 587 C6 C A 27 18.457 17.479 20.801 1.00 19.95 C +ATOM 588 P C A 28 18.077 20.582 15.871 1.00 33.10 P +ATOM 589 OP1 C A 28 17.758 21.706 14.934 1.00 25.70 O +ATOM 590 OP2 C A 28 17.098 19.469 15.710 1.00 32.53 O +ATOM 591 O5' C A 28 19.610 20.144 15.774 1.00 19.71 O +ATOM 592 C5' C A 28 20.419 20.627 14.675 1.00 18.88 C +ATOM 593 C4' C A 28 21.548 19.643 14.465 1.00 20.19 C +ATOM 594 O4' C A 28 21.821 18.918 15.645 1.00 20.55 O +ATOM 595 C3' C A 28 21.258 18.564 13.415 1.00 19.77 C +ATOM 596 O3' C A 28 21.448 19.031 12.089 1.00 25.59 O +ATOM 597 C2' C A 28 22.238 17.462 13.818 1.00 17.66 C +ATOM 598 O2' C A 28 23.503 17.822 13.269 1.00 20.65 O +ATOM 599 C1' C A 28 22.244 17.580 15.322 1.00 17.06 C +ATOM 600 N1 C A 28 21.368 16.574 15.952 1.00 15.63 N +ATOM 601 C2 C A 28 21.781 15.252 15.871 1.00 16.77 C +ATOM 602 O2 C A 28 22.827 14.949 15.305 1.00 17.51 O +ATOM 603 N3 C A 28 20.989 14.302 16.453 1.00 16.03 N +ATOM 604 C4 C A 28 19.826 14.628 17.083 1.00 15.19 C +ATOM 605 N4 C A 28 19.093 13.661 17.633 1.00 14.44 N +ATOM 606 C5 C A 28 19.416 15.989 17.164 1.00 17.77 C +ATOM 607 C6 C A 28 20.209 16.911 16.582 1.00 16.79 C +ATOM 608 P A A 29 20.302 18.839 10.974 1.00 34.75 P +ATOM 609 OP1 A A 29 20.602 19.683 9.778 1.00 29.69 O +ATOM 610 OP2 A A 29 19.060 19.250 11.685 1.00 31.89 O +ATOM 611 O5' A A 29 20.376 17.276 10.667 1.00 26.67 O +ATOM 612 C5' A A 29 20.965 16.894 9.406 1.00 27.49 C +ATOM 613 C4' A A 29 21.861 15.702 9.649 1.00 26.49 C +ATOM 614 O4' A A 29 22.051 15.477 11.039 1.00 25.60 O +ATOM 615 C3' A A 29 21.285 14.381 9.132 1.00 25.08 C +ATOM 616 O3' A A 29 21.428 14.201 7.725 1.00 22.54 O +ATOM 617 C2' A A 29 22.055 13.363 9.972 1.00 23.29 C +ATOM 618 O2' A A 29 23.334 13.223 9.358 1.00 22.73 O +ATOM 619 C1' A A 29 22.168 14.078 11.281 1.00 21.55 C +ATOM 620 N9 A A 29 21.065 13.588 12.138 1.00 18.46 N +ATOM 621 C8 A A 29 20.003 14.314 12.606 1.00 19.07 C +ATOM 622 N7 A A 29 19.167 13.611 13.334 1.00 17.22 N +ATOM 623 C5 A A 29 19.723 12.335 13.334 1.00 16.57 C +ATOM 624 C6 A A 29 19.296 11.132 13.932 1.00 19.45 C +ATOM 625 N6 A A 29 18.197 11.030 14.659 1.00 18.51 N +ATOM 626 N1 A A 29 20.103 10.052 13.722 1.00 21.52 N +ATOM 627 C2 A A 29 21.239 10.148 12.962 1.00 18.07 C +ATOM 628 N3 A A 29 21.698 11.250 12.364 1.00 17.75 N +ATOM 629 C4 A A 29 20.889 12.307 12.590 1.00 17.61 C +ATOM 630 P G A 30 20.049 13.712 7.014 1.00 28.73 P +ATOM 631 OP1 G A 30 20.029 14.100 5.576 1.00 28.93 O +ATOM 632 OP2 G A 30 19.010 14.409 7.822 1.00 28.96 O +ATOM 633 O5' G A 30 20.093 12.127 7.241 1.00 24.12 O +ATOM 634 C5' G A 30 21.305 11.553 7.774 1.00 23.01 C +ATOM 635 C4' G A 30 21.039 10.114 8.162 1.00 22.38 C +ATOM 636 O4' G A 30 20.775 10.058 9.568 1.00 23.65 O +ATOM 637 C3' G A 30 19.819 9.462 7.531 1.00 23.59 C +ATOM 638 O3' G A 30 20.093 8.984 6.222 1.00 25.81 O +ATOM 639 C2' G A 30 19.486 8.349 8.533 1.00 22.06 C +ATOM 640 O2' G A 30 20.346 7.252 8.259 1.00 21.95 O +ATOM 641 C1' G A 30 19.839 9.001 9.859 1.00 21.11 C +ATOM 642 N9 G A 30 18.624 9.496 10.521 1.00 17.84 N +ATOM 643 C8 G A 30 18.134 10.766 10.618 1.00 15.79 C +ATOM 644 N7 G A 30 17.025 10.862 11.313 1.00 14.31 N +ATOM 645 C5 G A 30 16.768 9.552 11.701 1.00 16.78 C +ATOM 646 C6 G A 30 15.716 8.984 12.477 1.00 19.20 C +ATOM 647 O6 G A 30 14.753 9.574 12.978 1.00 18.18 O +ATOM 648 N1 G A 30 15.809 7.623 12.655 1.00 18.94 N +ATOM 649 C2 G A 30 16.828 6.887 12.138 1.00 19.57 C +ATOM 650 N2 G A 30 16.778 5.571 12.396 1.00 17.51 N +ATOM 651 N3 G A 30 17.837 7.365 11.410 1.00 19.89 N +ATOM 652 C4 G A 30 17.744 8.703 11.233 1.00 18.38 C +ATOM 653 P A A 31 19.010 8.928 5.043 1.00 37.02 P +ATOM 654 OP1 A A 31 19.583 8.219 3.830 1.00 30.97 O +ATOM 655 OP2 A A 31 18.707 10.356 4.752 1.00 26.58 O +ATOM 656 O5' A A 31 17.808 8.084 5.673 1.00 30.07 O +ATOM 657 C5' A A 31 17.621 6.729 5.172 1.00 30.35 C +ATOM 658 C4' A A 31 16.995 5.926 6.303 1.00 27.81 C +ATOM 659 O4' A A 31 17.195 6.595 7.531 1.00 26.76 O +ATOM 660 C3' A A 31 15.483 5.740 6.190 1.00 26.45 C +ATOM 661 O3' A A 31 15.083 4.694 5.301 1.00 30.61 O +ATOM 662 C2' A A 31 15.113 5.470 7.661 1.00 24.54 C +ATOM 663 O2' A A 31 15.386 4.098 7.903 1.00 28.13 O +ATOM 664 C1' A A 31 16.082 6.364 8.388 1.00 22.42 C +ATOM 665 N9 A A 31 15.389 7.612 8.744 1.00 21.95 N +ATOM 666 C8 A A 31 15.696 8.894 8.372 1.00 22.76 C +ATOM 667 N7 A A 31 14.876 9.805 8.841 1.00 19.49 N +ATOM 668 C5 A A 31 13.967 9.057 9.584 1.00 20.04 C +ATOM 669 C6 A A 31 12.841 9.451 10.360 1.00 20.83 C +ATOM 670 N6 A A 31 12.431 10.699 10.505 1.00 21.29 N +ATOM 671 N1 A A 31 12.172 8.422 10.958 1.00 25.93 N +ATOM 672 C2 A A 31 12.571 7.129 10.828 1.00 26.19 C +ATOM 673 N3 A A 31 13.617 6.690 10.134 1.00 23.41 N +ATOM 674 C4 A A 31 14.270 7.713 9.536 1.00 22.31 C +HETATM 675 N1 OMC A 32 11.289 7.882 7.160 1.00 35.76 N +HETATM 676 C2 OMC A 32 10.706 9.018 7.677 1.00 35.78 C +HETATM 677 N3 OMC A 32 11.312 10.215 7.418 1.00 36.87 N +HETATM 678 C4 OMC A 32 12.451 10.305 6.675 1.00 37.09 C +HETATM 679 C5 OMC A 32 13.038 9.125 6.142 1.00 35.60 C +HETATM 680 C6 OMC A 32 12.428 7.961 6.400 1.00 34.56 C +HETATM 681 O2 OMC A 32 9.680 8.945 8.356 1.00 34.90 O +HETATM 682 N4 OMC A 32 13.001 11.503 6.449 1.00 35.89 N +HETATM 683 C1' OMC A 32 10.633 6.589 7.418 1.00 37.12 C +HETATM 684 C2' OMC A 32 9.480 6.499 6.432 1.00 38.77 C +HETATM 685 O2' OMC A 32 8.391 5.774 6.998 1.00 44.03 O +HETATM 686 CM2 OMC A 32 7.285 6.061 7.596 1.00 45.01 C +HETATM 687 C3' OMC A 32 10.093 5.639 5.333 1.00 37.31 C +HETATM 688 C4' OMC A 32 10.976 4.683 6.125 1.00 37.40 C +HETATM 689 O4' OMC A 32 11.485 5.481 7.192 1.00 41.20 O +HETATM 690 O3' OMC A 32 9.107 4.992 4.525 0.86 37.82 O +HETATM 691 C5' OMC A 32 12.122 4.059 5.366 1.00 34.16 C +HETATM 692 O5' OMC A 32 12.681 5.049 4.477 1.00 30.92 O +HETATM 693 P OMC A 32 14.197 4.998 3.976 1.00 40.89 P +HETATM 694 OP1 OMC A 32 14.423 3.935 2.941 1.00 30.60 O +HETATM 695 OP2 OMC A 32 14.720 6.313 3.507 1.00 32.99 O +ATOM 696 P U A 33 9.200 5.127 2.925 0.86 37.78 P +ATOM 697 OP1 U A 33 8.581 3.930 2.263 0.86 32.75 O +ATOM 698 OP2 U A 33 10.663 5.217 2.667 0.86 34.57 O +ATOM 699 O5' U A 33 8.387 6.465 2.602 0.86 34.32 O +ATOM 700 C5' U A 33 6.975 6.359 2.295 0.70 33.58 C +ATOM 701 C4' U A 33 6.232 7.421 3.071 0.70 30.43 C +ATOM 702 O4' U A 33 7.032 7.899 4.137 0.70 31.83 O +ATOM 703 C3' U A 33 5.886 8.675 2.263 0.70 30.27 C +ATOM 704 O3' U A 33 4.730 8.529 1.438 0.69 33.13 O +ATOM 705 C2' U A 33 5.709 9.720 3.362 0.70 30.04 C +ATOM 706 O2' U A 33 4.394 9.574 3.895 0.70 25.89 O +ATOM 707 C1' U A 33 6.765 9.276 4.364 0.70 29.86 C +ATOM 708 N1 U A 33 7.951 10.125 4.121 1.00 27.37 N +ATOM 709 C2 U A 33 7.911 11.418 4.622 1.00 25.07 C +ATOM 710 O2 U A 33 6.932 11.823 5.236 1.00 22.79 O +ATOM 711 N3 U A 33 9.004 12.205 4.396 1.00 24.34 N +ATOM 712 C4 U A 33 10.093 11.761 3.717 1.00 24.68 C +ATOM 713 O4 U A 33 11.049 12.582 3.556 1.00 30.20 O +ATOM 714 C5 U A 33 10.113 10.434 3.200 1.00 22.57 C +ATOM 715 C6 U A 33 9.037 9.670 3.426 1.00 24.36 C +HETATM 716 P OMG A 34 4.883 7.809 0.000 0.69 39.54 P +HETATM 717 OP1 OMG A 34 3.857 6.713 -0.097 0.69 37.74 O +HETATM 718 OP2 OMG A 34 6.289 7.326 -0.016 0.69 31.79 O +HETATM 719 O5' OMG A 34 4.610 8.945 -1.083 0.69 33.84 O +HETATM 720 C5' OMG A 34 5.699 9.496 -1.891 0.79 30.14 C +HETATM 721 C4' OMG A 34 5.370 10.974 -1.972 0.79 28.23 C +HETATM 722 O4' OMG A 34 3.971 11.115 -2.101 0.79 24.42 O +HETATM 723 C3' OMG A 34 5.736 11.773 -0.727 0.79 29.46 C +HETATM 724 O3' OMG A 34 7.108 12.149 -0.711 1.00 36.37 O +HETATM 725 C2' OMG A 34 4.777 12.959 -0.824 0.79 26.04 C +HETATM 726 O2' OMG A 34 5.343 13.892 -1.729 0.79 27.05 O +HETATM 727 CM2 OMG A 34 4.917 14.651 -2.683 0.79 23.06 C +HETATM 728 C1' OMG A 34 3.554 12.307 -1.406 0.79 24.20 C +HETATM 729 N9 OMG A 34 2.588 11.919 -0.372 0.93 22.94 N +HETATM 730 C8 OMG A 34 2.225 10.637 -0.032 0.93 20.77 C +HETATM 731 N7 OMG A 34 1.336 10.581 0.921 0.93 23.23 N +HETATM 732 C5 OMG A 34 1.099 11.919 1.245 0.93 23.53 C +HETATM 733 C6 OMG A 34 0.240 12.526 2.198 0.93 21.69 C +HETATM 734 O6 OMG A 34 -0.516 11.958 2.990 0.93 20.60 O +HETATM 735 N1 OMG A 34 0.283 13.898 2.198 0.93 20.10 N +HETATM 736 C2 OMG A 34 1.086 14.606 1.374 0.93 20.29 C +HETATM 737 N2 OMG A 34 1.003 15.938 1.503 0.93 21.41 N +HETATM 738 N3 OMG A 34 1.912 14.094 0.452 0.93 16.67 N +HETATM 739 C4 OMG A 34 1.865 12.751 0.452 1.00 22.66 C +ATOM 740 P A A 35 7.878 12.683 0.598 1.00 42.69 P +ATOM 741 OP1 A A 35 9.204 11.992 0.727 1.00 40.46 O +ATOM 742 OP2 A A 35 6.982 12.374 1.745 1.00 32.74 O +ATOM 743 O5' A A 35 8.044 14.252 0.323 1.00 29.05 O +ATOM 744 C5' A A 35 8.411 15.050 1.471 1.00 32.39 C +ATOM 745 C4' A A 35 7.208 15.865 1.891 1.00 31.49 C +ATOM 746 O4' A A 35 5.996 15.314 1.406 1.00 32.06 O +ATOM 747 C3' A A 35 6.995 15.944 3.410 1.00 31.01 C +ATOM 748 O3' A A 35 7.878 16.883 4.041 1.00 32.13 O +ATOM 749 C2' A A 35 5.519 16.309 3.507 1.00 31.04 C +ATOM 750 O2' A A 35 5.400 17.709 3.297 1.00 33.84 O +ATOM 751 C1' A A 35 4.926 15.539 2.327 1.00 30.29 C +ATOM 752 N9 A A 35 4.287 14.325 2.845 1.00 28.33 N +ATOM 753 C8 A A 35 4.427 13.026 2.441 1.00 30.43 C +ATOM 754 N7 A A 35 3.697 12.172 3.135 1.00 29.55 N +ATOM 755 C5 A A 35 3.031 12.976 4.057 1.00 26.03 C +ATOM 756 C6 A A 35 2.102 12.661 5.075 1.00 29.14 C +ATOM 757 N6 A A 35 1.679 11.435 5.333 1.00 30.66 N +ATOM 758 N1 A A 35 1.645 13.729 5.786 1.00 29.22 N +ATOM 759 C2 A A 35 2.065 15.000 5.527 1.00 28.74 C +ATOM 760 N3 A A 35 2.935 15.359 4.590 1.00 24.86 N +ATOM 761 C4 A A 35 3.378 14.302 3.879 1.00 25.40 C +ATOM 762 P A A 36 9.124 16.337 4.913 1.00 32.39 P +ATOM 763 OP1 A A 36 9.686 17.451 5.754 1.00 27.38 O +ATOM 764 OP2 A A 36 10.080 15.877 3.879 1.00 26.17 O +ATOM 765 O5' A A 36 8.481 15.157 5.770 1.00 29.40 O +ATOM 766 C5' A A 36 8.387 15.163 7.208 1.00 29.36 C +ATOM 767 C4' A A 36 7.045 15.764 7.580 1.00 29.96 C +ATOM 768 O4' A A 36 6.016 15.247 6.740 1.00 26.55 O +ATOM 769 C3' A A 36 6.582 15.444 9.002 1.00 31.22 C +ATOM 770 O3' A A 36 7.198 16.276 9.988 1.00 34.74 O +ATOM 771 C2' A A 36 5.076 15.640 8.905 1.00 28.02 C +ATOM 772 O2' A A 36 4.807 17.035 9.002 1.00 31.89 O +ATOM 773 C1' A A 36 4.793 15.146 7.483 1.00 27.57 C +ATOM 774 N9 A A 36 4.250 13.780 7.580 1.00 28.30 N +ATOM 775 C8 A A 36 4.620 12.655 6.901 1.00 28.20 C +ATOM 776 N7 A A 36 3.921 11.593 7.224 1.00 30.17 N +ATOM 777 C5 A A 36 3.024 12.059 8.178 1.00 28.26 C +ATOM 778 C6 A A 36 2.005 11.396 8.905 1.00 32.35 C +ATOM 779 N6 A A 36 1.715 10.108 8.776 1.00 31.13 N +ATOM 780 N1 A A 36 1.309 12.189 9.778 1.00 31.00 N +ATOM 781 C2 A A 36 1.589 13.515 9.924 1.00 27.16 C +ATOM 782 N3 A A 36 2.532 14.190 9.261 1.00 28.43 N +ATOM 783 C4 A A 36 3.211 13.403 8.404 1.00 27.49 C +HETATM 784 N1 YG A 37 2.585 8.422 12.186 0.67 32.84 N +HETATM 785 N2 YG A 37 1.199 8.422 13.932 0.67 33.74 N +HETATM 786 C2 YG A 37 2.125 9.243 13.140 0.67 32.65 C +HETATM 787 N3 YG A 37 2.418 10.530 13.366 0.67 34.48 N +HETATM 788 C3 YG A 37 1.862 11.233 14.400 0.67 34.99 C +HETATM 789 C4 YG A 37 3.304 10.963 12.445 0.67 32.20 C +HETATM 790 C5 YG A 37 3.857 10.260 11.410 0.67 32.03 C +HETATM 791 C6 YG A 37 3.458 8.894 11.281 0.67 32.23 C +HETATM 792 O6 YG A 37 3.887 8.124 10.392 0.67 34.22 O +HETATM 793 N7 YG A 37 4.713 11.014 10.667 0.67 34.64 N +HETATM 794 C8 YG A 37 4.670 12.194 11.265 0.67 33.25 C +HETATM 795 N9 YG A 37 3.817 12.217 12.364 0.67 31.28 N +HETATM 796 C10 YG A 37 0.453 6.044 13.786 0.67 33.29 C +HETATM 797 C11 YG A 37 1.242 7.241 13.334 0.67 35.54 C +HETATM 798 C12 YG A 37 2.099 7.219 12.251 0.67 35.82 C +HETATM 799 C13 YG A 37 2.382 6.055 11.330 0.67 36.24 C +HETATM 800 C14 YG A 37 1.612 6.297 10.053 0.67 37.78 C +HETATM 801 C15 YG A 37 0.716 5.155 9.729 0.67 37.43 C +HETATM 802 C16 YG A 37 -0.723 5.583 9.713 0.67 39.34 C +HETATM 803 O17 YG A 37 -1.036 6.774 9.616 0.67 41.33 O +HETATM 804 O18 YG A 37 -1.762 4.593 9.810 0.67 39.75 O +HETATM 805 C19 YG A 37 -2.355 4.677 11.103 0.67 37.00 C +HETATM 806 N20 YG A 37 1.033 4.689 8.356 0.67 37.87 N +HETATM 807 C21 YG A 37 1.006 5.560 7.370 0.67 37.22 C +HETATM 808 O22 YG A 37 0.486 6.668 7.499 0.67 34.82 O +HETATM 809 O23 YG A 37 1.589 5.217 6.109 0.67 37.80 O +HETATM 810 C24 YG A 37 0.573 5.161 5.107 0.67 38.27 C +HETATM 811 C1' YG A 37 3.571 13.347 13.204 1.00 35.07 C +HETATM 812 C2' YG A 37 4.427 13.234 14.465 1.00 35.84 C +HETATM 813 O2' YG A 37 3.784 13.847 15.564 1.00 32.99 O +HETATM 814 C3' YG A 37 5.626 14.100 14.109 1.00 36.71 C +HETATM 815 O3' YG A 37 6.346 14.538 15.273 1.00 40.56 O +HETATM 816 C4' YG A 37 4.977 15.213 13.285 1.00 36.00 C +HETATM 817 O4' YG A 37 3.951 14.561 12.558 1.00 37.29 O +HETATM 818 C5' YG A 37 5.892 15.944 12.332 1.00 32.52 C +HETATM 819 O5' YG A 37 7.035 15.056 12.138 1.00 34.35 O +HETATM 820 P YG A 37 8.131 15.618 11.120 1.00 45.47 P +HETATM 821 OP1 YG A 37 8.964 16.675 11.782 1.00 43.57 O +HETATM 822 OP2 YG A 37 8.914 14.617 10.360 1.00 39.46 O +ATOM 823 P A A 38 7.718 13.751 15.613 1.00 42.64 P +ATOM 824 OP1 A A 38 8.821 14.696 15.984 1.00 37.89 O +ATOM 825 OP2 A A 38 8.021 13.043 14.336 1.00 39.37 O +ATOM 826 O5' A A 38 7.318 12.790 16.825 1.00 32.99 O +ATOM 827 C5' A A 38 6.152 13.037 17.649 1.00 30.76 C +ATOM 828 C4' A A 38 5.330 11.761 17.617 1.00 33.37 C +ATOM 829 O4' A A 38 4.767 11.626 16.324 1.00 32.03 O +ATOM 830 C3' A A 38 6.089 10.457 17.827 1.00 33.48 C +ATOM 831 O3' A A 38 6.339 10.103 19.201 1.00 33.69 O +ATOM 832 C2' A A 38 5.166 9.445 17.148 1.00 31.61 C +ATOM 833 O2' A A 38 4.120 9.141 18.053 1.00 33.00 O +ATOM 834 C1' A A 38 4.653 10.255 15.984 1.00 30.13 C +ATOM 835 N9 A A 38 5.493 9.884 14.821 1.00 27.03 N +ATOM 836 C8 A A 38 6.402 10.659 14.158 1.00 26.03 C +ATOM 837 N7 A A 38 7.005 10.035 13.172 1.00 24.70 N +ATOM 838 C5 A A 38 6.459 8.759 13.204 1.00 26.51 C +ATOM 839 C6 A A 38 6.692 7.618 12.412 1.00 27.76 C +ATOM 840 N6 A A 38 7.558 7.584 11.410 1.00 30.99 N +ATOM 841 N1 A A 38 5.956 6.516 12.736 1.00 28.54 N +ATOM 842 C2 A A 38 5.063 6.533 13.770 1.00 30.31 C +ATOM 843 N3 A A 38 4.797 7.573 14.562 1.00 30.93 N +ATOM 844 C4 A A 38 5.530 8.652 14.223 1.00 27.66 C +HETATM 845 N1 PSU A 39 9.443 9.659 14.917 1.00 32.19 N +HETATM 846 C2 PSU A 39 10.389 9.490 13.964 1.00 31.34 C +HETATM 847 N3 PSU A 39 10.656 8.180 13.657 1.00 29.52 N +HETATM 848 C4 PSU A 39 10.070 7.061 14.223 1.00 33.42 C +HETATM 849 C5 PSU A 39 9.070 7.331 15.241 1.00 30.22 C +HETATM 850 C6 PSU A 39 8.804 8.618 15.532 1.00 32.36 C +HETATM 851 O2 PSU A 39 10.972 10.418 13.398 1.00 32.92 O +HETATM 852 O4 PSU A 39 10.369 5.920 13.899 1.00 36.50 O +HETATM 853 C1' PSU A 39 8.371 6.190 15.903 1.00 29.21 C +HETATM 854 C2' PSU A 39 9.283 5.189 16.615 1.00 32.27 C +HETATM 855 O2' PSU A 39 8.747 3.874 16.534 1.00 33.25 O +HETATM 856 C3' PSU A 39 9.204 5.633 18.069 1.00 33.21 C +HETATM 857 C4' PSU A 39 7.751 6.100 18.166 1.00 34.51 C +HETATM 858 O3' PSU A 39 9.503 4.604 19.007 1.00 34.14 O +HETATM 859 O4' PSU A 39 7.475 6.696 16.922 1.00 34.97 O +HETATM 860 C5' PSU A 39 7.481 7.089 19.297 1.00 33.91 C +HETATM 861 O5' PSU A 39 8.141 8.321 18.910 1.00 32.59 O +HETATM 862 P PSU A 39 7.841 9.698 19.621 1.00 32.28 P +HETATM 863 OP1 PSU A 39 7.951 9.602 21.124 1.00 37.67 O +HETATM 864 OP2 PSU A 39 8.651 10.834 19.087 1.00 32.24 O +HETATM 865 P 5MC A 40 10.782 4.486 19.960 1.00 34.43 P +HETATM 866 OP1 5MC A 40 10.449 3.671 21.172 1.00 30.68 O +HETATM 867 OP2 5MC A 40 11.089 5.892 20.332 1.00 28.93 O +HETATM 868 O5' 5MC A 40 11.885 3.778 19.039 1.00 28.81 O +HETATM 869 C5' 5MC A 40 11.728 2.401 18.619 1.00 28.35 C +HETATM 870 C4' 5MC A 40 12.548 2.221 17.358 1.00 29.61 C +HETATM 871 O4' 5MC A 40 11.988 3.002 16.308 1.00 29.56 O +HETATM 872 C3' 5MC A 40 13.997 2.687 17.439 1.00 31.68 C +HETATM 873 O3' 5MC A 40 14.873 1.743 18.069 1.00 35.29 O +HETATM 874 C2' 5MC A 40 14.347 2.946 15.968 1.00 27.14 C +HETATM 875 O2' 5MC A 40 14.696 1.692 15.386 1.00 23.50 O +HETATM 876 C1' 5MC A 40 13.024 3.469 15.435 1.00 25.42 C +HETATM 877 N1 5MC A 40 13.091 4.942 15.418 1.00 19.88 N +HETATM 878 C2 5MC A 40 13.940 5.526 14.497 1.00 20.27 C +HETATM 879 O2 5MC A 40 14.603 4.835 13.722 1.00 20.75 O +HETATM 880 N3 5MC A 40 14.010 6.893 14.465 1.00 20.93 N +HETATM 881 C4 5MC A 40 13.284 7.668 15.322 1.00 18.76 C +HETATM 882 N4 5MC A 40 13.384 9.001 15.257 1.00 17.83 N +HETATM 883 C5 5MC A 40 12.418 7.056 16.259 1.00 18.58 C +HETATM 884 C6 5MC A 40 12.358 5.718 16.275 1.00 19.25 C +HETATM 885 CM5 5MC A 40 11.608 7.905 17.196 1.00 21.21 C +ATOM 886 P U A 41 16.185 2.215 18.877 1.00 35.32 P +ATOM 887 OP1 U A 41 16.658 1.124 19.799 1.00 26.86 O +ATOM 888 OP2 U A 41 15.739 3.429 19.621 1.00 33.21 O +ATOM 889 O5' U A 41 17.235 2.513 17.714 1.00 26.80 O +ATOM 890 C5' U A 41 17.698 1.389 16.906 1.00 27.18 C +ATOM 891 C4' U A 41 18.850 1.934 16.081 1.00 23.05 C +ATOM 892 O4' U A 41 18.314 2.699 15.014 1.00 20.17 O +ATOM 893 C3' U A 41 19.789 2.878 16.825 1.00 24.93 C +ATOM 894 O3' U A 41 20.799 2.237 17.600 1.00 27.91 O +ATOM 895 C2' U A 41 20.382 3.682 15.661 1.00 27.05 C +ATOM 896 O2' U A 41 21.435 2.890 15.111 1.00 26.81 O +ATOM 897 C1' U A 41 19.193 3.772 14.724 1.00 23.28 C +ATOM 898 N1 U A 41 18.554 5.077 14.966 1.00 24.30 N +ATOM 899 C2 U A 41 19.077 6.179 14.303 1.00 28.89 C +ATOM 900 O2 U A 41 20.033 6.055 13.544 1.00 29.05 O +ATOM 901 N3 U A 41 18.487 7.393 14.546 1.00 24.96 N +ATOM 902 C4 U A 41 17.428 7.528 15.386 1.00 22.40 C +ATOM 903 O4 U A 41 16.958 8.697 15.532 1.00 24.28 O +ATOM 904 C5 U A 41 16.908 6.387 16.065 1.00 22.17 C +ATOM 905 C6 U A 41 17.498 5.206 15.823 1.00 23.91 C +ATOM 906 P G A 42 21.595 3.036 18.764 1.00 31.63 P +ATOM 907 OP1 G A 42 22.384 2.075 19.604 1.00 28.12 O +ATOM 908 OP2 G A 42 20.516 3.699 19.556 1.00 22.68 O +ATOM 909 O5' G A 42 22.551 4.031 17.972 1.00 19.53 O +ATOM 910 C5' G A 42 23.583 3.519 17.099 1.00 19.97 C +ATOM 911 C4' G A 42 24.146 4.717 16.356 1.00 22.10 C +ATOM 912 O4' G A 42 23.084 5.375 15.661 1.00 26.60 O +ATOM 913 C3' G A 42 24.776 5.807 17.212 1.00 19.91 C +ATOM 914 O3' G A 42 26.128 5.560 17.617 1.00 20.68 O +ATOM 915 C2' G A 42 24.659 7.028 16.291 1.00 21.26 C +ATOM 916 O2' G A 42 25.732 6.966 15.370 1.00 21.51 O +ATOM 917 C1' G A 42 23.337 6.774 15.596 1.00 23.91 C +ATOM 918 N9 G A 42 22.324 7.578 16.308 1.00 18.48 N +ATOM 919 C8 G A 42 21.332 7.129 17.148 1.00 18.18 C +ATOM 920 N7 G A 42 20.586 8.084 17.633 1.00 18.52 N +ATOM 921 C5 G A 42 21.125 9.243 17.099 1.00 16.12 C +ATOM 922 C6 G A 42 20.759 10.609 17.245 1.00 16.98 C +ATOM 923 O6 G A 42 19.836 11.059 17.924 1.00 18.24 O +ATOM 924 N1 G A 42 21.542 11.480 16.534 1.00 16.87 N +ATOM 925 C2 G A 42 22.577 11.070 15.758 1.00 17.61 C +ATOM 926 N2 G A 42 23.234 12.059 15.128 1.00 17.39 N +ATOM 927 N3 G A 42 22.964 9.805 15.580 1.00 18.80 N +ATOM 928 C4 G A 42 22.194 8.945 16.275 1.00 17.14 C +ATOM 929 P G A 43 26.715 6.263 18.958 1.00 25.50 P +ATOM 930 OP1 G A 43 27.960 5.577 19.427 1.00 18.73 O +ATOM 931 OP2 G A 43 25.605 6.100 19.944 1.00 18.55 O +ATOM 932 O5' G A 43 27.021 7.758 18.506 1.00 23.39 O +ATOM 933 C5' G A 43 27.791 8.073 17.309 1.00 22.77 C +ATOM 934 C4' G A 43 27.774 9.585 17.164 1.00 21.17 C +ATOM 935 O4' G A 43 26.495 10.007 16.695 1.00 22.41 O +ATOM 936 C3' G A 43 27.980 10.361 18.457 1.00 19.66 C +ATOM 937 O3' G A 43 29.349 10.502 18.861 1.00 20.78 O +ATOM 938 C2' G A 43 27.337 11.705 18.118 1.00 18.90 C +ATOM 939 O2' G A 43 28.310 12.430 17.374 1.00 23.35 O +ATOM 940 C1' G A 43 26.178 11.278 17.245 1.00 17.39 C +ATOM 941 N9 G A 43 24.993 11.227 18.118 1.00 14.19 N +ATOM 942 C8 G A 43 24.486 10.108 18.732 1.00 14.07 C +ATOM 943 N7 G A 43 23.427 10.339 19.459 1.00 14.63 N +ATOM 944 C5 G A 43 23.220 11.711 19.314 1.00 13.86 C +ATOM 945 C6 G A 43 22.231 12.571 19.847 1.00 17.13 C +ATOM 946 O6 G A 43 21.298 12.250 20.607 1.00 19.62 O +ATOM 947 N1 G A 43 22.354 13.881 19.475 1.00 12.99 N +ATOM 948 C2 G A 43 23.344 14.314 18.651 1.00 12.94 C +ATOM 949 N2 G A 43 23.314 15.629 18.376 1.00 14.10 N +ATOM 950 N3 G A 43 24.296 13.549 18.118 1.00 14.82 N +ATOM 951 C4 G A 43 24.176 12.262 18.489 1.00 12.94 C +ATOM 952 P A A 44 29.649 10.783 20.429 1.00 28.67 P +ATOM 953 OP1 A A 44 30.902 10.097 20.881 1.00 27.63 O +ATOM 954 OP2 A A 44 28.460 10.193 21.124 1.00 25.89 O +ATOM 955 O5' A A 44 29.749 12.363 20.526 1.00 21.80 O +ATOM 956 C5' A A 44 30.465 13.195 19.572 1.00 18.26 C +ATOM 957 C4' A A 44 29.886 14.595 19.685 1.00 17.95 C +ATOM 958 O4' A A 44 28.517 14.538 19.297 1.00 14.43 O +ATOM 959 C3' A A 44 29.872 15.213 21.075 1.00 20.30 C +ATOM 960 O3' A A 44 31.111 15.815 21.479 1.00 24.13 O +ATOM 961 C2' A A 44 28.736 16.231 20.946 1.00 15.67 C +ATOM 962 O2' A A 44 29.266 17.389 20.316 1.00 20.20 O +ATOM 963 C1' A A 44 27.777 15.505 20.041 1.00 13.70 C +ATOM 964 N9 A A 44 26.755 14.859 20.865 1.00 14.55 N +ATOM 965 C8 A A 44 26.731 13.560 21.318 1.00 13.46 C +ATOM 966 N7 A A 44 25.689 13.268 22.045 1.00 13.16 N +ATOM 967 C5 A A 44 24.972 14.460 22.094 1.00 12.41 C +ATOM 968 C6 A A 44 23.753 14.797 22.724 1.00 13.52 C +ATOM 969 N6 A A 44 23.034 13.954 23.451 1.00 11.26 N +ATOM 970 N1 A A 44 23.354 16.096 22.546 1.00 15.70 N +ATOM 971 C2 A A 44 24.083 16.978 21.802 1.00 15.78 C +ATOM 972 N3 A A 44 25.236 16.725 21.188 1.00 13.28 N +ATOM 973 C4 A A 44 25.619 15.444 21.366 1.00 15.37 C +ATOM 974 P G A 45 31.541 15.938 23.031 1.00 21.79 P +ATOM 975 OP1 G A 45 32.670 16.911 23.209 1.00 23.16 O +ATOM 976 OP2 G A 45 31.928 14.544 23.386 1.00 18.86 O +ATOM 977 O5' G A 45 30.226 16.489 23.742 1.00 25.81 O +ATOM 978 C5' G A 45 29.699 17.782 23.306 1.00 26.80 C +ATOM 979 C4' G A 45 28.610 18.120 24.308 1.00 23.63 C +ATOM 980 O4' G A 45 27.457 17.361 24.033 1.00 23.89 O +ATOM 981 C3' G A 45 28.970 17.765 25.746 1.00 21.22 C +ATOM 982 O3' G A 45 29.766 18.783 26.344 1.00 24.50 O +ATOM 983 C2' G A 45 27.601 17.619 26.409 1.00 19.23 C +ATOM 984 O2' G A 45 27.148 18.935 26.716 1.00 21.32 O +ATOM 985 C1' G A 45 26.791 17.018 25.277 1.00 19.47 C +ATOM 986 N9 G A 45 26.645 15.556 25.342 1.00 17.65 N +ATOM 987 C8 G A 45 27.471 14.561 24.889 1.00 15.36 C +ATOM 988 N7 G A 45 27.008 13.352 25.083 1.00 12.76 N +ATOM 989 C5 G A 45 25.779 13.566 25.698 1.00 15.59 C +ATOM 990 C6 G A 45 24.786 12.661 26.166 1.00 17.46 C +ATOM 991 O6 G A 45 24.803 11.424 26.118 1.00 18.92 O +ATOM 992 N1 G A 45 23.690 13.268 26.748 1.00 15.79 N +ATOM 993 C2 G A 45 23.570 14.617 26.845 1.00 11.32 C +ATOM 994 N2 G A 45 22.444 15.056 27.427 1.00 9.86 N +ATOM 995 N3 G A 45 24.463 15.511 26.409 1.00 14.42 N +ATOM 996 C4 G A 45 25.539 14.910 25.859 1.00 17.21 C +HETATM 997 P 7MG A 46 30.895 18.485 27.411 1.00 24.75 P +HETATM 998 OP1 7MG A 46 32.028 19.463 27.362 1.00 34.31 O +HETATM 999 OP2 7MG A 46 31.345 17.102 27.087 1.00 36.12 O +HETATM 1000 O5' 7MG A 46 30.142 18.598 28.817 1.00 16.31 O +HETATM 1001 C5' 7MG A 46 31.132 18.446 29.867 1.00 20.52 C +HETATM 1002 C4' 7MG A 46 30.429 18.429 31.193 1.00 15.75 C +HETATM 1003 O4' 7MG A 46 29.563 17.316 31.290 1.00 13.39 O +HETATM 1004 C3' 7MG A 46 31.461 18.322 32.340 1.00 13.44 C +HETATM 1005 O3' 7MG A 46 31.045 19.171 33.439 1.00 19.39 O +HETATM 1006 C2' 7MG A 46 31.401 16.827 32.647 1.00 10.98 C +HETATM 1007 O2' 7MG A 46 31.858 16.624 33.956 1.00 18.01 O +HETATM 1008 C1' 7MG A 46 29.962 16.495 32.421 1.00 13.21 C +HETATM 1009 N9 7MG A 46 29.772 15.073 32.082 1.00 11.97 N +HETATM 1010 C8 7MG A 46 30.642 14.269 31.403 1.00 13.63 C +HETATM 1011 N7 7MG A 46 30.215 13.043 31.241 1.00 11.58 N +HETATM 1012 C5 7MG A 46 28.980 13.032 31.872 1.00 11.10 C +HETATM 1013 C6 7MG A 46 28.020 11.997 32.049 1.00 7.57 C +HETATM 1014 O6 7MG A 46 28.080 10.828 31.661 1.00 5.90 O +HETATM 1015 N1 7MG A 46 26.898 12.380 32.744 1.00 8.38 N +HETATM 1016 C2 7MG A 46 26.731 13.645 33.213 1.00 13.83 C +HETATM 1017 N2 7MG A 46 25.579 13.858 33.859 1.00 18.21 N +HETATM 1018 N3 7MG A 46 27.594 14.651 33.068 1.00 15.33 N +HETATM 1019 C4 7MG A 46 28.697 14.274 32.389 1.00 11.80 C +HETATM 1020 CM7 7MG A 46 30.862 11.936 30.579 1.00 11.28 C +ATOM 1021 P U A 47 31.328 20.762 33.229 1.00 16.71 P +ATOM 1022 OP1 U A 47 30.072 21.549 33.423 1.00 16.42 O +ATOM 1023 OP2 U A 47 31.851 20.835 31.839 1.00 9.53 O +ATOM 1024 O5' U A 47 32.431 21.066 34.344 1.00 21.56 O +ATOM 1025 C5' U A 47 33.773 20.593 34.053 1.00 22.87 C +ATOM 1026 C4' U A 47 34.399 20.166 35.362 1.00 23.82 C +ATOM 1027 O4' U A 47 34.932 21.319 36.009 1.00 28.37 O +ATOM 1028 C3' U A 47 33.427 19.531 36.364 1.00 20.32 C +ATOM 1029 O3' U A 47 34.109 18.508 37.108 1.00 17.51 O +ATOM 1030 C2' U A 47 33.057 20.695 37.253 1.00 24.77 C +ATOM 1031 O2' U A 47 32.611 20.245 38.498 1.00 28.72 O +ATOM 1032 C1' U A 47 34.356 21.448 37.318 1.00 28.41 C +ATOM 1033 N1 U A 47 34.119 22.853 37.690 1.00 34.31 N +ATOM 1034 C2 U A 47 34.986 23.410 38.611 1.00 36.46 C +ATOM 1035 O2 U A 47 35.898 22.741 39.096 1.00 33.40 O +ATOM 1036 N3 U A 47 34.782 24.714 38.967 1.00 37.41 N +ATOM 1037 C4 U A 47 33.770 25.462 38.433 1.00 37.38 C +ATOM 1038 O4 U A 47 33.673 26.660 38.821 1.00 40.42 O +ATOM 1039 C5 U A 47 32.890 24.872 37.480 1.00 35.84 C +ATOM 1040 C6 U A 47 33.104 23.590 37.140 1.00 33.82 C +ATOM 1041 P C A 48 33.403 17.136 37.528 1.00 12.71 P +ATOM 1042 OP1 C A 48 34.429 16.231 38.142 1.00 13.81 O +ATOM 1043 OP2 C A 48 32.850 16.624 36.251 1.00 4.91 O +ATOM 1044 O5' C A 48 32.284 17.558 38.579 1.00 9.32 O +ATOM 1045 C5' C A 48 32.031 16.714 39.726 1.00 8.64 C +ATOM 1046 C4' C A 48 30.552 16.371 39.710 1.00 5.36 C +ATOM 1047 O4' C A 48 30.302 15.320 38.805 1.00 8.35 O +ATOM 1048 C3' C A 48 30.049 15.893 41.084 1.00 1.59 C +ATOM 1049 O3' C A 48 28.713 16.371 41.326 1.00 0.00 O +ATOM 1050 C2' C A 48 30.082 14.375 40.922 1.00 4.65 C +ATOM 1051 O2' C A 48 29.203 13.780 41.811 1.00 8.42 O +ATOM 1052 C1' C A 48 29.696 14.212 39.468 1.00 7.30 C +ATOM 1053 N1 C A 48 30.179 12.908 38.999 1.00 6.93 N +ATOM 1054 C2 C A 48 29.346 11.812 39.144 1.00 7.42 C +ATOM 1055 O2 C A 48 28.234 11.919 39.645 1.00 8.37 O +ATOM 1056 N3 C A 48 29.802 10.597 38.708 1.00 10.80 N +ATOM 1057 C4 C A 48 31.032 10.446 38.142 1.00 9.85 C +ATOM 1058 N4 C A 48 31.411 9.226 37.738 1.00 10.53 N +ATOM 1059 C5 C A 48 31.881 11.570 37.997 1.00 8.70 C +ATOM 1060 C6 C A 48 31.418 12.751 38.433 1.00 7.80 C +HETATM 1061 P 5MC A 49 28.367 17.091 42.716 1.00 0.00 P +HETATM 1062 OP1 5MC A 49 28.883 16.382 43.928 1.00 0.00 O +HETATM 1063 OP2 5MC A 49 26.878 17.074 42.668 1.00 10.83 O +HETATM 1064 O5' 5MC A 49 29.046 18.513 42.603 1.00 8.20 O +HETATM 1065 C5' 5MC A 49 28.560 19.531 41.682 1.00 5.64 C +HETATM 1066 C4' 5MC A 49 29.036 20.852 42.264 1.00 5.09 C +HETATM 1067 O4' 5MC A 49 28.224 21.223 43.363 1.00 4.65 O +HETATM 1068 C3' 5MC A 49 30.452 20.835 42.829 1.00 8.88 C +HETATM 1069 O3' 5MC A 49 31.471 20.953 41.827 1.00 11.57 O +HETATM 1070 C2' 5MC A 49 30.422 22.010 43.799 1.00 7.00 C +HETATM 1071 O2' 5MC A 49 30.599 23.196 43.023 1.00 10.65 O +HETATM 1072 C1' 5MC A 49 29.010 21.931 44.332 1.00 2.41 C +HETATM 1073 N1 5MC A 49 29.013 21.274 45.658 1.00 0.00 N +HETATM 1074 C2 5MC A 49 29.529 22.021 46.708 1.00 3.14 C +HETATM 1075 O2 5MC A 49 29.956 23.163 46.530 1.00 1.70 O +HETATM 1076 N3 5MC A 49 29.549 21.448 47.953 1.00 4.30 N +HETATM 1077 C4 5MC A 49 29.086 20.189 48.163 1.00 0.00 C +HETATM 1078 N4 5MC A 49 29.130 19.671 49.391 1.00 0.00 N +HETATM 1079 C5 5MC A 49 28.563 19.441 47.080 1.00 0.00 C +HETATM 1080 C6 5MC A 49 28.550 20.014 45.868 1.00 0.00 C +HETATM 1081 CM5 5MC A 49 28.047 18.047 47.322 1.00 2.17 C +ATOM 1082 P U A 50 32.904 20.267 42.134 1.00 0.00 P +ATOM 1083 OP1 U A 50 33.660 20.144 40.857 1.00 13.35 O +ATOM 1084 OP2 U A 50 32.534 18.963 42.749 1.00 10.49 O +ATOM 1085 O5' U A 50 33.576 21.268 43.169 1.00 7.33 O +ATOM 1086 C5' U A 50 33.583 22.707 42.942 1.00 4.60 C +ATOM 1087 C4' U A 50 34.376 23.303 44.090 1.00 0.00 C +ATOM 1088 O4' U A 50 33.540 23.489 45.205 1.00 1.82 O +ATOM 1089 C3' U A 50 35.508 22.404 44.607 1.00 4.48 C +ATOM 1090 O3' U A 50 36.658 22.488 43.783 1.00 3.57 O +ATOM 1091 C2' U A 50 35.695 22.932 46.029 1.00 1.05 C +ATOM 1092 O2' U A 50 36.504 24.096 45.949 1.00 8.34 O +ATOM 1093 C1' U A 50 34.276 23.281 46.417 1.00 0.00 C +ATOM 1094 N1 U A 50 33.656 22.184 47.177 1.00 0.00 N +ATOM 1095 C2 U A 50 33.830 22.151 48.551 1.00 3.12 C +ATOM 1096 O2 U A 50 34.489 23.017 49.116 1.00 5.90 O +ATOM 1097 N3 U A 50 33.250 21.122 49.229 1.00 0.00 N +ATOM 1098 C4 U A 50 32.521 20.155 48.615 1.00 0.00 C +ATOM 1099 O4 U A 50 32.018 19.244 49.343 1.00 4.06 O +ATOM 1100 C5 U A 50 32.347 20.205 47.193 1.00 0.00 C +ATOM 1101 C6 U A 50 32.924 21.217 46.547 1.00 0.00 C +ATOM 1102 P G A 51 38.133 21.948 44.042 1.00 0.00 P +ATOM 1103 OP1 G A 51 39.093 22.600 43.104 1.00 9.19 O +ATOM 1104 OP2 G A 51 38.007 20.498 43.767 1.00 8.40 O +ATOM 1105 O5' G A 51 38.456 22.297 45.577 1.00 0.39 O +ATOM 1106 C5' G A 51 39.552 23.163 45.916 1.00 0.51 C +ATOM 1107 C4' G A 51 39.842 23.022 47.403 1.00 0.23 C +ATOM 1108 O4' G A 51 38.630 22.955 48.147 1.00 3.84 O +ATOM 1109 C3' G A 51 40.602 21.751 47.791 1.00 4.20 C +ATOM 1110 O3' G A 51 42.001 21.864 47.581 1.00 12.57 O +ATOM 1111 C2' G A 51 40.229 21.605 49.278 1.00 6.01 C +ATOM 1112 O2' G A 51 41.078 22.471 50.005 1.00 9.05 O +ATOM 1113 C1' G A 51 38.793 22.106 49.294 1.00 7.48 C +ATOM 1114 N9 G A 51 37.820 20.993 49.294 1.00 11.98 N +ATOM 1115 C8 G A 51 37.204 20.391 48.227 1.00 8.25 C +ATOM 1116 N7 G A 51 36.378 19.430 48.551 1.00 4.30 N +ATOM 1117 C5 G A 51 36.451 19.402 49.941 1.00 0.00 C +ATOM 1118 C6 G A 51 35.795 18.581 50.894 1.00 4.48 C +ATOM 1119 O6 G A 51 34.975 17.681 50.684 1.00 7.97 O +ATOM 1120 N1 G A 51 36.128 18.851 52.203 1.00 3.73 N +ATOM 1121 C2 G A 51 37.014 19.823 52.543 1.00 4.10 C +ATOM 1122 N2 G A 51 37.231 19.964 53.868 1.00 6.12 N +ATOM 1123 N3 G A 51 37.660 20.622 51.686 1.00 6.39 N +ATOM 1124 C4 G A 51 37.331 20.352 50.409 1.00 4.38 C +ATOM 1125 P U A 52 43.056 20.689 47.322 1.00 12.37 P +ATOM 1126 OP1 U A 52 44.372 21.217 46.854 1.00 16.93 O +ATOM 1127 OP2 U A 52 42.427 19.834 46.288 1.00 17.95 O +ATOM 1128 O5' U A 52 43.193 20.037 48.777 1.00 7.70 O +ATOM 1129 C5' U A 52 43.709 20.914 49.811 1.00 8.29 C +ATOM 1130 C4' U A 52 43.466 20.250 51.137 1.00 7.91 C +ATOM 1131 O4' U A 52 42.070 20.256 51.428 1.00 14.10 O +ATOM 1132 C3' U A 52 43.859 18.778 51.234 1.00 7.92 C +ATOM 1133 O3' U A 52 45.258 18.553 51.444 1.00 11.59 O +ATOM 1134 C2' U A 52 42.983 18.305 52.397 1.00 5.26 C +ATOM 1135 O2' U A 52 43.656 18.710 53.577 1.00 0.00 O +ATOM 1136 C1' U A 52 41.721 19.092 52.187 1.00 7.18 C +ATOM 1137 N1 U A 52 40.735 18.260 51.476 1.00 1.28 N +ATOM 1138 C2 U A 52 40.072 17.288 52.203 1.00 1.93 C +ATOM 1139 O2 U A 52 40.298 17.125 53.399 1.00 1.97 O +ATOM 1140 N3 U A 52 39.159 16.512 51.541 1.00 1.62 N +ATOM 1141 C4 U A 52 38.893 16.669 50.215 1.00 6.76 C +ATOM 1142 O4 U A 52 38.033 15.910 49.682 1.00 3.70 O +ATOM 1143 C5 U A 52 39.589 17.681 49.488 1.00 5.38 C +ATOM 1144 C6 U A 52 40.478 18.434 50.151 1.00 0.00 C +ATOM 1145 P G A 53 45.921 17.231 50.781 1.00 17.98 P +ATOM 1146 OP1 G A 53 47.417 17.333 50.846 1.00 26.93 O +ATOM 1147 OP2 G A 53 45.422 17.226 49.391 1.00 17.10 O +ATOM 1148 O5' G A 53 45.375 16.017 51.654 1.00 7.43 O +ATOM 1149 C5' G A 53 46.124 15.623 52.850 1.00 7.82 C +ATOM 1150 C4' G A 53 45.138 14.994 53.803 1.00 9.56 C +ATOM 1151 O4' G A 53 43.843 15.539 53.593 1.00 8.28 O +ATOM 1152 C3' G A 53 44.945 13.487 53.658 1.00 7.57 C +ATOM 1153 O3' G A 53 45.948 12.694 54.288 1.00 10.26 O +ATOM 1154 C2' G A 53 43.563 13.296 54.288 1.00 0.85 C +ATOM 1155 O2' G A 53 43.789 13.257 55.694 1.00 0.00 O +ATOM 1156 C1' G A 53 42.847 14.555 53.852 1.00 0.58 C +ATOM 1157 N9 G A 53 42.050 14.263 52.656 1.00 0.32 N +ATOM 1158 C8 G A 53 42.164 14.831 51.411 1.00 4.73 C +ATOM 1159 N7 G A 53 41.311 14.359 50.539 1.00 5.02 N +ATOM 1160 C5 G A 53 40.588 13.408 51.250 1.00 1.44 C +ATOM 1161 C6 G A 53 39.522 12.554 50.862 1.00 0.00 C +ATOM 1162 O6 G A 53 38.993 12.470 49.747 1.00 1.53 O +ATOM 1163 N1 G A 53 39.059 11.744 51.880 1.00 0.04 N +ATOM 1164 C2 G A 53 39.579 11.767 53.125 1.00 0.66 C +ATOM 1165 N2 G A 53 39.019 10.912 53.997 1.00 0.50 N +ATOM 1166 N3 G A 53 40.578 12.548 53.545 1.00 2.56 N +ATOM 1167 C4 G A 53 41.031 13.341 52.559 1.00 1.75 C +HETATM 1168 N1 5MU A 54 41.857 8.978 52.284 1.00 0.00 N +HETATM 1169 C2 5MU A 54 40.798 8.439 51.573 1.00 0.00 C +HETATM 1170 N3 5MU A 54 40.625 8.860 50.280 1.00 0.00 N +HETATM 1171 C4 5MU A 54 41.444 9.777 49.698 1.00 0.00 C +HETATM 1172 C5 5MU A 54 42.523 10.316 50.458 1.00 0.00 C +HETATM 1173 C5M 5MU A 54 43.446 11.328 49.844 1.00 0.00 C +HETATM 1174 C6 5MU A 54 42.687 9.895 51.718 1.00 0.00 C +HETATM 1175 O2 5MU A 54 40.042 7.612 52.074 1.00 0.71 O +HETATM 1176 O4 5MU A 54 41.194 10.103 48.502 1.00 0.00 O +HETATM 1177 C1' 5MU A 54 42.014 8.500 53.674 1.00 3.38 C +HETATM 1178 C2' 5MU A 54 42.910 7.280 53.706 1.00 5.21 C +HETATM 1179 O2' 5MU A 54 42.580 6.364 54.741 1.00 4.00 O +HETATM 1180 C3' 5MU A 54 44.249 7.899 54.110 1.00 8.98 C +HETATM 1181 C4' 5MU A 54 43.819 9.001 55.064 1.00 7.57 C +HETATM 1182 O3' 5MU A 54 45.135 6.949 54.692 1.00 6.80 O +HETATM 1183 O4' 5MU A 54 42.613 9.495 54.514 1.00 7.24 O +HETATM 1184 C5' 5MU A 54 44.835 10.120 55.209 1.00 2.64 C +HETATM 1185 O5' 5MU A 54 45.342 10.288 53.852 1.00 0.43 O +HETATM 1186 P 5MU A 54 46.487 11.345 53.577 1.00 5.60 P +HETATM 1187 OP1 5MU A 54 47.793 10.940 54.191 1.00 8.01 O +HETATM 1188 OP2 5MU A 54 46.634 11.750 52.155 1.00 0.00 O +HETATM 1189 N1 PSU A 55 44.782 8.191 49.391 1.00 0.00 N +HETATM 1190 C2 PSU A 55 44.459 8.703 48.179 1.00 0.00 C +HETATM 1191 N3 PSU A 55 43.649 7.893 47.436 1.00 1.25 N +HETATM 1192 C4 PSU A 55 43.150 6.651 47.791 1.00 0.00 C +HETATM 1193 C5 PSU A 55 43.546 6.184 49.100 1.00 0.00 C +HETATM 1194 C6 PSU A 55 44.346 6.977 49.827 1.00 3.80 C +HETATM 1195 O2 PSU A 55 44.852 9.793 47.775 1.00 0.98 O +HETATM 1196 O4 PSU A 55 42.430 5.987 47.064 1.00 5.16 O +HETATM 1197 C1' PSU A 55 43.063 4.852 49.569 1.00 1.54 C +HETATM 1198 C2' PSU A 55 43.886 3.716 48.987 1.00 0.00 C +HETATM 1199 O2' PSU A 55 43.086 2.558 48.809 1.00 0.00 O +HETATM 1200 C3' PSU A 55 44.862 3.446 50.134 1.00 0.13 C +HETATM 1201 C4' PSU A 55 43.989 3.694 51.363 1.00 0.00 C +HETATM 1202 O3' PSU A 55 45.428 2.136 50.118 1.00 7.95 O +HETATM 1203 O4' PSU A 55 43.163 4.779 50.991 1.00 4.08 O +HETATM 1204 C5' PSU A 55 44.765 4.031 52.607 1.00 2.64 C +HETATM 1205 O5' PSU A 55 44.938 5.470 52.656 1.00 1.06 O +HETATM 1206 P PSU A 55 46.011 6.027 53.706 1.00 10.33 P +HETATM 1207 OP1 PSU A 55 46.671 4.976 54.547 1.00 11.01 O +HETATM 1208 OP2 PSU A 55 46.980 6.932 53.044 1.00 1.86 O +ATOM 1209 P C A 56 47.040 2.058 49.941 1.00 1.09 P +ATOM 1210 OP1 C A 56 47.607 0.900 50.700 1.00 10.69 O +ATOM 1211 OP2 C A 56 47.507 3.345 50.506 1.00 11.24 O +ATOM 1212 O5' C A 56 47.177 1.951 48.373 1.00 0.00 O +ATOM 1213 C5' C A 56 48.156 2.789 47.710 1.00 0.00 C +ATOM 1214 C4' C A 56 47.963 2.502 46.239 1.00 0.00 C +ATOM 1215 O4' C A 56 47.670 1.136 46.046 1.00 0.00 O +ATOM 1216 C3' C A 56 46.787 3.250 45.609 1.00 4.26 C +ATOM 1217 O3' C A 56 47.114 4.610 45.302 1.00 6.56 O +ATOM 1218 C2' C A 56 46.481 2.378 44.397 1.00 0.33 C +ATOM 1219 O2' C A 56 47.410 2.710 43.363 1.00 8.88 O +ATOM 1220 C1' C A 56 46.771 0.989 44.930 1.00 0.00 C +ATOM 1221 N1 C A 56 45.511 0.337 45.334 1.00 0.00 N +ATOM 1222 C2 C A 56 44.635 0.000 44.316 1.00 0.00 C +ATOM 1223 O2 C A 56 44.905 0.247 43.153 1.00 0.00 O +ATOM 1224 N3 C A 56 43.466 -0.613 44.656 1.00 0.00 N +ATOM 1225 C4 C A 56 43.150 -0.894 45.949 1.00 0.00 C +ATOM 1226 N4 C A 56 41.981 -1.495 46.207 1.00 0.57 N +ATOM 1227 C5 C A 56 44.056 -0.545 46.983 1.00 0.00 C +ATOM 1228 C6 C A 56 45.202 0.056 46.644 1.00 0.00 C +ATOM 1229 P G A 57 46.184 5.847 45.755 1.00 0.00 P +ATOM 1230 OP1 G A 57 46.887 7.134 45.447 1.00 10.63 O +ATOM 1231 OP2 G A 57 45.994 5.689 47.225 1.00 0.00 O +ATOM 1232 O5' G A 57 44.909 5.661 44.817 1.00 0.00 O +ATOM 1233 C5' G A 57 45.092 5.898 43.395 1.00 0.00 C +ATOM 1234 C4' G A 57 43.859 5.346 42.716 1.00 3.53 C +ATOM 1235 O4' G A 57 43.733 3.964 42.975 1.00 5.63 O +ATOM 1236 C3' G A 57 42.530 5.931 43.185 1.00 3.57 C +ATOM 1237 O3' G A 57 42.250 7.202 42.571 1.00 3.71 O +ATOM 1238 C2' G A 57 41.538 4.846 42.781 1.00 0.64 C +ATOM 1239 O2' G A 57 41.261 4.970 41.391 1.00 2.20 O +ATOM 1240 C1' G A 57 42.344 3.592 43.039 1.00 0.00 C +ATOM 1241 N9 G A 57 41.997 2.974 44.332 1.00 0.00 N +ATOM 1242 C8 G A 57 42.697 2.935 45.496 1.00 0.00 C +ATOM 1243 N7 G A 57 42.087 2.277 46.466 1.00 0.00 N +ATOM 1244 C5 G A 57 40.898 1.844 45.868 1.00 0.00 C +ATOM 1245 C6 G A 57 39.812 1.085 46.369 1.00 0.00 C +ATOM 1246 O6 G A 57 39.679 0.618 47.516 1.00 0.00 O +ATOM 1247 N1 G A 57 38.806 0.866 45.480 1.00 0.00 N +ATOM 1248 C2 G A 57 38.853 1.332 44.203 1.00 0.00 C +ATOM 1249 N2 G A 57 37.797 1.040 43.411 1.00 0.00 N +ATOM 1250 N3 G A 57 39.845 2.046 43.670 1.00 0.00 N +ATOM 1251 C4 G A 57 40.835 2.266 44.559 1.00 0.00 C +HETATM 1252 P 1MA A 58 41.821 8.388 43.589 1.00 0.00 P +HETATM 1253 OP1 1MA A 58 42.247 9.692 42.991 1.00 0.42 O +HETATM 1254 OP2 1MA A 58 42.540 8.045 44.833 1.00 0.00 O +HETATM 1255 O5' 1MA A 58 40.255 8.203 43.734 1.00 0.00 O +HETATM 1256 C5' 1MA A 58 39.449 7.657 42.684 1.00 0.00 C +HETATM 1257 C4' 1MA A 58 38.010 8.045 42.991 1.00 0.08 C +HETATM 1258 O4' 1MA A 58 37.560 7.314 44.122 1.00 0.00 O +HETATM 1259 C3' 1MA A 58 37.804 9.529 43.314 1.00 0.00 C +HETATM 1260 O3' 1MA A 58 36.528 10.007 42.862 1.00 1.72 O +HETATM 1261 C2' 1MA A 58 37.793 9.529 44.849 1.00 0.88 C +HETATM 1262 O2' 1MA A 58 37.127 10.659 45.318 1.00 0.00 O +HETATM 1263 C1' 1MA A 58 37.101 8.225 45.140 1.00 0.00 C +HETATM 1264 N9 1MA A 58 37.404 7.792 46.514 1.00 0.00 N +HETATM 1265 C8 1MA A 58 38.500 8.039 47.274 1.00 0.00 C +HETATM 1266 N7 1MA A 58 38.440 7.505 48.470 1.00 0.31 N +HETATM 1267 C5 1MA A 58 37.207 6.859 48.486 1.00 0.00 C +HETATM 1268 C6 1MA A 58 36.558 6.100 49.488 1.00 0.00 C +HETATM 1269 N6 1MA A 58 37.064 5.852 50.684 1.00 0.00 N +HETATM 1270 N1 1MA A 58 35.332 5.616 49.132 1.00 0.00 N +HETATM 1271 CM1 1MA A 58 34.606 4.829 50.102 1.00 0.00 C +HETATM 1272 C2 1MA A 58 34.789 5.852 47.920 1.00 0.00 C +HETATM 1273 N3 1MA A 58 35.342 6.555 46.934 1.00 0.00 N +HETATM 1274 C4 1MA A 58 36.554 7.033 47.290 1.00 0.00 C +ATOM 1275 P U A 59 36.041 9.968 41.342 1.00 19.38 P +ATOM 1276 OP1 U A 59 36.288 8.652 40.680 1.00 21.40 O +ATOM 1277 OP2 U A 59 36.818 11.064 40.696 1.00 20.54 O +ATOM 1278 O5' U A 59 34.476 10.294 41.456 1.00 13.10 O +ATOM 1279 C5' U A 59 34.109 11.699 41.536 1.00 12.06 C +ATOM 1280 C4' U A 59 32.810 11.761 42.312 1.00 9.41 C +ATOM 1281 O4' U A 59 31.724 11.351 41.504 1.00 11.15 O +ATOM 1282 C3' U A 59 32.767 10.822 43.524 1.00 7.99 C +ATOM 1283 O3' U A 59 33.460 11.379 44.623 1.00 3.26 O +ATOM 1284 C2' U A 59 31.265 10.659 43.718 1.00 5.68 C +ATOM 1285 O2' U A 59 30.832 11.812 44.429 1.00 6.83 O +ATOM 1286 C1' U A 59 30.742 10.670 42.296 1.00 8.66 C +ATOM 1287 N1 U A 59 30.492 9.304 41.811 1.00 9.07 N +ATOM 1288 C2 U A 59 29.396 8.647 42.344 1.00 13.23 C +ATOM 1289 O2 U A 59 28.683 9.186 43.201 1.00 15.49 O +ATOM 1290 N3 U A 59 29.130 7.382 41.908 1.00 17.81 N +ATOM 1291 C4 U A 59 29.906 6.758 40.987 1.00 16.30 C +ATOM 1292 O4 U A 59 29.563 5.577 40.647 1.00 16.87 O +ATOM 1293 C5 U A 59 31.025 7.449 40.437 1.00 11.35 C +ATOM 1294 C6 U A 59 31.268 8.692 40.874 1.00 10.61 C +ATOM 1295 P C A 60 33.340 11.098 46.175 1.00 0.00 P +ATOM 1296 OP1 C A 60 33.413 12.396 46.934 1.00 3.78 O +ATOM 1297 OP2 C A 60 34.513 10.232 46.466 1.00 7.82 O +ATOM 1298 O5' C A 60 31.981 10.339 46.482 1.00 0.00 O +ATOM 1299 C5' C A 60 31.851 9.990 47.888 1.00 0.00 C +ATOM 1300 C4' C A 60 30.845 8.866 48.001 1.00 0.00 C +ATOM 1301 O4' C A 60 30.609 8.292 46.724 1.00 1.74 O +ATOM 1302 C3' C A 60 31.328 7.719 48.906 1.00 0.00 C +ATOM 1303 O3' C A 60 30.226 7.196 49.666 1.00 0.00 O +ATOM 1304 C2' C A 60 31.858 6.718 47.888 1.00 0.00 C +ATOM 1305 O2' C A 60 31.854 5.453 48.486 1.00 1.57 O +ATOM 1306 C1' C A 60 30.908 6.887 46.741 1.00 0.00 C +ATOM 1307 N1 C A 60 31.515 6.465 45.480 1.00 0.00 N +ATOM 1308 C2 C A 60 31.008 5.346 44.833 1.00 0.00 C +ATOM 1309 O2 C A 60 30.066 4.728 45.318 1.00 2.85 O +ATOM 1310 N3 C A 60 31.578 4.964 43.654 1.00 0.00 N +ATOM 1311 C4 C A 60 32.620 5.639 43.104 1.00 0.00 C +ATOM 1312 N4 C A 60 33.147 5.223 41.957 1.00 0.00 N +ATOM 1313 C5 C A 60 33.143 6.786 43.767 1.00 2.56 C +ATOM 1314 C6 C A 60 32.567 7.151 44.930 1.00 3.95 C +ATOM 1315 P C A 61 29.733 8.118 50.910 1.00 7.49 P +ATOM 1316 OP1 C A 61 28.360 7.730 51.347 1.00 10.82 O +ATOM 1317 OP2 C A 61 29.769 9.479 50.312 1.00 6.11 O +ATOM 1318 O5' C A 61 30.808 7.899 52.058 1.00 10.45 O +ATOM 1319 C5' C A 61 31.291 6.589 52.462 1.00 9.37 C +ATOM 1320 C4' C A 61 32.501 6.864 53.335 1.00 10.13 C +ATOM 1321 O4' C A 61 33.693 6.696 52.607 1.00 10.76 O +ATOM 1322 C3' C A 61 32.554 8.321 53.836 1.00 12.52 C +ATOM 1323 O3' C A 61 31.688 8.500 54.935 1.00 9.41 O +ATOM 1324 C2' C A 61 34.043 8.495 54.143 1.00 11.10 C +ATOM 1325 O2' C A 61 34.299 7.916 55.419 1.00 9.69 O +ATOM 1326 C1' C A 61 34.676 7.657 53.044 1.00 5.41 C +ATOM 1327 N1 C A 61 35.115 8.461 51.896 1.00 0.00 N +ATOM 1328 C2 C A 61 36.278 9.192 52.042 1.00 0.00 C +ATOM 1329 O2 C A 61 36.904 9.181 53.092 1.00 0.00 O +ATOM 1330 N3 C A 61 36.698 9.928 50.975 1.00 0.00 N +ATOM 1331 C4 C A 61 36.011 9.957 49.795 1.00 0.02 C +ATOM 1332 N4 C A 61 36.488 10.699 48.793 1.00 0.00 N +ATOM 1333 C5 C A 61 34.819 9.198 49.666 1.00 0.54 C +ATOM 1334 C6 C A 61 34.423 8.478 50.716 1.00 0.00 C +ATOM 1335 P A A 62 31.598 9.715 55.969 1.00 0.00 P +ATOM 1336 OP1 A A 62 30.669 9.321 57.084 1.00 13.83 O +ATOM 1337 OP2 A A 62 31.052 10.839 55.161 1.00 3.90 O +ATOM 1338 O5' A A 62 33.083 9.928 56.470 1.00 5.65 O +ATOM 1339 C5' A A 62 33.403 9.720 57.876 1.00 7.49 C +ATOM 1340 C4' A A 62 34.706 10.474 58.070 1.00 6.76 C +ATOM 1341 O4' A A 62 35.545 10.215 56.971 1.00 6.26 O +ATOM 1342 C3' A A 62 34.569 11.986 58.102 1.00 11.20 C +ATOM 1343 O3' A A 62 34.173 12.498 59.379 1.00 15.08 O +ATOM 1344 C2' A A 62 35.965 12.441 57.682 1.00 6.66 C +ATOM 1345 O2' A A 62 36.804 12.357 58.830 1.00 13.43 O +ATOM 1346 C1' A A 62 36.328 11.379 56.680 1.00 2.51 C +ATOM 1347 N9 A A 62 36.065 11.879 55.322 1.00 1.03 N +ATOM 1348 C8 A A 62 35.142 11.452 54.417 1.00 0.95 C +ATOM 1349 N7 A A 62 35.172 12.121 53.270 1.00 0.87 N +ATOM 1350 C5 A A 62 36.188 13.049 53.464 1.00 0.00 C +ATOM 1351 C6 A A 62 36.711 14.055 52.624 1.00 2.21 C +ATOM 1352 N6 A A 62 36.281 14.302 51.395 1.00 2.87 N +ATOM 1353 N1 A A 62 37.730 14.797 53.157 1.00 3.05 N +ATOM 1354 C2 A A 62 38.197 14.567 54.417 1.00 3.86 C +ATOM 1355 N3 A A 62 37.750 13.639 55.258 1.00 0.41 N +ATOM 1356 C4 A A 62 36.744 12.914 54.724 1.00 0.00 C +ATOM 1357 P C A 63 32.847 13.431 59.411 1.00 11.17 P +ATOM 1358 OP1 C A 63 32.074 13.060 60.640 1.00 24.93 O +ATOM 1359 OP2 C A 63 32.118 13.111 58.167 1.00 9.41 O +ATOM 1360 O5' C A 63 33.453 14.898 59.476 1.00 8.59 O +ATOM 1361 C5' C A 63 34.502 15.129 60.446 1.00 8.21 C +ATOM 1362 C4' C A 63 35.462 16.135 59.848 1.00 11.64 C +ATOM 1363 O4' C A 63 36.065 15.590 58.684 1.00 6.82 O +ATOM 1364 C3' C A 63 34.819 17.445 59.379 1.00 9.87 C +ATOM 1365 O3' C A 63 34.582 18.389 60.414 1.00 12.92 O +ATOM 1366 C2' C A 63 35.845 17.917 58.345 1.00 11.37 C +ATOM 1367 O2' C A 63 36.897 18.541 59.088 1.00 12.44 O +ATOM 1368 C1' C A 63 36.321 16.619 57.731 1.00 4.28 C +ATOM 1369 N1 C A 63 35.595 16.456 56.454 1.00 3.35 N +ATOM 1370 C2 C A 63 36.131 17.085 55.339 1.00 0.67 C +ATOM 1371 O2 C A 63 37.171 17.737 55.436 1.00 0.00 O +ATOM 1372 N3 C A 63 35.468 16.945 54.159 1.00 0.00 N +ATOM 1373 C4 C A 63 34.316 16.219 54.046 1.00 0.00 C +ATOM 1374 N4 C A 63 33.716 16.124 52.866 1.00 0.00 N +ATOM 1375 C5 C A 63 33.773 15.584 55.193 1.00 0.00 C +ATOM 1376 C6 C A 63 34.439 15.730 56.357 1.00 1.15 C +ATOM 1377 P A A 64 33.427 19.508 60.381 1.00 29.16 P +ATOM 1378 OP1 A A 64 33.257 20.166 61.723 1.00 22.93 O +ATOM 1379 OP2 A A 64 32.197 18.766 59.993 1.00 29.67 O +ATOM 1380 O5' A A 64 33.893 20.599 59.298 1.00 13.19 O +ATOM 1381 C5' A A 64 34.536 21.796 59.832 1.00 11.86 C +ATOM 1382 C4' A A 64 35.309 22.426 58.684 1.00 9.56 C +ATOM 1383 O4' A A 64 35.835 21.437 57.828 1.00 10.56 O +ATOM 1384 C3' A A 64 34.459 23.298 57.763 1.00 9.53 C +ATOM 1385 O3' A A 64 34.179 24.596 58.313 1.00 6.01 O +ATOM 1386 C2' A A 64 35.285 23.320 56.486 1.00 9.55 C +ATOM 1387 O2' A A 64 36.328 24.264 56.696 1.00 13.78 O +ATOM 1388 C1' A A 64 35.832 21.920 56.486 1.00 10.59 C +ATOM 1389 N9 A A 64 34.945 21.099 55.646 1.00 9.66 N +ATOM 1390 C8 A A 64 34.213 20.003 56.050 1.00 8.00 C +ATOM 1391 N7 A A 64 33.503 19.475 55.080 1.00 9.86 N +ATOM 1392 C5 A A 64 33.786 20.273 53.981 1.00 6.12 C +ATOM 1393 C6 A A 64 33.330 20.217 52.640 1.00 7.34 C +ATOM 1394 N6 A A 64 32.484 19.300 52.187 1.00 4.78 N +ATOM 1395 N1 A A 64 33.823 21.184 51.815 1.00 6.33 N +ATOM 1396 C2 A A 64 34.696 22.134 52.268 1.00 8.33 C +ATOM 1397 N3 A A 64 35.165 22.241 53.512 1.00 10.73 N +ATOM 1398 C4 A A 64 34.666 21.279 54.320 1.00 9.34 C +ATOM 1399 P G A 65 32.620 24.596 58.813 1.00 15.79 P +ATOM 1400 OP1 G A 65 32.494 25.395 60.074 1.00 16.85 O +ATOM 1401 OP2 G A 65 32.334 23.151 59.024 1.00 11.11 O +ATOM 1402 O5' G A 65 31.888 25.232 57.553 1.00 11.06 O +ATOM 1403 C5' G A 65 32.317 26.564 57.214 1.00 9.01 C +ATOM 1404 C4' G A 65 32.814 26.575 55.775 1.00 5.09 C +ATOM 1405 O4' G A 65 33.303 25.310 55.355 1.00 8.92 O +ATOM 1406 C3' G A 65 31.711 26.901 54.757 1.00 0.42 C +ATOM 1407 O3' G A 65 31.378 28.295 54.724 1.00 0.15 O +ATOM 1408 C2' G A 65 32.301 26.345 53.480 1.00 1.10 C +ATOM 1409 O2' G A 65 33.217 27.329 52.995 1.00 0.00 O +ATOM 1410 C1' G A 65 33.020 25.114 53.965 1.00 6.67 C +ATOM 1411 N9 G A 65 32.107 23.989 53.706 1.00 3.03 N +ATOM 1412 C8 G A 65 31.561 23.106 54.611 1.00 0.12 C +ATOM 1413 N7 G A 65 30.768 22.224 54.062 1.00 2.15 N +ATOM 1414 C5 G A 65 30.788 22.544 52.704 1.00 0.00 C +ATOM 1415 C6 G A 65 30.135 21.959 51.589 1.00 1.19 C +ATOM 1416 O6 G A 65 29.373 20.987 51.589 1.00 8.55 O +ATOM 1417 N1 G A 65 30.402 22.561 50.393 1.00 0.00 N +ATOM 1418 C2 G A 65 31.232 23.635 50.280 1.00 0.00 C +ATOM 1419 N2 G A 65 31.381 24.101 49.035 1.00 0.00 N +ATOM 1420 N3 G A 65 31.868 24.225 51.298 1.00 0.00 N +ATOM 1421 C4 G A 65 31.604 23.629 52.478 1.00 0.00 C +ATOM 1422 P A A 66 29.776 28.571 54.902 1.00 2.97 P +ATOM 1423 OP1 A A 66 29.596 29.858 55.646 1.00 8.00 O +ATOM 1424 OP2 A A 66 29.279 27.430 55.694 1.00 1.20 O +ATOM 1425 O5' A A 66 29.263 28.594 53.383 1.00 0.00 O +ATOM 1426 C5' A A 66 29.972 29.465 52.462 1.00 0.00 C +ATOM 1427 C4' A A 66 29.626 29.116 51.040 1.00 0.34 C +ATOM 1428 O4' A A 66 30.052 27.795 50.732 1.00 3.47 O +ATOM 1429 C3' A A 66 28.134 29.094 50.716 1.00 2.03 C +ATOM 1430 O3' A A 66 27.581 30.387 50.474 1.00 3.00 O +ATOM 1431 C2' A A 66 28.070 28.189 49.472 1.00 0.26 C +ATOM 1432 O2' A A 66 28.437 28.976 48.357 1.00 0.00 O +ATOM 1433 C1' A A 66 29.143 27.188 49.795 1.00 0.00 C +ATOM 1434 N9 A A 66 28.563 25.979 50.409 1.00 0.00 N +ATOM 1435 C8 A A 66 28.577 25.631 51.735 1.00 0.00 C +ATOM 1436 N7 A A 66 27.990 24.484 51.977 1.00 0.00 N +ATOM 1437 C5 A A 66 27.557 24.056 50.732 1.00 0.30 C +ATOM 1438 C6 A A 66 26.858 22.893 50.328 1.00 0.00 C +ATOM 1439 N6 A A 66 26.468 21.943 51.153 1.00 0.00 N +ATOM 1440 N1 A A 66 26.601 22.803 48.987 1.00 0.00 N +ATOM 1441 C2 A A 66 27.001 23.775 48.114 1.00 0.12 C +ATOM 1442 N3 A A 66 27.661 24.889 48.421 1.00 0.00 N +ATOM 1443 C4 A A 66 27.907 24.967 49.747 1.00 0.18 C +ATOM 1444 P A A 67 26.072 30.651 50.991 1.00 14.15 P +ATOM 1445 OP1 A A 67 25.872 32.135 51.153 1.00 15.31 O +ATOM 1446 OP2 A A 67 25.985 29.943 52.300 1.00 0.00 O +ATOM 1447 O5' A A 67 25.186 30.021 49.827 1.00 3.37 O +ATOM 1448 C5' A A 67 25.272 30.494 48.454 1.00 2.77 C +ATOM 1449 C4' A A 67 24.520 29.487 47.613 1.00 8.12 C +ATOM 1450 O4' A A 67 25.169 28.228 47.710 1.00 11.83 O +ATOM 1451 C3' A A 67 23.080 29.218 48.034 1.00 10.99 C +ATOM 1452 O3' A A 67 22.121 30.168 47.565 1.00 5.85 O +ATOM 1453 C2' A A 67 22.847 27.812 47.452 1.00 9.78 C +ATOM 1454 O2' A A 67 22.531 27.969 46.078 1.00 7.59 O +ATOM 1455 C1' A A 67 24.210 27.177 47.646 1.00 6.70 C +ATOM 1456 N9 A A 67 24.163 26.378 48.874 1.00 0.00 N +ATOM 1457 C8 A A 67 24.626 26.727 50.118 1.00 0.00 C +ATOM 1458 N7 A A 67 24.440 25.805 51.023 1.00 0.00 N +ATOM 1459 C5 A A 67 23.800 24.776 50.328 1.00 2.64 C +ATOM 1460 C6 A A 67 23.334 23.506 50.749 1.00 0.00 C +ATOM 1461 N6 A A 67 23.434 23.039 51.993 1.00 0.00 N +ATOM 1462 N1 A A 67 22.747 22.758 49.779 1.00 0.00 N +ATOM 1463 C2 A A 67 22.628 23.208 48.486 1.00 0.00 C +ATOM 1464 N3 A A 67 23.051 24.388 48.034 1.00 2.05 N +ATOM 1465 C4 A A 67 23.627 25.119 49.003 1.00 1.40 C +ATOM 1466 P U A 68 20.892 30.623 48.502 1.00 5.45 P +ATOM 1467 OP1 U A 68 20.409 31.983 48.098 1.00 15.56 O +ATOM 1468 OP2 U A 68 21.428 30.595 49.892 1.00 0.71 O +ATOM 1469 O5' U A 68 19.773 29.521 48.227 1.00 13.99 O +ATOM 1470 C5' U A 68 19.500 29.263 46.821 1.00 15.33 C +ATOM 1471 C4' U A 68 18.947 27.857 46.724 1.00 11.41 C +ATOM 1472 O4' U A 68 19.946 26.913 47.080 1.00 5.34 O +ATOM 1473 C3' U A 68 17.781 27.570 47.678 1.00 10.61 C +ATOM 1474 O3' U A 68 16.512 28.059 47.209 1.00 12.58 O +ATOM 1475 C2' U A 68 17.844 26.047 47.775 1.00 8.89 C +ATOM 1476 O2' U A 68 17.191 25.529 46.611 1.00 14.50 O +ATOM 1477 C1' U A 68 19.346 25.799 47.726 1.00 3.33 C +ATOM 1478 N1 U A 68 19.740 25.619 49.132 1.00 5.06 N +ATOM 1479 C2 U A 68 19.366 24.416 49.714 1.00 7.94 C +ATOM 1480 O2 U A 68 18.757 23.562 49.068 1.00 8.66 O +ATOM 1481 N3 U A 68 19.706 24.220 51.023 1.00 1.82 N +ATOM 1482 C4 U A 68 20.382 25.158 51.751 1.00 1.17 C +ATOM 1483 O4 U A 68 20.642 24.866 52.947 1.00 2.33 O +ATOM 1484 C5 U A 68 20.749 26.384 51.120 1.00 0.00 C +ATOM 1485 C6 U A 68 20.409 26.564 49.844 1.00 1.22 C +ATOM 1486 P U A 69 15.506 28.712 48.308 1.00 16.15 P +ATOM 1487 OP1 U A 69 14.440 29.493 47.613 1.00 4.70 O +ATOM 1488 OP2 U A 69 16.362 29.560 49.165 1.00 6.55 O +ATOM 1489 O5' U A 69 14.923 27.435 49.084 1.00 9.24 O +ATOM 1490 C5' U A 69 13.993 26.586 48.340 1.00 11.14 C +ATOM 1491 C4' U A 69 13.907 25.293 49.132 1.00 11.93 C +ATOM 1492 O4' U A 69 15.219 24.827 49.391 1.00 9.85 O +ATOM 1493 C3' U A 69 13.254 25.417 50.506 1.00 14.96 C +ATOM 1494 O3' U A 69 11.825 25.395 50.474 1.00 18.44 O +ATOM 1495 C2' U A 69 13.850 24.208 51.234 1.00 10.35 C +ATOM 1496 O2' U A 69 13.081 23.078 50.846 1.00 10.10 O +ATOM 1497 C1' U A 69 15.246 24.169 50.652 1.00 8.52 C +ATOM 1498 N1 U A 69 16.162 24.844 51.589 1.00 6.23 N +ATOM 1499 C2 U A 69 16.618 24.130 52.672 1.00 7.37 C +ATOM 1500 O2 U A 69 16.269 22.966 52.850 1.00 4.14 O +ATOM 1501 N3 U A 69 17.468 24.771 53.528 1.00 13.59 N +ATOM 1502 C4 U A 69 17.868 26.058 53.351 1.00 11.26 C +ATOM 1503 O4 U A 69 18.657 26.558 54.207 1.00 11.67 O +ATOM 1504 C5 U A 69 17.381 26.778 52.219 1.00 9.01 C +ATOM 1505 C6 U A 69 16.548 26.142 51.395 1.00 9.28 C +ATOM 1506 P C A 70 10.942 26.227 51.525 1.00 20.19 P +ATOM 1507 OP1 C A 70 9.633 26.615 50.910 1.00 23.18 O +ATOM 1508 OP2 C A 70 11.792 27.407 51.848 1.00 23.85 O +ATOM 1509 O5' C A 70 10.766 25.215 52.753 1.00 18.48 O +ATOM 1510 C5' C A 70 9.710 24.220 52.769 1.00 18.36 C +ATOM 1511 C4' C A 70 10.086 23.225 53.852 1.00 16.55 C +ATOM 1512 O4' C A 70 11.479 22.938 53.771 1.00 20.18 O +ATOM 1513 C3' C A 70 9.893 23.702 55.274 1.00 17.86 C +ATOM 1514 O3' C A 70 8.544 23.601 55.759 1.00 19.20 O +ATOM 1515 C2' C A 70 10.862 22.803 56.050 1.00 18.99 C +ATOM 1516 O2' C A 70 10.219 21.549 56.228 1.00 21.45 O +ATOM 1517 C1' C A 70 12.005 22.679 55.064 1.00 16.52 C +ATOM 1518 N1 C A 70 13.051 23.646 55.436 1.00 12.39 N +ATOM 1519 C2 C A 70 13.927 23.275 56.438 1.00 19.05 C +ATOM 1520 O2 C A 70 13.830 22.184 57.003 1.00 20.39 O +ATOM 1521 N3 C A 70 14.906 24.163 56.793 1.00 18.84 N +ATOM 1522 C4 C A 70 15.023 25.378 56.195 1.00 21.95 C +ATOM 1523 N4 C A 70 15.995 26.204 56.583 1.00 22.46 N +ATOM 1524 C5 C A 70 14.117 25.749 55.161 1.00 20.70 C +ATOM 1525 C6 C A 70 13.164 24.861 54.821 1.00 14.92 C +ATOM 1526 P G A 71 8.071 24.759 56.809 1.00 27.09 P +ATOM 1527 OP1 G A 71 6.575 24.838 56.809 1.00 29.37 O +ATOM 1528 OP2 G A 71 8.711 25.985 56.276 1.00 25.13 O +ATOM 1529 O5' G A 71 8.647 24.321 58.232 1.00 22.21 O +ATOM 1530 C5' G A 71 8.138 23.095 58.813 1.00 25.96 C +ATOM 1531 C4' G A 71 9.064 22.747 59.961 1.00 25.18 C +ATOM 1532 O4' G A 71 10.373 22.499 59.476 1.00 25.51 O +ATOM 1533 C3' G A 71 9.240 23.860 60.979 1.00 26.36 C +ATOM 1534 O3' G A 71 8.161 23.950 61.917 1.00 28.99 O +ATOM 1535 C2' G A 71 10.573 23.483 61.626 1.00 25.65 C +ATOM 1536 O2' G A 71 10.296 22.460 62.579 1.00 26.25 O +ATOM 1537 C1' G A 71 11.322 22.938 60.446 1.00 24.32 C +ATOM 1538 N9 G A 71 12.165 24.012 59.896 1.00 22.16 N +ATOM 1539 C8 G A 71 11.955 24.720 58.733 1.00 19.05 C +ATOM 1540 N7 G A 71 12.868 25.625 58.506 1.00 16.20 N +ATOM 1541 C5 G A 71 13.737 25.507 59.589 1.00 17.04 C +ATOM 1542 C6 G A 71 14.926 26.210 59.912 1.00 18.37 C +ATOM 1543 O6 G A 71 15.456 27.121 59.266 1.00 20.31 O +ATOM 1544 N1 G A 71 15.516 25.805 61.092 1.00 17.37 N +ATOM 1545 C2 G A 71 15.000 24.810 61.868 1.00 16.19 C +ATOM 1546 N2 G A 71 15.719 24.557 62.967 1.00 18.04 N +ATOM 1547 N3 G A 71 13.890 24.118 61.610 1.00 11.69 N +ATOM 1548 C4 G A 71 13.314 24.517 60.446 1.00 17.46 C +ATOM 1549 P C A 72 7.705 25.456 62.321 1.00 30.95 P +ATOM 1550 OP1 C A 72 6.309 25.406 62.854 1.00 27.88 O +ATOM 1551 OP2 C A 72 7.828 26.215 61.044 1.00 29.12 O +ATOM 1552 O5' C A 72 8.797 25.822 63.420 1.00 16.81 O +ATOM 1553 C5' C A 72 8.887 24.816 64.470 1.00 21.29 C +ATOM 1554 C4' C A 72 10.163 25.069 65.230 1.00 20.33 C +ATOM 1555 O4' C A 72 11.279 24.877 64.389 1.00 24.21 O +ATOM 1556 C3' C A 72 10.329 26.491 65.779 1.00 22.82 C +ATOM 1557 O3' C A 72 9.623 26.733 66.992 1.00 25.84 O +ATOM 1558 C2' C A 72 11.842 26.575 65.925 1.00 27.18 C +ATOM 1559 O2' C A 72 12.158 25.934 67.169 1.00 29.23 O +ATOM 1560 C1' C A 72 12.328 25.771 64.745 1.00 25.74 C +ATOM 1561 N1 C A 72 12.668 26.699 63.662 1.00 25.00 N +ATOM 1562 C2 C A 72 13.794 27.492 63.808 1.00 24.31 C +ATOM 1563 O2 C A 72 14.480 27.407 64.826 1.00 25.98 O +ATOM 1564 N3 C A 72 14.103 28.352 62.806 1.00 22.66 N +ATOM 1565 C4 C A 72 13.351 28.447 61.674 1.00 24.65 C +ATOM 1566 N4 C A 72 13.707 29.313 60.721 1.00 27.48 N +ATOM 1567 C5 C A 72 12.195 27.632 61.529 1.00 27.22 C +ATOM 1568 C6 C A 72 11.902 26.789 62.531 1.00 27.68 C +ATOM 1569 P A A 73 9.097 28.234 67.315 1.00 27.45 P +ATOM 1570 OP1 A A 73 8.168 28.222 68.478 1.00 24.27 O +ATOM 1571 OP2 A A 73 8.401 28.633 66.054 1.00 24.10 O +ATOM 1572 O5' A A 73 10.436 29.043 67.606 1.00 24.19 O +ATOM 1573 C5' A A 73 11.199 28.745 68.802 1.00 27.37 C +ATOM 1574 C4' A A 73 12.411 29.656 68.769 1.00 29.49 C +ATOM 1575 O4' A A 73 13.251 29.291 67.670 1.00 29.13 O +ATOM 1576 C3' A A 73 12.101 31.129 68.527 1.00 32.07 C +ATOM 1577 O3' A A 73 11.682 31.832 69.690 1.00 34.60 O +ATOM 1578 C2' A A 73 13.424 31.646 67.945 1.00 29.01 C +ATOM 1579 O2' A A 73 14.297 31.894 69.044 1.00 27.53 O +ATOM 1580 C1' A A 73 13.890 30.454 67.137 1.00 28.43 C +ATOM 1581 N9 A A 73 13.554 30.679 65.731 1.00 26.68 N +ATOM 1582 C8 A A 73 12.518 30.151 65.004 1.00 24.43 C +ATOM 1583 N7 A A 73 12.495 30.550 63.743 1.00 23.16 N +ATOM 1584 C5 A A 73 13.590 31.404 63.646 1.00 19.93 C +ATOM 1585 C6 A A 73 14.103 32.152 62.563 1.00 20.89 C +ATOM 1586 N6 A A 73 13.574 32.163 61.351 1.00 24.41 N +ATOM 1587 N1 A A 73 15.219 32.889 62.838 1.00 21.91 N +ATOM 1588 C2 A A 73 15.779 32.894 64.082 1.00 24.59 C +ATOM 1589 N3 A A 73 15.349 32.214 65.149 1.00 24.25 N +ATOM 1590 C4 A A 73 14.250 31.489 64.858 1.00 22.52 C +ATOM 1591 P C A 74 10.693 33.102 69.480 1.00 39.69 P +ATOM 1592 OP1 C A 74 10.103 33.513 70.790 1.00 29.46 O +ATOM 1593 OP2 C A 74 9.677 32.602 68.511 1.00 29.74 O +ATOM 1594 O5' C A 74 11.682 34.204 68.866 1.00 37.49 O +ATOM 1595 C5' C A 74 13.078 34.165 69.270 1.00 34.40 C +ATOM 1596 C4' C A 74 13.850 35.064 68.333 1.00 33.09 C +ATOM 1597 O4' C A 74 14.040 34.435 67.072 1.00 34.22 O +ATOM 1598 C3' C A 74 13.144 36.386 68.010 1.00 31.87 C +ATOM 1599 O3' C A 74 13.317 37.330 69.044 1.00 30.07 O +ATOM 1600 C2' C A 74 13.804 36.779 66.684 1.00 33.18 C +ATOM 1601 O2' C A 74 15.036 37.409 67.008 1.00 34.33 O +ATOM 1602 C1' C A 74 14.027 35.419 66.022 1.00 31.93 C +ATOM 1603 N1 C A 74 12.991 35.154 65.004 1.00 28.12 N +ATOM 1604 C2 C A 74 13.161 35.795 63.775 1.00 28.57 C +ATOM 1605 O2 C A 74 14.120 36.537 63.581 1.00 30.04 O +ATOM 1606 N3 C A 74 12.228 35.570 62.806 1.00 27.74 N +ATOM 1607 C4 C A 74 11.156 34.755 63.000 1.00 25.44 C +ATOM 1608 N4 C A 74 10.289 34.592 61.997 1.00 23.80 N +ATOM 1609 C5 C A 74 10.992 34.103 64.260 1.00 25.11 C +ATOM 1610 C6 C A 74 11.922 34.333 65.198 1.00 27.20 C +ATOM 1611 P C A 75 12.571 38.702 69.319 1.00 40.57 P +ATOM 1612 OP1 C A 75 13.464 39.624 70.127 1.00 39.27 O +ATOM 1613 OP2 C A 75 11.342 38.325 70.078 1.00 36.25 O +ATOM 1614 O5' C A 75 12.255 39.365 67.897 1.00 31.83 O +ATOM 1615 C5' C A 75 11.335 40.495 67.913 1.00 27.55 C +ATOM 1616 C4' C A 75 11.006 40.805 66.458 1.00 26.80 C +ATOM 1617 O4' C A 75 11.309 39.674 65.666 1.00 24.35 O +ATOM 1618 C3' C A 75 9.537 41.102 66.183 1.00 28.28 C +ATOM 1619 O3' C A 75 9.134 42.440 66.458 0.70 29.36 O +ATOM 1620 C2' C A 75 9.437 40.771 64.697 1.00 26.31 C +ATOM 1621 O2' C A 75 9.906 41.912 64.002 1.00 28.50 O +ATOM 1622 C1' C A 75 10.393 39.590 64.567 1.00 24.36 C +ATOM 1623 N1 C A 75 9.597 38.353 64.567 1.00 21.37 N +ATOM 1624 C2 C A 75 8.867 38.095 63.403 1.00 25.62 C +ATOM 1625 O2 C A 75 8.900 38.870 62.450 1.00 27.04 O +ATOM 1626 N3 C A 75 8.114 36.953 63.371 1.00 25.19 N +ATOM 1627 C4 C A 75 8.071 36.088 64.422 1.00 24.44 C +ATOM 1628 N4 C A 75 7.315 34.991 64.309 1.00 24.63 N +ATOM 1629 C5 C A 75 8.821 36.357 65.585 1.00 24.16 C +ATOM 1630 C6 C A 75 9.553 37.487 65.618 1.00 23.86 C +ATOM 1631 P A A 76 9.487 43.244 67.800 0.70 29.71 P +ATOM 1632 OP1 A A 76 9.333 42.356 68.996 0.70 33.40 O +ATOM 1633 OP2 A A 76 8.507 44.369 67.816 0.70 33.05 O +ATOM 1634 O5' A A 76 10.989 43.722 67.606 0.70 35.56 O +ATOM 1635 C5' A A 76 12.058 43.211 68.430 0.62 37.62 C +ATOM 1636 C4' A A 76 13.161 44.251 68.414 0.62 39.32 C +ATOM 1637 O4' A A 76 12.615 45.527 68.705 0.62 39.53 O +ATOM 1638 C3' A A 76 14.267 44.048 69.448 0.62 40.32 C +ATOM 1639 O3' A A 76 15.253 43.098 69.044 0.62 41.15 O +ATOM 1640 C2' A A 76 14.826 45.465 69.577 0.62 40.57 C +ATOM 1641 O2' A A 76 15.709 45.667 68.478 0.62 39.29 O +ATOM 1642 C1' A A 76 13.580 46.308 69.416 0.62 40.91 C +ATOM 1643 N9 A A 76 13.071 46.708 70.741 0.35 42.10 N +ATOM 1644 C8 A A 76 13.727 47.365 71.759 0.35 42.72 C +ATOM 1645 N7 A A 76 12.991 47.579 72.810 0.35 43.75 N +ATOM 1646 C5 A A 76 11.762 47.022 72.487 0.35 43.24 C +ATOM 1647 C6 A A 76 10.549 46.927 73.214 0.35 44.11 C +ATOM 1648 N6 A A 76 10.379 47.399 74.442 0.35 44.46 N +ATOM 1649 N1 A A 76 9.527 46.297 72.551 0.35 44.29 N +ATOM 1650 C2 A A 76 9.683 45.802 71.291 0.35 43.56 C +ATOM 1651 N3 A A 76 10.792 45.859 70.563 0.35 43.16 N +ATOM 1652 C4 A A 76 11.795 46.483 71.226 0.35 42.87 C +TER 1653 A A 76 +HETATM 1654 MG MG 77 30.812 1.023 40.712 1.00 38.62 MG +HETATM 1655 MG MG 78 37.977 6.038 34.231 0.93 39.86 MG +HETATM 1656 MG MG 79 20.562 20.228 37.803 0.86 38.69 MG +HETATM 1657 MG MG 80 8.697 12.031 10.570 0.86 39.21 MG +HETATM 1658 O HOH 81 45.795 2.817 40.922 1.00 34.31 O +HETATM 1659 O HOH 82 27.334 7.219 35.573 1.00 34.77 O +HETATM 1660 O HOH 83 39.865 20.622 41.342 1.00 35.14 O +HETATM 1661 O HOH 84 35.242 5.498 44.122 1.00 36.97 O +HETATM 1662 O HOH 85 19.117 14.027 42.942 1.00 37.97 O +HETATM 1663 O HOH 86 9.196 22.970 55.611 1.00 37.30 O +HETATM 1664 O HOH 87 40.152 2.794 39.888 1.00 37.06 O +HETATM 1665 O HOH 88 12.435 4.677 11.556 1.00 39.01 O +HETATM 1666 O HOH 89 14.180 17.664 56.906 1.00 38.31 O +HETATM 1667 O HOH 90 22.641 25.389 45.512 1.00 35.73 O +HETATM 1668 O HOH 91 13.337 3.514 9.406 1.00 37.94 O +HETATM 1669 O HOH 92 40.039 2.204 51.282 1.00 35.88 O +HETATM 1670 O HOH 93 31.122 7.005 21.835 0.87 39.25 O +HETATM 1671 O HOH 94 16.255 5.296 44.462 1.00 35.18 O +HETATM 1672 O HOH 95 16.448 14.145 7.628 1.00 36.51 O +HETATM 1673 O HOH 96 36.231 -1.636 35.249 1.00 35.78 O +HETATM 1674 O HOH 97 27.614 21.628 27.653 1.00 37.29 O +HETATM 1675 O HOH 98 30.219 18.255 51.961 1.00 37.19 O +HETATM 1676 O HOH 99 24.696 17.574 28.542 1.00 37.65 O +HETATM 1677 O HOH 100 25.159 15.039 42.296 1.00 35.67 O +HETATM 1678 O HOH 101 46.098 9.389 51.104 1.00 36.27 O +HETATM 1679 O HOH 102 26.595 12.891 42.732 1.00 35.97 O +HETATM 1680 O HOH 103 40.205 11.857 39.177 0.98 37.19 O +HETATM 1681 O HOH 104 16.951 17.209 38.175 1.00 36.03 O +HETATM 1682 O HOH 105 48.150 13.262 49.908 1.00 35.98 O +HETATM 1683 O HOH 106 22.005 11.019 43.557 1.00 37.30 O +HETATM 1684 O HOH 107 44.589 13.487 47.937 1.00 36.38 O +HETATM 1685 O HOH 108 17.827 3.621 7.224 1.00 36.82 O +HETATM 1686 O HOH 109 19.736 11.109 26.700 1.00 35.63 O +HETATM 1687 O HOH 110 39.829 13.144 46.029 1.00 37.29 O +HETATM 1688 O HOH 111 11.492 29.111 53.900 1.00 36.81 O +HETATM 1689 O HOH 112 27.301 12.172 45.318 1.00 36.22 O +HETATM 1690 O HOH 113 27.737 4.166 39.371 1.00 37.56 O +HETATM 1691 O HOH 114 30.618 3.143 49.343 1.00 35.85 O +HETATM 1692 O HOH 115 43.683 15.888 48.147 1.00 36.30 O +HETATM 1693 O HOH 116 20.466 22.167 42.199 1.00 36.37 O +HETATM 1694 O HOH 117 22.338 28.706 60.414 1.00 37.00 O +HETATM 1695 O HOH 118 27.817 25.001 56.179 1.00 36.94 O +HETATM 1696 O HOH 119 13.291 10.895 43.880 1.00 36.42 O +HETATM 1697 O HOH 120 32.854 22.527 40.243 1.00 36.89 O +HETATM 1698 O HOH 121 34.925 -2.833 44.510 1.00 36.61 O +HETATM 1699 O HOH 122 35.922 18.052 42.991 0.99 37.61 O +HETATM 1700 O HOH 123 33.357 -0.062 31.370 0.95 38.00 O +HETATM 1701 O HOH 124 14.020 43.509 71.969 1.00 37.38 O +HETATM 1702 O HOH 125 24.233 11.278 45.755 1.00 36.38 O +HETATM 1703 O HOH 126 43.526 8.011 39.839 1.00 37.62 O +HETATM 1704 O HOH 127 18.177 22.943 66.216 1.00 38.01 O +HETATM 1705 O HOH 128 46.281 18.744 45.803 0.95 37.48 O +HETATM 1706 O HOH 129 29.363 15.753 49.052 1.00 36.68 O +HETATM 1707 O HOH 130 32.204 5.110 36.736 0.96 38.10 O +HETATM 1708 O HOH 131 28.207 22.420 54.126 1.00 36.72 O +HETATM 1709 O HOH 132 34.712 16.512 47.516 1.00 37.76 O +HETATM 1710 O HOH 133 11.239 6.640 24.534 0.91 37.05 O +HETATM 1711 O HOH 134 17.814 24.135 23.774 0.99 36.86 O +HETATM 1712 O HOH 135 35.785 25.007 35.734 1.00 36.39 O +HETATM 1713 O HOH 136 18.171 16.787 7.241 0.89 37.56 O +HETATM 1714 O HOH 137 32.131 16.590 47.096 1.00 36.66 O +HETATM 1715 O HOH 138 20.519 5.757 33.665 0.93 36.92 O +HETATM 1716 O HOH 139 12.218 20.442 52.866 0.86 37.57 O +HETATM 1717 O HOH 140 16.338 22.286 47.872 0.98 37.43 O +HETATM 1718 O HOH 141 38.110 10.491 35.653 1.00 36.95 O +HETATM 1719 O HOH 142 21.968 17.900 55.436 1.00 36.61 O +HETATM 1720 O HOH 143 30.209 23.298 56.858 1.00 36.83 O +HETATM 1721 O HOH 144 6.579 19.666 4.655 0.93 37.11 O +HETATM 1722 O HOH 145 33.896 -1.619 39.629 1.00 37.20 O +HETATM 1723 O HOH 146 23.247 14.555 60.737 1.00 36.59 O +HETATM 1724 O HOH 147 18.637 10.890 2.214 1.00 35.96 O +HETATM 1725 O HOH 148 15.343 0.663 4.574 1.00 37.35 O +HETATM 1726 O HOH 149 27.341 2.536 42.652 1.00 37.07 O +HETATM 1727 O HOH 150 9.190 31.905 65.278 0.97 37.34 O +HETATM 1728 O HOH 151 36.418 5.762 41.068 0.98 36.86 O +HETATM 1729 O HOH 152 30.319 31.494 47.193 0.82 37.33 O +HETATM 1730 O HOH 153 -0.667 9.540 7.681 0.94 37.13 O +HETATM 1731 O HOH 154 12.298 12.717 18.409 0.67 37.76 O +HETATM 1732 O HOH 155 15.712 17.872 17.617 1.00 36.71 O +HETATM 1733 O HOH 156 16.222 17.996 45.949 1.00 36.80 O +HETATM 1734 O HOH 157 22.431 25.979 33.665 0.94 36.71 O +HETATM 1735 O HOH 158 12.834 8.242 1.568 1.00 36.80 O +HETATM 1736 O HOH 159 11.186 8.967 20.316 1.00 37.01 O +HETATM 1737 O HOH 160 10.369 13.959 19.718 0.97 37.20 O +HETATM 1738 O HOH 161 4.990 28.987 68.527 1.00 36.68 O +HETATM 1739 O HOH 162 32.594 5.285 39.468 0.69 37.09 O +HETATM 1740 O HOH 163 27.341 6.758 21.835 1.00 37.01 O +HETATM 1741 O HOH 164 31.545 13.302 52.704 1.00 36.93 O +HETATM 1742 O HOH 165 19.127 17.007 40.954 1.00 36.73 O +HETATM 1743 O HOH 166 26.831 0.984 51.137 1.00 36.11 O +HETATM 1744 O HOH 167 31.201 -8.118 47.904 0.94 36.16 O +HETATM 1745 O HOH 168 15.552 11.244 23.176 1.00 36.17 O +HETATM 1746 O HOH 169 24.983 15.264 12.833 1.00 35.75 O +HETATM 1747 O HOH 170 33.347 15.404 27.896 0.95 36.33 O +HETATM 1748 O HOH 171 32.001 22.494 29.867 1.00 35.91 O +HETATM 1749 O HOH 172 5.416 29.071 65.181 0.93 36.15 O +HETATM 1750 O HOH 173 13.224 12.526 31.645 0.93 35.97 O +HETATM 1751 O HOH 174 15.349 17.625 12.655 1.00 35.97 O +HETATM 1752 O HOH 175 37.947 14.943 46.821 1.00 35.99 O +HETATM 1753 O HOH 176 24.426 16.231 64.923 0.98 36.15 O +HETATM 1754 O HOH 177 31.713 6.592 33.779 0.72 36.30 O +HETATM 1755 O HOH 178 15.343 20.925 17.067 0.91 36.06 O +HETATM 1756 O HOH 179 39.649 22.505 52.219 1.00 35.97 O +HETATM 1757 O HOH 180 38.340 18.513 46.902 1.00 36.03 O +HETATM 1758 O HOH 181 42.444 -5.195 44.785 0.94 36.25 O +HETATM 1759 O HOH 182 -1.272 9.990 4.299 0.98 36.35 O +HETATM 1760 O HOH 183 29.090 14.471 15.677 0.87 35.88 O +HETATM 1761 O HOH 184 40.072 21.881 39.160 0.85 36.06 O +HETATM 1762 O HOH 185 29.826 1.850 42.215 1.00 36.14 O +HETATM 1763 O HOH 186 31.734 0.174 39.144 1.00 39.72 O +HETATM 1764 O HOH 187 31.401 2.817 40.082 1.00 37.18 O +HETATM 1765 O HOH 188 30.166 -0.781 41.278 0.90 38.94 O +HETATM 1766 O HOH 189 29.173 1.220 39.581 0.67 38.62 O +HETATM 1767 O HOH 190 38.443 6.600 32.373 1.00 37.39 O +HETATM 1768 O HOH 191 36.881 7.719 34.393 1.00 39.12 O +HETATM 1769 O HOH 192 39.566 7.061 34.894 0.93 39.00 O +HETATM 1770 O HOH 193 19.370 19.441 36.429 0.93 37.87 O +HETATM 1771 O HOH 194 21.738 21.032 39.209 0.73 40.06 O +HETATM 1772 O HOH 195 21.595 18.536 37.900 0.81 39.85 O +HETATM 1773 O HOH 196 19.526 21.931 37.738 0.57 39.67 O +HETATM 1774 O HOH 197 19.346 19.565 39.241 0.89 39.65 O +HETATM 1775 O HOH 198 21.778 20.914 36.397 1.00 37.28 O +HETATM 1776 O HOH 199 7.628 11.115 9.164 0.92 39.38 O +HETATM 1777 O HOH 200 9.800 12.874 12.008 0.57 39.51 O +HETATM 1778 O HOH 201 10.290 12.031 9.358 0.90 39.05 O +HETATM 1779 O HOH 202 7.132 12.031 11.782 0.93 39.65 O +HETATM 1780 O HOH 203 9.233 10.238 11.281 1.00 38.49 O +CONECT 181 195 +CONECT 195 181 196 197 198 +CONECT 196 195 +CONECT 197 195 +CONECT 198 195 199 +CONECT 199 198 200 +CONECT 200 199 201 202 +CONECT 201 200 206 +CONECT 202 200 203 204 +CONECT 203 202 219 +CONECT 204 202 205 206 +CONECT 205 204 +CONECT 206 201 204 207 +CONECT 207 206 208 218 +CONECT 208 207 209 +CONECT 209 208 210 +CONECT 210 209 211 218 +CONECT 211 210 212 213 +CONECT 212 211 +CONECT 213 211 214 +CONECT 214 213 215 217 +CONECT 215 214 216 +CONECT 216 215 +CONECT 217 214 218 +CONECT 218 207 210 217 +CONECT 219 203 +CONECT 309 324 +CONECT 324 309 325 326 327 +CONECT 325 324 +CONECT 326 324 +CONECT 327 324 328 +CONECT 328 327 329 +CONECT 329 328 330 331 +CONECT 330 329 333 +CONECT 331 329 332 334 +CONECT 332 331 344 +CONECT 333 330 334 336 +CONECT 334 331 333 335 +CONECT 335 334 +CONECT 336 333 337 343 +CONECT 337 336 338 339 +CONECT 338 337 +CONECT 339 337 340 +CONECT 340 339 341 342 +CONECT 341 340 +CONECT 342 340 343 +CONECT 343 336 342 +CONECT 344 332 345 346 347 +CONECT 345 344 +CONECT 346 344 +CONECT 347 344 348 +CONECT 348 347 349 +CONECT 349 348 350 351 +CONECT 350 349 353 +CONECT 351 349 352 354 +CONECT 352 351 364 +CONECT 353 350 354 356 +CONECT 354 351 353 355 +CONECT 355 354 +CONECT 356 353 357 363 +CONECT 357 356 358 359 +CONECT 358 357 +CONECT 359 357 360 +CONECT 360 359 361 362 +CONECT 361 360 +CONECT 362 360 363 +CONECT 363 356 362 +CONECT 364 352 +CONECT 531 543 +CONECT 543 531 544 545 546 +CONECT 544 543 +CONECT 545 543 +CONECT 546 543 547 +CONECT 547 546 548 +CONECT 548 547 549 550 +CONECT 549 548 554 +CONECT 550 548 551 552 +CONECT 551 550 568 +CONECT 552 550 553 554 +CONECT 553 552 +CONECT 554 549 552 555 +CONECT 555 554 556 565 +CONECT 556 555 557 +CONECT 557 556 558 +CONECT 558 557 559 565 +CONECT 559 558 560 561 +CONECT 560 559 +CONECT 561 559 562 +CONECT 562 561 563 564 +CONECT 563 562 566 567 +CONECT 564 562 565 +CONECT 565 555 558 564 +CONECT 566 563 +CONECT 567 563 +CONECT 568 551 +CONECT 661 693 +CONECT 675 676 680 683 +CONECT 676 675 677 681 +CONECT 677 676 678 +CONECT 678 677 679 682 +CONECT 679 678 680 +CONECT 680 675 679 +CONECT 681 676 +CONECT 682 678 +CONECT 683 675 684 689 +CONECT 684 683 685 687 +CONECT 685 684 686 +CONECT 686 685 +CONECT 687 684 688 690 +CONECT 688 687 689 691 +CONECT 689 683 688 +CONECT 690 687 696 +CONECT 691 688 692 +CONECT 692 691 693 +CONECT 693 661 692 694 695 +CONECT 694 693 +CONECT 695 693 +CONECT 696 690 +CONECT 704 716 +CONECT 716 704 717 718 719 +CONECT 717 716 +CONECT 718 716 +CONECT 719 716 720 +CONECT 720 719 721 +CONECT 721 720 722 723 +CONECT 722 721 728 +CONECT 723 721 724 725 +CONECT 724 723 740 +CONECT 725 723 726 728 +CONECT 726 725 727 +CONECT 727 726 +CONECT 728 722 725 729 +CONECT 729 728 730 739 +CONECT 730 729 731 +CONECT 731 730 732 +CONECT 732 731 733 739 +CONECT 733 732 734 735 +CONECT 734 733 +CONECT 735 733 736 +CONECT 736 735 737 738 +CONECT 737 736 +CONECT 738 736 739 +CONECT 739 729 732 738 +CONECT 740 724 +CONECT 770 820 +CONECT 784 786 791 798 +CONECT 785 786 797 +CONECT 786 784 785 787 +CONECT 787 786 788 789 +CONECT 788 787 +CONECT 789 787 790 795 +CONECT 790 789 791 793 +CONECT 791 784 790 792 +CONECT 792 791 +CONECT 793 790 794 +CONECT 794 793 795 +CONECT 795 789 794 811 +CONECT 796 797 +CONECT 797 785 796 798 +CONECT 798 784 797 799 +CONECT 799 798 800 +CONECT 800 799 801 +CONECT 801 800 802 806 +CONECT 802 801 803 804 +CONECT 803 802 +CONECT 804 802 805 +CONECT 805 804 +CONECT 806 801 807 +CONECT 807 806 808 809 +CONECT 808 807 +CONECT 809 807 810 +CONECT 810 809 +CONECT 811 795 812 817 +CONECT 812 811 813 814 +CONECT 813 812 +CONECT 814 812 815 816 +CONECT 815 814 823 +CONECT 816 814 817 818 +CONECT 817 811 816 +CONECT 818 816 819 +CONECT 819 818 820 +CONECT 820 770 819 821 822 +CONECT 821 820 +CONECT 822 820 +CONECT 823 815 +CONECT 831 862 +CONECT 845 846 850 +CONECT 846 845 847 851 +CONECT 847 846 848 +CONECT 848 847 849 852 +CONECT 849 848 850 853 +CONECT 850 845 849 +CONECT 851 846 +CONECT 852 848 +CONECT 853 849 854 859 +CONECT 854 853 855 856 +CONECT 855 854 +CONECT 856 854 857 858 +CONECT 857 856 859 860 +CONECT 858 856 865 +CONECT 859 853 857 +CONECT 860 857 861 +CONECT 861 860 862 +CONECT 862 831 861 863 864 +CONECT 863 862 +CONECT 864 862 +CONECT 865 858 866 867 868 +CONECT 866 865 +CONECT 867 865 +CONECT 868 865 869 +CONECT 869 868 870 +CONECT 870 869 871 872 +CONECT 871 870 876 +CONECT 872 870 873 874 +CONECT 873 872 886 +CONECT 874 872 875 876 +CONECT 875 874 +CONECT 876 871 874 877 +CONECT 877 876 878 884 +CONECT 878 877 879 880 +CONECT 879 878 +CONECT 880 878 881 +CONECT 881 880 882 883 +CONECT 882 881 +CONECT 883 881 884 885 +CONECT 884 877 883 +CONECT 885 883 +CONECT 886 873 +CONECT 982 997 +CONECT 997 982 998 999 1000 +CONECT 998 997 +CONECT 999 997 +CONECT 1000 997 1001 +CONECT 1001 1000 1002 +CONECT 1002 1001 1003 1004 +CONECT 1003 1002 1008 +CONECT 1004 1002 1005 1006 +CONECT 1005 1004 1021 +CONECT 1006 1004 1007 1008 +CONECT 1007 1006 +CONECT 1008 1003 1006 1009 +CONECT 1009 1008 1010 1019 +CONECT 1010 1009 1011 +CONECT 1011 1010 1012 1020 +CONECT 1012 1011 1013 1019 +CONECT 1013 1012 1014 1015 +CONECT 1014 1013 +CONECT 1015 1013 1016 +CONECT 1016 1015 1017 1018 +CONECT 1017 1016 +CONECT 1018 1016 1019 +CONECT 1019 1009 1012 1018 +CONECT 1020 1011 +CONECT 1021 1005 +CONECT 1049 1061 +CONECT 1061 1049 1062 1063 1064 +CONECT 1062 1061 +CONECT 1063 1061 +CONECT 1064 1061 1065 +CONECT 1065 1064 1066 +CONECT 1066 1065 1067 1068 +CONECT 1067 1066 1072 +CONECT 1068 1066 1069 1070 +CONECT 1069 1068 1082 +CONECT 1070 1068 1071 1072 +CONECT 1071 1070 +CONECT 1072 1067 1070 1073 +CONECT 1073 1072 1074 1080 +CONECT 1074 1073 1075 1076 +CONECT 1075 1074 +CONECT 1076 1074 1077 +CONECT 1077 1076 1078 1079 +CONECT 1078 1077 +CONECT 1079 1077 1080 1081 +CONECT 1080 1073 1079 +CONECT 1081 1079 +CONECT 1082 1069 +CONECT 1153 1186 +CONECT 1168 1169 1174 1177 +CONECT 1169 1168 1170 1175 +CONECT 1170 1169 1171 +CONECT 1171 1170 1172 1176 +CONECT 1172 1171 1173 1174 +CONECT 1173 1172 +CONECT 1174 1168 1172 +CONECT 1175 1169 +CONECT 1176 1171 +CONECT 1177 1168 1178 1183 +CONECT 1178 1177 1179 1180 +CONECT 1179 1178 +CONECT 1180 1178 1181 1182 +CONECT 1181 1180 1183 1184 +CONECT 1182 1180 1206 +CONECT 1183 1177 1181 +CONECT 1184 1181 1185 +CONECT 1185 1184 1186 +CONECT 1186 1153 1185 1187 1188 +CONECT 1187 1186 +CONECT 1188 1186 +CONECT 1189 1190 1194 +CONECT 1190 1189 1191 1195 +CONECT 1191 1190 1192 +CONECT 1192 1191 1193 1196 +CONECT 1193 1192 1194 1197 +CONECT 1194 1189 1193 +CONECT 1195 1190 +CONECT 1196 1192 +CONECT 1197 1193 1198 1203 +CONECT 1198 1197 1199 1200 +CONECT 1199 1198 +CONECT 1200 1198 1201 1202 +CONECT 1201 1200 1203 1204 +CONECT 1202 1200 1209 +CONECT 1203 1197 1201 +CONECT 1204 1201 1205 +CONECT 1205 1204 1206 +CONECT 1206 1182 1205 1207 1208 +CONECT 1207 1206 +CONECT 1208 1206 +CONECT 1209 1202 +CONECT 1237 1252 +CONECT 1252 1237 1253 1254 1255 +CONECT 1253 1252 +CONECT 1254 1252 +CONECT 1255 1252 1256 +CONECT 1256 1255 1257 +CONECT 1257 1256 1258 1259 +CONECT 1258 1257 1263 +CONECT 1259 1257 1260 1261 +CONECT 1260 1259 1275 +CONECT 1261 1259 1262 1263 +CONECT 1262 1261 +CONECT 1263 1258 1261 1264 +CONECT 1264 1263 1265 1274 +CONECT 1265 1264 1266 +CONECT 1266 1265 1267 +CONECT 1267 1266 1268 1274 +CONECT 1268 1267 1269 1270 +CONECT 1269 1268 +CONECT 1270 1268 1271 1272 +CONECT 1271 1270 +CONECT 1272 1270 1273 +CONECT 1273 1272 1274 +CONECT 1274 1264 1267 1273 +CONECT 1275 1260 +CONECT 1654 1762 1763 1764 1765 +CONECT 1654 1766 +CONECT 1655 1767 1768 1769 +CONECT 1656 1770 1771 1772 1773 +CONECT 1656 1774 1775 +CONECT 1657 1776 1777 1778 1779 +CONECT 1657 1780 +CONECT 1762 1654 +CONECT 1763 1654 +CONECT 1764 1654 +CONECT 1765 1654 +CONECT 1766 1654 +CONECT 1767 1655 +CONECT 1768 1655 +CONECT 1769 1655 +CONECT 1770 1656 +CONECT 1771 1656 +CONECT 1772 1656 +CONECT 1773 1656 +CONECT 1774 1656 +CONECT 1775 1656 +CONECT 1776 1657 +CONECT 1777 1657 +CONECT 1778 1657 +CONECT 1779 1657 +CONECT 1780 1657 +MASTER 274 0 18 0 0 0 0 6 1779 1 371 6 +END diff --git a/Default-Landscape.png b/Default-Landscape.png deleted file mode 100644 index dcc8122c73ed91ba700d4f24a94077db03c294a3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13010 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 JTygmGe*h1}BD(+p diff --git a/Default-Landscape@2x.png b/Default-Landscape@2x.png deleted file mode 100644 index a5ca2364de14498483531536269a6f524c687b3a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 33335 zcmeIbcRZK<`#yfzl35h83MG4{Y*}fDGBZm?Bzw;g5{W_?5!svUy%lAXy^_7Mv%crm zo%{WM|Ni>@|M7b0?j^il*L6P6=XoCIaU4(gb43L?A_8gx1VM=8uSwrQ5PW!w^Bo@- z{^Foz&qWZdfHJxGb91ear^I7(UJ_Hh$Wpnh zS$u{%qXAd`b;8V5>f*28SwDWZeL{V=_fkQpnM%wCra9pjmPalg-7ANy)P!{#xeFMd zQoDdeqguqoTX`zI;a}PUF~LDG zZ^MLD&mL=)ys##bl<2HbqQ!~H+3-xRr&jlD^{^mb|J;GKt!hdV7SsEykJUt7=~fqDVEDyq7eC*9<9cQlFw|AddY3YgH{3aOZQfsR%U+vJRdY(8q}?0-`GX!o@rVv zU&qB*@pFsnygS88d4m>npaYl0uXhp7A8WQh3!a+0yTma3yvSthS&CwX@z*!!i_EMX zzlo*#M46Td`ipv{4{ar5Urw^MWR%!_gsBb^P+ ze$J%s)BUs9ts3fyhB}k`&)8n-5?hn@aO5ff5PL3N9@`+X*qhW)#BpVG;;fY>uQuOH z@~itH`nY+1#lfpjq%ypt&O}5uTZ{h?%V+)2+Lf6aW>8Km?LzsInLk#qmT*x>JsX2J z`IRqW1+9hBT`a7dZxp-kyrF!abf4H@!Ew@~*0Sy`QF`w?0og~bV`PioG?6wZpUpQ; zFCBsd7#KiQ%hu5i!|UzNecRbZQM;T-q!SupI3I3Yi~#73w|tNd3YzUV<<4s2+}mSZHj?7oBrq)C!31o|w{Ko%eFm z?B=~ys&kh+A>fmyll+(KT-UO$)i=d-aS+MtchGgs4~rB!lsRzVMpcz}cW>le&hos6 zrGP`>AnD6-InQig%CzH#&$aslUBr1BPk8F0-cocjyl}23v%E%QR1_LhglEfGG*xgj zdjBnfaR5b@HN#t>OKB}mXWJYlT%G!jPA?=cL|F#*m2fTHebYP1@0OaZmAzK&rongX zilSrg4bfT7wpoVkC5Fm4`sC_Oj+|8|rqvPL%WJ2HH3ZM~*7_pTPR2%FdcE||2{$+pA=5)Zm(XueY^4V>kGjV5&mUmB3V|{ZwL_M?o^5sCr-4bYvhe% zQr`+xe(ML1RNpuj^t)Fk8iS*w7gp|RY0)^X&pyl4F1;Gxp1mL^efUt8yZGh6XXAFX z&@+1XU9!fPv|i1VcM8)mkn^vPscG9%^yiy4zSM;)kHcl*<2zZow{ZsU0Ab+gCm$-Y zmAZNrbHM%Z#iXUz1L9b=W|Cqoe*KUqKXnQdu~|GlF)`7ec9T_4PapY!FE$QV5IU0nLdM z<2jee{|zl!_wAgVoTk=RUrt`$6YcHoQQNoS3dykvalL*U#kZ@!5IFnq%g0GP4+xNU zaJc*_I5;?J5B}~J98veO- zGMuyj{Qb|h2+%;hg)&r|zx|0dL)|5Ye=kz0_CvSH2mMB_fB)sr6Q#XI1o;z^db|Jr z=D61e#RYSJBAxGnf&AwZiEdQ!A+RbnCiz#%Ws0;ouZ57eP4tpys2-12!1Jm`Uesg0 zsJ}Cy0xz0sP5s5XGV{K$_u=LH`R$TrBZzP9)4?dMBBs-8PNFS_eAtp6s%b<`E2a_b z&99h_5&L3YxpD>KDQ9EDA0Vr5rWWYum)s-pY06Ez)(-zaH?Ymfa0B`N_|lK5saj+H zILp@!#$Cx|?5Bv@SFA!p61l5>dP|r`@dqiAIv1L zO-+H=2+_j*V@oDaMkr;M#t|ZMEOBkBe&4^VGchs6%D+C(9r=|m`e40SUxO6;?N`IM zH_v-}dsjWkVGt0Y2YTe-aHS2YvxNVyNvc>JOU;-?@}qHWPPKP?Je~{^Y27u({56j+S?{J0 zhcF$3l)*oCta{{N-#&m^fB?!)sN6ev)pVV5>gF&GDOJ@|4<0;tdn^{yKhEM&AF?Wp z<-y{QPeLO1_%S=SYTDVeXS-FjQ{$k(elqDMSgP3J%q-uy z7h?cX@-m8XJ%xo!SS7x{Lh|G1Pu_S3X6232aT_5eW#x~!gvR{TEdH2zeJwl#Cw7UE zHpDN$&*Sq$&vVWAiHrKLKYJ%PT_e}3XnAQ33r|qJJD6qqo%pc4q-sLGL-2>5YpJ6WleN1-{jfiP${F3Q2!Lqox67}f3H27@5{p*C^T=J=}Za^5BG&s@%Hhl zd63gpm!L=$rEsC9wl?_98*kN&dn)P`&YO$Z6cifvHb+Rpo9n9|(O>_z+X{={_n33h zeIuBxs{HGiMU_du6YY)ob|~v_`mpzZY@R@zyfDt^QQgZpb>~$A(qqie?dx5o$IrXR z^Cz-yXk;Jza(f&`w#j|5`kHSo1_E2HXfjN5xv+AN$o2QyMeFR5@JnX>v9}^GP4{Ne z>o`tQd`wJyurc5Fy~LL7*RNlCg#%<16gU~Gn%d!=qn~rHQD^}?fB3L5+-ymmJSgsk(U?v`ug@7i_WqQi>Ds7HBTawkrMJpi5#*CJE?Pq411JrSCBf8``yi1hVm>A4{vYmLEBz!6|n<9 zGREb0Ir{CWe7boi`Kz!$pc?&bjS0#(?HwPj_A(wU;z1m+F^cduoY$tGPQ|zn&#AYH ztP)o2|2EA+JUXsd{!Du;g@0mM7=g_Qm;if$!9zCwDFgoeP4?ZH6DJtXk_Mva+wFO*#)7{=DJ>X@UM>r$|kVjg^QV?Jc%8 zx*2(iXz%xWS_jX@!hkPe9Drq)%=|I1aNqQqFA-yZuW%JUFUCvO*VpIw@}(iE1T$0; zc<HKnHc)}4m8 zEYSy^8-F{p@=0a!^jVIgQ-k$wYb|ai~TRS{0tAe`JZh@dHK>$C5>FlLT zr*4F?OG4Sb5Bu`WnHL{FGND=5^ySOdn>PuyIKzduvRM47FOR-wP|2+E(|u#e-;RS5D+Dqc2_+pPbS zRr8M@KSmGsmkwBIm3~@W=62h42%r-!8MJ!-{P|_4HRFUiYJ9ZfE|1lg7r5`PGz#1c z`t83NH0!D74i)IJFMz;5k8T8LL_NWRi}JnYrHd zP6IS~%k5v21mUh`ory%S1-&73@ceA#?G4yRN#rAHd`XEpK@!c^-JT z>OGeYq{cv;Hs+s0PjFeT##7IIcx>LTREWW4A;0&*c-?b{orOZn6*yiS|{aY3x;8*Iga^}O8OV4IBKe% zVtweGQB+j)a9sU4w%RJ5^=p0^0=iKf8rN>Dm)FN_@P(XRBNrR(dC&=2{EN1KM)H^s zi-_!RO|+Th;~zin#okOYT-%X%K!l5RisPR5w`gHTQBm4UkwlQsDCX4Bp|i5GdKDS@ z0$v#!^wkh1?`O}(etdonU(I?xplG)H4Pj{JWzz6Tdr64hWh44g+-S)1C+&88uK1di zFL6C216u&`U2+@=S93yiF~Oo5|Y1IDlYeVoZnxp z5GiObYh&|C?VZtkM_kBN-^Kz6>uB2tDTld?LekTxr&jJkwYPu<4#?9s_tw<6XxH}# z5DS>PgrN&sP6P@*nE##w9Tcr#xdzP^jkNYv4|%(H`o27cV9(nLHw4iKh3mTYW$`a+ zm)KOpAjECb#R4c=Dv9m=jlC)?lF`oF;`M+I*|hwNnPilfYe~ej=`+dgh1?-0FT8ia z{QUW|--{PsEdG#1jIse$&yJtwHtk_I&c6pJBJ2#dBJW=1j(B{#ACzoHCMN7?A=^~t z@=NB!0kDC3cQ%_3m=W>>2kuxOJj^d8W3bE8+ntKUAJTX*x+`y3^F98~UH0n>8%5Y@ z88R^eCMlqE$j^&)Vc;#UA0mOYA7C~$i zAa5O%tQ@^+%%BCB(I|y%SloWkTtJ6A*Y$2OybB~WmpuZ{2&TG^RJx0Qi@M@fS0{~B zt@r8%`O>>l%FD}#zNh)#CLiKGT<*Lhy0z5v>Cs*Gd!O(j^BGV~XR|m+1+D(J$O<-^ zscUn+B)DE5?lk`1o{F&?uRFF;yUief0$UOd(DHZvptD!5PysxI%?!o$^@ZEAva+~g z9GbW=J4Gv~J#jbw?3~U*z<%mQ#91kFnV22uqJaF7Do>o_)GZy&Lhj-giyB6@1`M%@ zb7K{5Zue7!WTd6h3bh3n-R!Dfti?I#x;YdoWcZn!Mdj=b-iI=(z2zw#6$ZNuYWZ|9 zvjkH#`KMDo@%LT`}6&AECe^tpFG8>Mfu9PJx5<| zcW=qP5$fPdpHYlteEX=m`_7p#b`8(l;(O%K{xg;-6_QRKKgG^sz5LFBJTf#3&#|(mwp85Hr9xl?=#n6cR+w5Zh7JxZ2V;Z7 z@0lda(JBlCrqGPdliXP>NGxXtj#lTo1qpHXMAWWQXzSM@`;ov}Uy|@Mw|d|T>+0&T z*LD~4Rhi^HnVYsNuL;6P69)$LQ-cM-C_wPAujr_xbJ1qS#)_D z#le3%XwKjcG>YkUZnQETJ9g2UUPe`q?USQf@7y{C=XNYzeAr?Pi$7pM_%H}-pIe9V z6FPz9$ns+rlZj&UyuMeVp(+Wkumeb`s5DY?i;IhkT()^V#+PbT)U;)Rs=vAK7fvmE~iAE;a5Ev5vi$X^XNZi zXOkMW8am)0%CZ`B4erpWOb~RqJAeCq0Bo1HHxpT7;?G)MrlAyd;(ws8k7d{5zB@s$ zUg;)kw>XFbC`?4hVGO72_iP%v1L1PN4EE00j3Oh`}S_M)-JU>0%84qu6iLo9ja?S1@{L5oDLEQ(`8UIuKXbez6Yn#BzVv(Kg^? zW`L{ivFLLv+|Jo>WlVg%ZmAGF0vstDOT#%SUY05?>Q zYkx+-#5+5iJ4)2~6leJH5T@!Go4Dj;eAuLIb8DfQUQU?=72+Z6AF{I*X^VPhY@q9+ zSR0TdvQh! zBT&|>txa*-yF}0{A~iEHX2+?#;(GNhiIu6cTwB6p$CSLB`Kbd-bd8OZBQ{cg*daw0 zW1hc<^3~aa5>X1BJhZpdg<+=K zq+4DY*9${%(85c19c5J%-iyreWOK=f^gb+rHPheU-Ig3_)#s;X6JV=kg*^ud=hxtb4H3qLjMI%HFo7y&nK5ILxLb!8>*_ zcu=sZj{b}^Z}|B&a%DcRH5CJMF#rJ=yZP~=j8a>XrT%(;$P|YEg)*4Z1x$MwI5>#L z#>Qsf%1Xt)rZ~7mS!DDc#}x6{>EA1NCm&4x#ZaKmKHZmh!EJX%E=F7=j%9qB&i-<5=36u(+%UYYvM7;7_m4kBpLA4_R|+#h8VX{s7?N*6wO+aA@cxPpn#4 zNbh!dB!cGSY=+;WG!k6*p{`EMWmRS>_mwV|96x#fCZf3OdeA7o zkAYB%yNUP^oxw!_F|)jP-O#X&@$|?ldP`X`??XmN-JM@fF=Q<0O zB)(d7%-5 zDD)Z8t~-gA*vz2$qz5p}VXa+`$8ua6UGFwy{!j(oC9xzfC^UAZw17Jr2Fxq97w=s| zkv*{Q%XKGj>wV9@kFEi^%tbzY2_)$tfb=~XZWL|s`DQGud+uncp@Sz?6MT14P-}hWn zUhb&De6qZ3k*-~O3ix-nLY-4Da5jhR=@(0v#6Bh^O?(qxk~TCvUsF@VYOAd**xg_~ z5@St8=yQEZ&1reKT=(;m{fz7gWYSBRaxjsebPX>60xHEXpZ;%OIk*cO-88cp7hXK3 zapTh`e#8R?Z7r>+MpHn9HINC*@SfwqEi^!}w?Jp1A5FnUD-CD%GH<|L(z&iFl|i1H z$bJE^t_Sm!j={mN@JhXd{XMssvjb;CPRcF?RqA>ScKIow!?nn zAwXEz*`<3kwR3D{ z?tFUOm)p_Xk#hIKe1HCN^Rg9O^a{x3LX}h*uHL_Y7Ohf+BW|UG0yfwE0|V<}jRSq@ zobVwjy?}tQ{+d8cD>OP0BrXe+Je%!fq-dkxG{L##p+Ic$B_pz7nkfoQi;J zWVIeF;Kp_&0eS0hdn3iqO-&w5@+Lj6LQtd%fDns?*d^ujn&SU}#Wiv0f({3Jo86U3 z2?_Tn8Ujt|Nfkv>Os3mD%$^95uxYaAQ&FA#-tP6hf6OF7WO6O3{up-hxpr@xWjJpdZ&#P=M13Sz_X z%^k(iUXz{u&$$6=x~|<*qwY>lLUv2T8Mv=(mWGGsDjMtS(TTZ{qj2!f%qVH9Gc2y| zZ@zt~OZuCc+LMbDI;m9|v9Sma4vx+wRJ1oICdp-#vdo71^VN$V;2;u7z44Zh**QUL znY423k`H0}G5He{3BZ*K#4XCEz4{kRlB-3PwRWcHw?GsI2DNQ`{*aVgUnAdV#C1}|?^TXu>`b3*hkhTI$2D0yConvHl zjnQ18<7Q=Lt$~j1LcC8e?nVP(x)v_#6|n0lx_~YY0Qr+zA1E&OLA=bqmk~;h&!$bX z>FPKYZD01K|M8_VSdYstTihL;on+84p~BKZfe-1`+lQCUww#b{%AF_U2E6J0&nh3l zVC4gq^8o;7l)M0z1kIQ%*(jKQtgLvU6WtlpD-g#()Iu;~OBQ2bSRuHM;g5Cey~{88 zpw>Kvq}n3O32yxc!l2xRfkHWuV!yo?)l4ysFS*dxy7z@;PxBF1E@PJTC)-E&GI}No zJ5BOI`a5^_EQYAlngmc0FoHY-6rvCNmc>d)WW!!pGs!elsds^ug{A8G$qN<(3x%Lp zGoC*$%P|7|bI@`s8u0)$Btc%!RyxXx58~JY)YLscxdP(xIS~<> zI2IspZbtJ`P#N(p1D1=wo21|u7^u?752Pt{mtU)^D_wVQ--jk);&cF)lssksA|OEG z+BJNdm*e~6KFd0~8c0=49>&~jiEe&xZRtZVfS{u3(uq(*fL)HOhUmOaH{c89IpuCr zYRnalFdmChiLAZWXkp(N_kF6uL8}|@rdalh7uw_T8eY=QsVp`HGdheu#S<}XeFE4H zm0{ZBnVT6l5bH;g_BcL6KgY_ zmn}vSs9GtBbGZJ!Kd3G}ri4a3krQSQi+KK4#1EAqkiQ14TSc4ptj}{Hz>?|#(TE~( zEE17Xr3ygb#v>J?3Nd0tRrV5%p)D=zf(RsuE^0dvF#T!kW;|f_=Bb9)ij#@HC4P|EC+BjFQFT zV6n-S0_RskZ|-Wi^f4KqmBEd0iX03$bsPCL-i zj{|Z|>U<8!eZ=$t0I_pI3a5OqsCc1&oE|8KI+K7KV3UgUTw;Yu>KGulTF`Dp3P`AU zv4Py!pnS}k#+k4r1+!nPZHSJ`icHjH%bF=3G}J9I;v=RF+n*kFW*dH`UZ;VV1gtF9 zJ52{S1B#X_-9dh@PAx1%*+%I?!N9ACh*@j;LjPeSU6;Mgu8loY4U@J`v%wBpL zpFV}6y0`m-)^rBnV#I?$Qb&n0*PV@Zhg;~tP#kw}XR{H|Ehz;>f}1XvP8mKjTUQTihhzwo2n>cuY~lf)f4ESE%XY3@`K+*Z zy^PCi1FDXggYFAYQ?rz7y%@-82E|!NTsLW9enb_!mEW*z*{(Zw@D3oeZ)?4F1vsiA zl*A+tBXeZ$Mpnh|>w8bxo4B)!cPV-qqC#^c-n=2d86!pudjps$m>|o9nn%96nlV>Ke~B50ahkn|RB4)* zqtY`L%YM0bnL|!xxV;Q!H}NCP-b4e?{$4Y#n{XN3@k8Z>cc!jmqkP;LJ7Vb z5CWBym=h8b&N4G6)ZizCWPu|q^c&5cxwql0KbK%T8t+X#n%fWY*QBTU30A+MKz-QdETe+x`4&?-c~oi%Ac zQFA<$X#gS|k)SnoLiH~AGZAe+Ttr>8sQD611=~@Ml_uS(1mY73cyF8ieVSWZ(wA2=SO@?N z&E@~x1?^HTWl461cTwQpb+E)J8m+0SJbs++^fE286?Iq2euk*&M&7`{ z0pEZf;bC#i>DU`&?w$nk7Zhfa9Y-Wxw~#KLo>F_!dFH28vBhlHMU?4;oh+-aP6@5R z3y?yxf!M*WHO&<(@67@sWB;A=g^&9@K?|&X$eSXGK#i)+zF)fx4ETMRHMh{gi+>L? z7BDPo!b4{-82ZMFX*~!1s^YO8C@cb z>TBN%)7MrLf<^$16SS&P@bK`=UWpLg9Fo`pM@!b0^Smjd7&d7YWXezN`sB3hZf4iB zop5kS4m>HPMI8@xPSYQ*6a5f0>B7I8bhAGGHC_Ej`PbPd-K@Asmp*W8T&%3Wwsulg z(z0#mdaf^M^4coH`~`*s$mby58D`I84>gnq@V}jy6PIT$I32z-vZ+m8?~%`z$_lQB z{fTFX)Y`r&rlD5A&fjABfK)8f?5T0F5i=i zZ9_ytL4g>eMFKkyY8e*LC)Fse0QIAG#-;`+dWdnA@KTu{HS0StG2D{wvF@X z(IXYHR=|!fPe_P}GP?F5cXb8;Sfy%_AeZeIqjYyd6CQrl(eRoE2Tjeyn*ajqPGf$Z zi>ql+#J0-KHBwjiG3trp0d1e-;UPn%edvMhmG16fk9*@x19bpi{hXlSY0w7h26UVjO-1%Pj+md{#tca3UYI$%LxuRD;s;EGfo~y4-nFHJsS4Sc~$C6%$H{ z{pDC8#tLRyR~41WaX>YN_V)IQb)wMl==(k{d`v=IERe;!C)3d_2dH)wsJFV!5qb$1 z7i+jk*jI#SE)g1ii>ih$<_iuI6wiY^W#GE~%b;v?*cmkv{DaA~GJOQLNV+~C9x$z{ zr4GVeY$daEvKwbI<3Oh)qT93#69LfH%vCv`Zw}f;!Z)RU2%*c9AaE{N=*ow`v8Ow@4MMaqsRkp zuvq?iq(;ED8w1HzjgRj<6O$f5OrqH5cz;Pjl5)b705vF?2Kf)AGiUsgp~vL<%JlR6Eib2Z@KSd-uqPJpo%nuYh&d*^R#qzu3SKphQ;V5$^&)KO>DrX&xo9s(l1 zzZGA?0frKkpAp+zyDMOz2A&_#7E~vN-Vi1}YL9yspC+1j;T}9PGip5I(Mq-zZJ|pNH(#FOtxWqJm&{?;&V_=1t&Y#Z>N&=F~0>s-v z07pd!`#US6Uh!n~BQ#*cE`oXH3-}{@Il*pXob6R(3&vX@;o;-5^-n?_rx|8;oia)k z4`&n(`#%{fIV&cnLz`t0B#g35=y?64a?fti{a_CreqgJDQ3ZBT&9mcZ?M27_qV*n4 zOSp0YMcK4&Rn~#Wu89*tHKAjB@e*v?@9vP}fP+9RzWut8#i$2$j&ip(WHb#lbx6=v zmQ|o6^ufXi7Q@*f^Ur(C@-?oT&fhNC3xk6xq2f$JbCifg56P3;D3$mlM}V40_9e{> zMR_q`!u)*{P^9Jo4G%tC3ur9sMa)KBBy=K09h(Wclc{t2qT4@SPY;)uHoAepAQ1BU zb-AI4W`-)twm|Oy5&n~!HXx{siVM;z018wyX*Vv5Gw?|HH8^r{vuYOz7Ia8P zhd)5xU~iFv5evO;kV-(o2V=`Q;4Xpossde#+iqT+UAIDu2opvh^?VZw7(snucY+}~ zts0BSadae*3h|pQg2JRii6{!~xvOMo6?d>946rrsCMs0VJxM%jveWTJC%6bOR&cDM6pj-qhS=u=`x{c%XpDFi&G>3UW|qrd%8d(Uu5- z<~gCHTai9jtbcBaY{UnN&E}2e!D|f1VFy_)q2|bhje-pUTEpgS7AxO0$-0 z`H#(nhx!!{W#4qXV+YaUW9D2U*k6|#AO@z)$mn&Rf4$Pl-vQUQXX#e*>W<{-*#8{PuI#vQfQ}0)-pZ8^sUpI5^Hl(%Qa|0!{sOK;pztveP;pRe(=w1_ zXQ8D7SeIG-`IYR{DIXL}z&1xs=tEC@tM!)Cf?W&kuCra$b^! z1+#}fur%LG?X##s#sYnkt77XM$P+q~`XGiUsTVw^f|$}YrJgt(HaG@gY%k6dU+=nW zP@y`^^a6|;*)3uZ3t^E$b&KynB?rZ&j&(t*-gTNyEwc`2DDDS!_-GbGAR6E;gSJ(D%3jz?eTECH0J+*w3?7%t$8b68(03S!-Zcuh{;UeoLfatI;+v z%;(tH(mJLrNcO{+{Lj%j{vewlC~ReuDHEQw1BQT?j}P_Ig;A{(Jg6sd0R>Y`mkoF} zS}5PB8Bsq-^k+^vRGKZ+>IHQat?OVY0`Fm9Yu`5bioh|yAym;hQ|Eo`qzkceYt)sK zFelc*hCm5?kObf;heIeFqd)=ULk-nJ*OmIy;NTT3!(E5v2E8iBVdVjgxiO8a^&`xP z$851XbYpOKU`Fsi8drJ(BxljYOX2K?pHD=Ixl#kx0>KN_w&5^W*~^`3Jo+tKnyb~1 zpm|IW7Ozs!GP1E9hpG<;MIaLhUFmLzFrm};AGhI&6PVyz zFCEe`%{wT@Mh=%9*dwlO)d!f1Rpj9&m=-TH15dq0u)}WAVSX3#T%Pyrv z4y_WbQ&~$lVY`1|Api@*b68bE5aZz}2$*zK(p1s4hZ=T2uTSq%q-cW!^*K%q31t$O z?a6QhTm*(AUWTcyCHI3Dr*#~v*<>m-0jjk@@#fvUEiaEE>eH`ETHcGE5gc}iS7UXK zz3l2X^K?1{z;Rop`vEWB1nM0@frtTTnxa$2P6z6o1dF!w<7OyNa`dj=z@dj0>&{dk z039?jo0he4FOh*1)Aiu5)C>5kmQ61fvSgNeZgLyMHXcB6tXeoqdN8hj{P+=j-XWL0 z_K&85JO4KS>3@z1)pvAspk!f|0fKtpU~9E4P55|w$AUQtJoWmZ2LiE^%RYfh$_X!R zp=Y2jR|-RN2J>9@cyP#W0jUM%Yt8$PbUK(EA6&FMKAzT9r3$Zq~8RDw$BZo=ceo(et za~wrE{dhuP5QfP)wr20kTrX^I{)(MNVaPs#y&~sJd`KC{=rA;BKbyJ?AVM~PQpxlk z%bc)$2)FHQ*L9-(Qt%@|6`O#f?b|$=+R(2gGw8LmxD?2M4_qPw>Bddgj3 zhohPc07;M{!DX!gK2VRP#&PI3k|>zJOUm^}6WJit1&3ds zFYuUjCG&QUqFm|HaJj=`@f0hK#ilQlyaPyx%iU_a_hG~-nn{d=hA@*i4GWlW1;=% z&tv}r^!S5K_XA(p`s1*;MIcKiY_M@5dSFg#`uw>XFqa7KQWh*LN<%=kPhH+Zy_|+E zZAttO-)Tz;I$zF>e_}VH%M7OM}4;>H948Zc3~i54F;{PLAL}`Y_(80 zw+3Dd@ZFiNl}QVHeaQO(>P!)&HB`T+z;^=&eHQvg!yK41yx?}?fY${j_pUou6=Q3M*ObA#L$P6BPCwkW>ho z&4x-SkT$rs>tBDjLU7+)yxXDHi+oQBEAhVkY@J z;RrZWfzzvnBgNfTiwE{|Jw%!XkKsrUjGzE?5f7NY#sKtq=w#uwEuX(WGdqCOy(Zx< zGdr-yn95o@^b#$P8P2+Apuc&8Tyh`PUc?K&8rA|IamVnLl1flMOdv=XYc`Zg2wCvE zcT}(R$%5Vg>lm9W+)J8r@t2kj`yI&GE0NoodC>)4gW~jwT`Pl{UK>4`NBuuFa$$2l@9n$YrYGo=G9&&|?9h z;r*k>#W&`hIq}~IfFVb%r$DLR6ta_w z!)Ml5`m){5W8aRr*)Vju^v{E=f`#XFl}K!RbZ3$giqDt%wo|OR*%W$4A92aHtUKSt3+2Hg{PRyy>EQ zxDc3VDFV$^ST=|*5Evn6984M!2T~v{7z==b0}ij}4u{w4=E<7lTxA}p{{n73blAKn z9X$t?))1kj;uoj>Gl_?N9tFkqs?M^Z zT95wc5HiXHssY!V+>f&ZCu)n)U5tgEvt>vF7ymyG4S%cL+7Jp~VE_I#P+>N3EF z@GHqTmDP?3MK2n-+ws4{TK0A$z34WwM?lluUAvmBeb*cqDNk_Us8s3Z(yXAU# zIN}_^Dd?;PyIA1DM z{ri8;m!A7I0_c?o4rhR=h!%Ve$1{t6QsaAi1OyP$2-)Dn{3hY%=4Ph$y2%{07kp1d zMhV!rPxs8)T5xT2^H6o|LqQrGB2Z@ZL{COzm(X^+>Mj1eAJ3g*eF2dNTN8Q`DnIMB zt3j9I7A*o~clTPJCj<6Zy(0EzKs^YoylL?z2$%q)`kzB_17~;Th+wJyJaB|Jf|aBu z>|1kG)1I{(D+|XNXb?XvpZyrpo7y|~&Mn-}&r_CLY;%sfKSfAa|pQ*kV~ zxtRiB#gWYFg|~#GoebjQ^iU~SSXp6y)H;A)5aD@U3Ozm7-A8O`L;|Oco~Cjh#-EI= z8$*p8q*G|f;K~8g2*yqDYQsDHledb0PS00OT7e>pubM^yAD}0}8;3AiHqgM?-~=Bu z@ls3w>qsozjE_7_4OqM(9Qc?JRS<5C(vNu}8LD$inH7XlrM&~Tz@)h^+B!fqJ+%#D zc9^@vFG*-C4lR({7x#Wf1^GK>y-(2}G{NBebC3Tl zjR5<9XThIa>q4f5Y^D_lZ2#Ob^_yP@^FvBx9*X#gq|JQHQaz71eSl!;F?@@vD|{1} zu%-wi@ik8r{t_ zJDC1F7$p8aK@g08-VHv-Cq4W}36H~PM=yyWN3leXk_bVLb`Nq?Hje7$QKLY6<57o1 zj-bI2G=QCO1PzX$0i?kZG&q6=kOoIHtDu^@Lxibo8j*lk)v-s3eVqMI7*7cO@SQknZr;VCBeaRj3O=E4zFID!g)Q{kvS9@WRc5%~X4!eY<23w~5C SexHZ_q`ZuRbmmn(&;JK!!w4S$ diff --git a/Default-Portrait.png b/Default-Portrait.png deleted file mode 100644 index fda18a784293fc9aa9b274ddf8e9495a688398cd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11476 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 diff --git a/Default@2x.png b/Default@2x.png deleted file mode 100644 index bbbe0940744483cfdd4d04658fe1158096585033..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16139 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 diff --git a/Document-molecules-320.png b/Document-molecules-320.png deleted file mode 100644 index cf95f35c4f9da510e53b99935c42499f21cfea4f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 24331 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 - - - - CFBundleDevelopmentRegion - English - CFBundleDisplayName - ${PRODUCT_NAME} - CFBundleDocumentTypes - - - CFBundleTypeIconFiles - - Document-molecules-320.png - Document-molecules-64.png - - CFBundleTypeName - Molecules Structure File - CFBundleTypeRole - Viewer - LSHandlerRank - Owner - LSItemContentTypes - - com.sunsetlakesoftware.molecules.pdb - org.gnu.gnu-zip-archive - - - - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIconFile - MoleculesIcon.png - CFBundleIconFiles - - MoleculesIcon@2x.png - MoleculesIcon.png - MoleculesIcon72.png - MoleculesIcon72@2x.png - - CFBundleIcons - - CFBundlePrimaryIcon - - CFBundleIconFiles - - MoleculesIcon@2x.png - MoleculesIcon.png - MoleculesIcon72.png - MoleculesIcon72@2x.png - - UIPrerenderedIcon - - - - CFBundleIdentifier - com.sunsetlakesoftware.Molecules - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - APPL - CFBundleSignature - ???? - CFBundleURLTypes - - - CFBundleURLName - com.sunsetlakesoftware.${PRODUCT_NAME:identifier} - CFBundleURLSchemes - - molecules - - - - CFBundleVersion - 2.20 - LSRequiresIPhoneOS - - UIFileSharingEnabled - - UILaunchStoryboardName - LaunchScreen - UIPrerenderedIcon - - UIStatusBarStyle - UIStatusBarStyleBlackOpaque - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UTExportedTypeDeclarations - - - UTTypeConformsTo - - public.plain-text - public.text - - UTTypeDescription - Molecules Structure File - UTTypeIdentifier - com.sunsetlakesoftware.molecules.pdb - UTTypeTagSpecification - - public.filename-extension - pdb - public.mime-type - chemical/x-pdb - - - - - diff --git a/English.lproj/Localized.strings b/English.lproj/Localized.strings deleted file mode 100644 index de97fdc..0000000 --- a/English.lproj/Localized.strings +++ /dev/null @@ -1,63 +0,0 @@ -/* English */ - -"Error: failed to prepare statement with message '%s'." = "Error: failed to prepare statement with message '%s'."; -"Could not delete file" = "Could not delete file"; -"OK" = "OK"; -"Error: failed to dehydrate with message '%s'." = "Error: failed to dehydrate with message '%s'."; -"Error: failed to insert metadata with message '%s'." = "Error: failed to insert metadata with message '%s'."; -"Error: failed to insert bond with message '%s'." = "Error: failed to insert bond with message '%s'."; -"Failed to create writable database file with message '%@'." = "Failed to create writable database file with message '%@'."; -"Error: failed to close database with message '%s'." = "Error: failed to close database with message '%s'."; -"Failed to open database with message '%s'." = "Failed to open database with message '%s'."; -"Initializing database..." = "Initializing database..."; -"Downloading molecule..." = "Downloading molecule..."; -"Error in downloaded file" = "Error in downloaded file"; -"The molecule file is either corrupted or not of a supported format" = "The molecule file is either corrupted or not of a supported format"; -"Connection failed" = "Connection failed"; -"Could not connect to the Protein Data Bank" = "Could not connect to the Protein Data Bank"; -"Could not find file" = "Could not find file"; -"No such file exists on the server: %@" = "No such file exists on the server: %@"; -"Molecule location" = "Molecule location"; -"Error in loading custom location" = "Error in loading custom location"; -"The address could not be reached" = "The address could not be reached"; -"Online Data Source" = "Online Data Source"; -"Download" = "Download"; -"RCSB Protein Data Bank" = "RCSB Protein Data Bank"; -"Custom location" = "Custom location"; -"Description" = "Description"; -"Statistics" = "Statistics"; -"Journal" = "Journal"; -"Source" = "Source"; -"Author(s)" = "Author(s)"; -"Sequence" = "Sequence"; -"File name" = "File name"; -"Number of atoms" = "Number of atoms"; -"Number of structures" = "Number of structures"; -"Current structure" = "Current structure"; -"Download" = "Download"; -"Done" = "Done"; -"File already exists" = "File already exists"; -"The molecule with this PDB code has already been downloaded" = "The molecule with this PDB code has already been downloaded"; -"Cancel download" = "Cancel download"; -"Connecting..." = "Connecting..."; -"Downloading" = "Downloading"; -"No protein with the code %@ exists in the data bank" = "No protein with the code %@ exists in the data bank"; -"Connected" = "Connected"; -"Processing..." = "Processing..."; -"Triangles: %d" = "Triangles: %d"; -"Vertices: %d" = "Vertices: %d"; -"Elapsed time: %f" = "Elapsed time: %f"; -"Spacefilling" = "Spacefilling"; -"Cylinders" = "Cylinders"; -"Ball-and-stick" = "Ball-and-stick"; -"Visualization mode" = "Visualization mode"; -"Rendering..." = "Rendering..."; -"Search for molecules" = "Search for molecules"; -"Protein Data Bank" = "Protein Data Bank"; -"Retrieving titles..." = "Retrieving titles..."; -"Searching..." = "Searching..."; -"No results" = "No results"; -"Results" = "Results"; -"Molecules" = "Molecules"; -"3D Model" = "3D Model"; -"Download new molecules" = "Download new molecules"; \ No newline at end of file diff --git a/English.lproj/MainWindow.xib b/English.lproj/MainWindow.xib deleted file mode 100644 index 3547858..0000000 --- a/English.lproj/MainWindow.xib +++ /dev/null @@ -1,388 +0,0 @@ - - - - 512 - 9F2523 - 672 - 949.41 - 352.00 - - YES - - - - - YES - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - YES - - IBFilesOwner - - - IBFirstResponder - - - - - 1292 - - {320, 480} - - 1 - MSAxIDEAA - - NO - YES - - - - - 274 - {320, 460} - - - 3 - MQA - - 2 - - - NO - - - 2 - - - - - 292 - - YES - - - 292 - {{51, 176}, {219, 21}} - - NO - YES - NO - Rendering... - - 1 - MSAxIDEAA - - 1 - - - - 1 - 1.000000e+01 - 1 - - - - 292 - {{142, 212}, {37, 37}} - - NO - NO - NO - NO - YES - 0 - - - - 292 - {{85, 226}, {150, 9}} - - NO - YES - YES - 5.000000e-01 - - - {320, 460} - - - 1 - MCAwIDAAA - - NO - - - - - YES - - - delegate - - - - 4 - - - - window - - - - 5 - - - - rootViewController - - - - 25 - - - - renderingActivityLabel - - - - 40 - - - - renderingProgressIndicator - - - - 43 - - - - scanningActivityIndicator - - - - 44 - - - - - YES - - 0 - - YES - - - - - - 2 - - - YES - - - - - -1 - - - RmlsZSdzIE93bmVyA - - - 3 - - - - - -2 - - - - - 20 - - - YES - - - - Molecule Root View Controller - - - 26 - - - - - 36 - - - YES - - - - - - - - 37 - - - - - 38 - - - - - 42 - - - - - - - YES - - YES - -1.CustomClassName - -2.CustomClassName - 2.IBAttributePlaceholdersKey - 2.IBEditorWindowLastContentRect - 2.IBPluginDependency - 20.CustomClassName - 20.IBEditorWindowLastContentRect - 20.IBPluginDependency - 26.IBPluginDependency - 3.CustomClassName - 3.IBPluginDependency - 36.IBEditorWindowLastContentRect - 36.IBPluginDependency - 36.IBUserGuides - 37.IBPluginDependency - 38.IBPluginDependency - 42.IBPluginDependency - - - YES - UIApplication - UIResponder - - YES - - YES - - - YES - - - {{425, 129}, {320, 480}} - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - SLSMoleculeRootViewController - {{411, 37}, {320, 480}} - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - SLSMoleculeAppDelegate - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - {{355, 171}, {320, 460}} - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - YES - - - 2.300000e+02 - 1 - - - - 1.600000e+02 - 0 - - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - - YES - - YES - - - YES - - - - - YES - - YES - - - YES - - - - 44 - - - - YES - - SLSMoleculeAppDelegate - NSObject - - YES - - YES - rootViewController - window - - - YES - SLSMoleculeRootViewController - UIWindow - - - - IBProjectSource - SLSMoleculeAppDelegate.h - - - - SLSMoleculeRootViewController - UIViewController - - YES - - YES - moleculeDownloadToolbar - renderingActivityLabel - renderingProgressIndicator - scanningActivityIndicator - tableNavigationController - - - YES - UIToolbar - UILabel - UIProgressView - UIActivityIndicatorView - UINavigationController - - - - IBProjectSource - SLSMoleculeRootViewController.h - - - - - 0 - ../Molecules.xcodeproj - 3 - - diff --git a/English.lproj/SLSMoleculeGLView.xib b/English.lproj/SLSMoleculeGLView.xib deleted file mode 100644 index 5432936..0000000 --- a/English.lproj/SLSMoleculeGLView.xib +++ /dev/null @@ -1,384 +0,0 @@ - - - - 512 - 10A421a - 732 - 1038 - 435.00 - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - 58 - - - YES - - - - YES - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - YES - - YES - - - YES - - - - YES - - IBFilesOwner - - - IBFirstResponder - - - - 292 - {320, 460} - - - 1 - MCAwIDAAA - - YES - - 2 - - - - - - YES - - - view - - - - 5 - - - - - YES - - 0 - - - - - - 1 - - - YES - - - Molecule OpenGL View - - - -1 - - - File's Owner - - - -2 - - - - - - - YES - - YES - -1.CustomClassName - -2.CustomClassName - 1.CustomClassName - 1.IBEditorWindowLastContentRect - 1.IBPluginDependency - 1.IBUserGuides - - - YES - SLSMoleculeGLViewController - UIResponder - SLSMoleculeGLView - {{518, 113}, {320, 480}} - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - YES - - - 240 - 1 - - - - 160 - 0 - - - - - - YES - - - YES - - - - - YES - - - YES - - - - 22 - - - - YES - - SLSMoleculeGLView - UIView - - switchToTableView - id - - - IBProjectSource - SLSMoleculeGLView.h - - - - SLSMoleculeGLViewController - UIViewController - - IBProjectSource - SLSMoleculeGLViewController.h - - - - - YES - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSError.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSFileManager.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSKeyValueCoding.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSKeyValueObserving.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSKeyedArchiver.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSNetServices.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSObject.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSPort.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSRunLoop.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSStream.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSThread.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSURL.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSURLConnection.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSXMLParser.h - - - - NSObject - - IBFrameworkSource - QuartzCore.framework/Headers/CAAnimation.h - - - - NSObject - - IBFrameworkSource - QuartzCore.framework/Headers/CALayer.h - - - - NSObject - - IBFrameworkSource - UIKit.framework/Headers/UIAccessibility.h - - - - NSObject - - IBFrameworkSource - UIKit.framework/Headers/UINibLoading.h - - - - NSObject - - IBFrameworkSource - UIKit.framework/Headers/UIResponder.h - - - - UIResponder - NSObject - - - - UISearchBar - UIView - - IBFrameworkSource - UIKit.framework/Headers/UISearchBar.h - - - - UISearchDisplayController - NSObject - - IBFrameworkSource - UIKit.framework/Headers/UISearchDisplayController.h - - - - UIView - - IBFrameworkSource - UIKit.framework/Headers/UITextField.h - - - - UIView - UIResponder - - IBFrameworkSource - UIKit.framework/Headers/UIView.h - - - - UIViewController - - IBFrameworkSource - UIKit.framework/Headers/UINavigationController.h - - - - UIViewController - - IBFrameworkSource - UIKit.framework/Headers/UITabBarController.h - - - - UIViewController - UIResponder - - IBFrameworkSource - UIKit.framework/Headers/UIViewController.h - - - - - 0 - - com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS - - - - com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS - - - - com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 - - - YES - ../Molecules.xcodeproj - 3 - 3.0 - - diff --git a/French.lproj/Info.plist b/French.lproj/Info.plist deleted file mode 100644 index 373cce2..0000000 --- a/French.lproj/Info.plist +++ /dev/null @@ -1,100 +0,0 @@ - - - - - CFBundleDevelopmentRegion - fr - CFBundleDisplayName - ${PRODUCT_NAME} - CFBundleDocumentTypes - - - CFBundleTypeIconFiles - - Document-molecules-320.png - Document-molecules-64.png - - CFBundleTypeName - Molecules Structure File - CFBundleTypeRole - Viewer - LSHandlerRank - Owner - LSItemContentTypes - - com.sunsetlakesoftware.molecules.pdb - org.gnu.gnu-zip-archive - - - - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIconFile - MoleculesIcon.png - CFBundleIconFiles - - MoleculesIcon@2x.png - MoleculesIcon.png - MoleculesIcon72.png - - CFBundleIdentifier - com.sunsetlakesoftware.Molecules - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - APPL - CFBundleSignature - ???? - CFBundleURLTypes - - - CFBundleURLName - com.sunsetlakesoftware.${PRODUCT_NAME:identifier} - CFBundleURLSchemes - - molecules - - - - CFBundleVersion - 2.10 - LSRequiresIPhoneOS - - UIFileSharingEnabled - - UIPrerenderedIcon - - UIStatusBarStyle - UIStatusBarStyleBlackOpaque - UISupportedInterfaceOrientations - - UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown - UIInterfaceOrientationLandscapeLeft - UIInterfaceOrientationLandscapeRight - - UTExportedTypeDeclarations - - - UTTypeConformsTo - - public.plain-text - public.text - - UTTypeDescription - Molecules Structure File - UTTypeIdentifier - com.sunsetlakesoftware.molecules.pdb - UTTypeTagSpecification - - public.filename-extension - pdb - public.mime-type - chemical/x-pdb - - - - - diff --git a/French.lproj/Localized.strings b/French.lproj/Localized.strings deleted file mode 100644 index ba4cf1d..0000000 --- a/French.lproj/Localized.strings +++ /dev/null @@ -1,64 +0,0 @@ -/* Internationalization by Olivier Kaluzny (olivier.kaluzny@he-arc.ch). 13/11/2008 -/* French */ - -"Error: failed to prepare statement with message '%s'." = "Erreur: Impossible de préparer la déclaration avec le message '%s'."; -"Could not delete file" = "Impossible de supprimer le fichier"; -"OK" = "OK"; -"Error: failed to dehydrate with message '%s'." = "Erreur: Impossible de déshydrater avec le message '%s'."; -"Error: failed to insert metadata with message '%s'." = "Erreur: Impossible d'insérer les meta-données avec le message '%s'."; -"Error: failed to insert bond with message '%s'." = "Erreur: Impossible d'insérer le lien avec le message '%s'."; -"Failed to create writable database file with message '%@'." = "Impossible de créer une base de donnée modifiable avec le message '%@'."; -"Error: failed to close database with message '%s'." = "Erreur: Impossible de fermer la base de donnée avec le message '%s'."; -"Failed to open database with message '%s'." = "Impossible d'ouvrir la base de donnée avec le message '%s'."; -"Initializing database..." = "Initialisation de la base de donnée..."; -"Downloading molecule..." = "Téléchargement de la molécule..."; -"Error in downloaded file" = "Erreur sur le fichier téléchargé"; -"The molecule file is either corrupted or not of a supported format" = "Le fichier molécule est corrompu ou n'est pas dans un format supporté"; -"Connection failed" = "Connexion échouée"; -"Could not connect to the Protein Data Bank" = "Impossible de se connecter à la Base de Donnée des Protéines"; -"Could not find file" = "Impossible de trouver le fichier"; -"Molecule location" = "Emplacement de la Molécule"; -"Error in loading custom location" = "Erreur de chargement de l'emplacement personnalisé"; -"The address could not be reached" = "L'adresse ne peut pas être atteint"; -"Online Data Source" = "Source de Fichier en Ligne"; -"Download" = "Télécharger"; -"RCSB Protein Data Bank" = "Banque De Donnée RCSB"; -"Custom location" = "Emplacement Personnalisé"; -"Description" = "Déscription"; -"Statistics" = "Statistique"; -"Journal" = "Journal"; -"Source" = "Source"; -"Author(s)" = "Auteur(s)"; -"Sequence" = "Séquence"; -"File name" = "Nom du fichier"; -"Number of atoms" = "Nombre d'atome"; -"Number of structures" = "Nombre de structure"; -"Current structure" = "Structure actuel"; -"Download" = "Download"; -"Done" = "Terminer"; -"File already exists" = "Le fichier existe déjà"; -"The molecule with this PDB code has already been downloaded" = "La molécule avec ce code PDB à déjà été téléchargée."; -"Cancel download" = "Annuler le téléchargement"; -"Connecting..." = "Connexion..."; -"Downloading" = "Téléchargement"; -"No protein with the code %@ exists in the data bank" = "Aucune protéine avec le code %@ éxiste dans la banque de donnée"; -"Connected" = "Connecté"; -"Processing..." = "En cours..."; -"Triangles: %d" = "Triangles: %d"; -"Vertices: %d" = "Sommets: %d"; -"Elapsed time: %f" = "Temps écoulé: %f"; -"Spacefilling" = "Modèle compact"; -"Cylinders" = "Modèle cylindre"; -"Ball-and-stick" = "Modèle éclaté"; -"Visualization mode" = "Mode de Visualisation"; -"Rendering..." = "Mise en forme..."; -"Search for molecules" = "Chercher une molécule"; -"Protein Data Bank" = "Banque de donnée Protéine"; -"Retrieving titles..." = "Réception des titres..."; -"Searching..." = "Recherche en cours..."; -"No results" = "Aucun résultat"; -"Results" = "Résultats"; -"Molecules" = "Molécules"; -"3D Model" = "Modèle 3D"; -"Download new molecules" = "Télécharger des molécules"; -"Download molecule" = "Télécharger la molécule"; diff --git a/French.lproj/MainWindow.xib b/French.lproj/MainWindow.xib deleted file mode 100644 index 94d48e1..0000000 --- a/French.lproj/MainWindow.xib +++ /dev/null @@ -1,386 +0,0 @@ - - - - 512 - 9F2523 - 672 - 949.41 - 352.00 - - YES - - - - - YES - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - YES - - IBFilesOwner - - - IBFirstResponder - - - - - 1292 - - {320, 480} - - 1 - MSAxIDEAA - - NO - YES - - - - - 274 - {320, 460} - - - 3 - MQA - - 2 - - - NO - - - - - - 292 - - YES - - - 292 - {{55, 175}, {210, 21}} - - NO - YES - NO - Mise en forme ... - - 1 - MSAxIDEAA - - 1 - - - - 1 - 1.000000e+01 - 1 - - - - 292 - {{142, 212}, {37, 37}} - - NO - NO - NO - NO - YES - 0 - - - - 292 - {{85, 226}, {150, 9}} - - NO - YES - YES - 5.000000e-01 - - - {320, 460} - - - 1 - MCAwIDAAA - - NO - - - - - YES - - - delegate - - - - 4 - - - - window - - - - 5 - - - - rootViewController - - - - 25 - - - - renderingActivityLabel - - - - 40 - - - - renderingProgressIndicator - - - - 43 - - - - scanningActivityIndicator - - - - 44 - - - - - YES - - 0 - - YES - - - - - - 2 - - - YES - - - - - -1 - - - RmlsZSdzIE93bmVyA - - - 3 - - - - - -2 - - - - - 20 - - - YES - - - - Molecule Root View Controller - - - 26 - - - - - 36 - - - YES - - - - - - - - 37 - - - - - 38 - - - - - 42 - - - - - - - YES - - YES - -1.CustomClassName - -2.CustomClassName - 2.IBAttributePlaceholdersKey - 2.IBEditorWindowLastContentRect - 2.IBPluginDependency - 20.CustomClassName - 20.IBEditorWindowLastContentRect - 20.IBPluginDependency - 26.IBPluginDependency - 3.CustomClassName - 3.IBPluginDependency - 36.IBEditorWindowLastContentRect - 36.IBPluginDependency - 36.IBUserGuides - 37.IBPluginDependency - 38.IBPluginDependency - 42.IBPluginDependency - - - YES - UIApplication - UIResponder - - YES - - YES - - - YES - - - {{425, 129}, {320, 480}} - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - SLSMoleculeRootViewController - {{411, 37}, {320, 480}} - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - SLSMoleculeAppDelegate - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - {{355, 171}, {320, 460}} - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - YES - - - 2.300000e+02 - 1 - - - - 1.600000e+02 - 0 - - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - - YES - - YES - - - YES - - - - - YES - - YES - - - YES - - - - 44 - - - - YES - - SLSMoleculeAppDelegate - NSObject - - YES - - YES - rootViewController - window - - - YES - SLSMoleculeRootViewController - UIWindow - - - - IBProjectSource - SLSMoleculeAppDelegate.h - - - - SLSMoleculeRootViewController - UIViewController - - YES - - YES - moleculeDownloadToolbar - renderingActivityLabel - renderingProgressIndicator - scanningActivityIndicator - tableNavigationController - - - YES - UIToolbar - UILabel - UIProgressView - UIActivityIndicatorView - UINavigationController - - - - IBProjectSource - SLSMoleculeRootViewController.h - - - - - 0 - ../Molecules.xcodeproj - 3 - - diff --git a/French.lproj/SLSMoleculeGLView.xib b/French.lproj/SLSMoleculeGLView.xib deleted file mode 100644 index e211aa3..0000000 --- a/French.lproj/SLSMoleculeGLView.xib +++ /dev/null @@ -1,218 +0,0 @@ - - - - 512 - 9F33 - 672 - 949.34 - 352.00 - - YES - - - - YES - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - YES - - IBFilesOwner - - - IBFirstResponder - - - - 292 - - YES - - - 292 - {{274, 414}, {18, 19}} - - NO - 0 - 0 - - Helvetica-Bold - 1.500000e+01 - 16 - - 3 - YES - - 1 - MSAxIDEAA - - - 1 - MC4xOTYwNzg0MyAwLjMwOTgwMzkzIDAuNTIxNTY4NjYAA - - - - {320, 480} - - - 1 - MCAwIDAAA - - - - - - YES - - - view - - - - 5 - - - - switchToTableView - - - 7 - - 7 - - - - switchToTableView - - - 8 - - 8 - - - - - YES - - 0 - - YES - - - - - - 1 - - - YES - - - - Molecule OpenGL View - - - -1 - - - RmlsZSdzIE93bmVyA - - - -2 - - - - - 6 - - - - - - - YES - - YES - -1.CustomClassName - -2.CustomClassName - 1.CustomClassName - 1.IBEditorWindowLastContentRect - 1.IBPluginDependency - 1.IBUserGuides - 6.IBPluginDependency - - - YES - SLSMoleculeGLViewController - UIResponder - SLSMoleculeGLView - {{576, 113}, {320, 480}} - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - YES - - - 2.400000e+02 - 1 - - - - 1.600000e+02 - 0 - - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - - YES - - YES - - - YES - - - - - YES - - YES - - - YES - - - - 22 - - - - YES - - SLSMoleculeGLView - UIView - - switchToTableView - id - - - IBProjectSource - SLSMoleculeGLView.h - - - - SLSMoleculeGLViewController - UIViewController - - IBProjectSource - SLSMoleculeGLViewController.h - - - - - 0 - ../Molecules.xcodeproj - 3 - - diff --git a/GLProgram.h b/GLProgram.h deleted file mode 100644 index b9ab6ae..0000000 --- a/GLProgram.h +++ /dev/null @@ -1,29 +0,0 @@ -// This is Jeff LaMarche's GLProgram OpenGL shader wrapper class from his OpenGL ES 2.0 book. -// A description of this can be found at his page on the topic: -// http://iphonedevelopment.blogspot.com/2010/11/opengl-es-20-for-ios-chapter-4.html - - -#import -#import -#import - -@interface GLProgram : NSObject -{ - NSMutableArray *attributes; - NSMutableArray *uniforms; - GLuint program, - vertShader, - fragShader; -} -- (id)initWithVertexShaderFilename:(NSString *)vShaderFilename - fragmentShaderFilename:(NSString *)fShaderFilename; -- (void)addAttribute:(NSString *)attributeName; -- (GLuint)attributeIndex:(NSString *)attributeName; -- (GLuint)uniformIndex:(NSString *)uniformName; -- (BOOL)link; -- (void)use; -- (NSString *)vertexShaderLog; -- (NSString *)fragmentShaderLog; -- (NSString *)programLog; -- (void)validate; -@end diff --git a/GLProgram.m b/GLProgram.m deleted file mode 100644 index 8b95364..0000000 --- a/GLProgram.m +++ /dev/null @@ -1,233 +0,0 @@ -// This is Jeff LaMarche's GLProgram OpenGL shader wrapper class from his OpenGL ES 2.0 book. -// A description of this can be found at his page on the topic: -// http://iphonedevelopment.blogspot.com/2010/11/opengl-es-20-for-ios-chapter-4.html - - -#import "GLProgram.h" -// START:typedefs -#pragma mark Function Pointer Definitions -typedef void (*GLInfoFunction)(GLuint program, - GLenum pname, - GLint* params); -typedef void (*GLLogFunction) (GLuint program, - GLsizei bufsize, - GLsizei* length, - GLchar* infolog); -// END:typedefs -#pragma mark - -#pragma mark Private Extension Method Declaration -// START:extension -@interface GLProgram() - -- (BOOL)compileShader:(GLuint *)shader - type:(GLenum)type - file:(NSString *)file; -- (NSString *)logForOpenGLObject:(GLuint)object - infoCallback:(GLInfoFunction)infoFunc - logFunc:(GLLogFunction)logFunc; -@end -// END:extension -#pragma mark - - -@implementation GLProgram -// START:init -- (id)initWithVertexShaderFilename:(NSString *)vShaderFilename - fragmentShaderFilename:(NSString *)fShaderFilename -{ - if ((self = [super init])) - { - attributes = [[NSMutableArray alloc] init]; - uniforms = [[NSMutableArray alloc] init]; - NSString *vertShaderPathname, *fragShaderPathname; - program = glCreateProgram(); - - vertShaderPathname = [[NSBundle mainBundle] - pathForResource:vShaderFilename - ofType:@"vsh"]; - - if (![self compileShader:&vertShader - type:GL_VERTEX_SHADER - file:vertShaderPathname]) - NSLog(@"Failed to compile vertex shader"); - - // Create and compile fragment shader - fragShaderPathname = [[NSBundle mainBundle] - pathForResource:fShaderFilename - ofType:@"fsh"]; - if (![self compileShader:&fragShader - type:GL_FRAGMENT_SHADER - file:fragShaderPathname]) - NSLog(@"Failed to compile fragment shader"); - - glAttachShader(program, vertShader); - glAttachShader(program, fragShader); - } - - return self; -} -// END:init -// START:compile -- (BOOL)compileShader:(GLuint *)shader - type:(GLenum)type - file:(NSString *)file -{ - GLint status; - const GLchar *source; - - source = - (GLchar *)[[NSString stringWithContentsOfFile:file - encoding:NSUTF8StringEncoding - error:nil] UTF8String]; - if (!source) - { - NSLog(@"Failed to load vertex shader"); - return NO; - } - - *shader = glCreateShader(type); - glShaderSource(*shader, 1, &source, NULL); - glCompileShader(*shader); - - glGetShaderiv(*shader, GL_COMPILE_STATUS, &status); - - if (status != GL_TRUE) - { - GLint logLength; - glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &logLength); - if (logLength > 0) - { - GLchar *log = (GLchar *)malloc(logLength); - glGetShaderInfoLog(*shader, logLength, &logLength, log); - NSLog(@"Shader compile log:\n%s", log); - free(log); - } - } - - return status == GL_TRUE; -} -// END:compile -#pragma mark - -// START:addattribute -- (void)addAttribute:(NSString *)attributeName -{ - if (![attributes containsObject:attributeName]) - { - [attributes addObject:attributeName]; - glBindAttribLocation(program, - (GLuint)[attributes indexOfObject:attributeName], - [attributeName UTF8String]); - } -} -// END:addattribute -// START:indexmethods -- (GLuint)attributeIndex:(NSString *)attributeName -{ - return (GLuint)[attributes indexOfObject:attributeName]; -} -- (GLuint)uniformIndex:(NSString *)uniformName -{ - return glGetUniformLocation(program, [uniformName UTF8String]); -} -// END:indexmethods -#pragma mark - -// START:link -- (BOOL)link -{ - GLint status; - - glLinkProgram(program); - glValidateProgram(program); - - glGetProgramiv(program, GL_LINK_STATUS, &status); - if (status == GL_FALSE) - return NO; - - if (vertShader) - glDeleteShader(vertShader); - if (fragShader) - glDeleteShader(fragShader); - - return YES; -} -// END:link -// START:use -- (void)use -{ - glUseProgram(program); -} -// END:use -#pragma mark - -// START:privatelog -- (NSString *)logForOpenGLObject:(GLuint)object - infoCallback:(GLInfoFunction)infoFunc - logFunc:(GLLogFunction)logFunc -{ - GLint logLength = 0, charsWritten = 0; - - infoFunc(object, GL_INFO_LOG_LENGTH, &logLength); - if (logLength < 1) - return nil; - - char *logBytes = malloc(logLength); - logFunc(object, logLength, &charsWritten, logBytes); - NSString *log = [[NSString alloc] initWithBytes:logBytes - length:logLength - encoding:NSUTF8StringEncoding]; - free(logBytes); - return log; -} -// END:privatelog -// START:log -- (NSString *)vertexShaderLog -{ - return [self logForOpenGLObject:vertShader - infoCallback:(GLInfoFunction)&glGetProgramiv - logFunc:(GLLogFunction)&glGetProgramInfoLog]; - -} -- (NSString *)fragmentShaderLog -{ - return [self logForOpenGLObject:fragShader - infoCallback:(GLInfoFunction)&glGetProgramiv - logFunc:(GLLogFunction)&glGetProgramInfoLog]; -} -- (NSString *)programLog -{ - return [self logForOpenGLObject:program - infoCallback:(GLInfoFunction)&glGetProgramiv - logFunc:(GLLogFunction)&glGetProgramInfoLog]; -} -// END:log - -- (void)validate; -{ - GLint logLength; - - glValidateProgram(program); - glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength); - if (logLength > 0) - { - GLchar *log = (GLchar *)malloc(logLength); - glGetProgramInfoLog(program, logLength, &logLength, log); - NSLog(@"Program validate log:\n%s", log); - free(log); - } -} - -#pragma mark - -// START:dealloc -- (void)dealloc -{ - - if (vertShader) - glDeleteShader(vertShader); - - if (fragShader) - glDeleteShader(fragShader); - - if (program) - glDeleteProgram(program); - -} -// END:dealloc -@end diff --git a/GlyphishIconLicense.txt b/GlyphishIconLicense.txt deleted file mode 100644 index 5fa3c99..0000000 --- a/GlyphishIconLicense.txt +++ /dev/null @@ -1,14 +0,0 @@ -Created by Joseph Wain (see http://penandthink.com) at and probably downloaded from http://glyphish.com - -This work is licensed under the Creative Commons Attribution 3.0 United States License. To view a copy of this license, visit http://creativecommons.org/licenses/by/3.0/us/ or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA. - -You are free to share it and to remix it remix under the following conditions: - -* You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work). -* For any reuse or distribution, you must make clear to others the license terms of this work. The best way to do this is with a link to http://creativecommons.org/licenses/by/3.0/us/ -* Any of the above conditions can be waived if you get permission from the copyright holder (send me an email!). -* Apart from the remix rights granted under this license, nothing in this license impairs or restricts the author's moral rights. - -ATTRIBUTION -- a note reading "icons by Joseph Wain / glyphish.com" or similar, plus a link back to glyphish.com from your app's website, is the preferred form of attribution. Also acceptable would be, like, a link from within your iPhone application, or from the iTunes store page, but those aren't as useful to other people. If none of these work for you, please contact hello@glyphish.com and we can work something out. - -USE WITHOUT ATTRIBUTION -- If attribution is not possible, workable or desirable for your application, contact hello@glyphish.com for commercial non-attributed licensing terms. \ No newline at end of file diff --git a/Info.plist b/Info.plist deleted file mode 100644 index e679a29..0000000 --- a/Info.plist +++ /dev/null @@ -1,32 +0,0 @@ - - - - - CFBundleDevelopmentRegion - en - CFBundleDisplayName - ${PRODUCT_NAME} - CFBundleExecutable - ${EXECUTABLE_NAME} - CFBundleIconFile - MoleculesIcon.png - CFBundleIdentifier - com.sunsetlakesoftware.Molecules - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - ${PRODUCT_NAME} - CFBundlePackageType - APPL - CFBundleSignature - ???? - CFBundleVersion - 1.2 - LSRequiresIPhoneOS - - NSMainNibFile - MainWindow - UIStatusBarStyle - UIStatusBarStyleBlackOpaque - - diff --git a/LaunchScreen.storyboard b/LaunchScreen.storyboard deleted file mode 100644 index 5afec28..0000000 --- a/LaunchScreen.storyboard +++ /dev/null @@ -1,31 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Molecules.xcodeproj/larson.mode2v3 b/Molecules.xcodeproj/larson.mode2v3 deleted file mode 100644 index 1ed53d4..0000000 --- a/Molecules.xcodeproj/larson.mode2v3 +++ /dev/null @@ -1,1551 +0,0 @@ - - - - - ActivePerspectiveName - Project - AllowedModules - - - BundleLoadPath - - MaxInstances - n - Module - PBXSmartGroupTreeModule - Name - Groups and Files Outline View - - - BundleLoadPath - - MaxInstances - n - Module - PBXNavigatorGroup - Name - Editor - - - BundleLoadPath - - MaxInstances - n - Module - XCTaskListModule - Name - Task List - - - BundleLoadPath - - MaxInstances - n - Module - XCDetailModule - Name - File and Smart Group Detail Viewer - - - BundleLoadPath - - MaxInstances - 1 - Module - PBXBuildResultsModule - Name - Detailed Build Results Viewer - - - BundleLoadPath - - MaxInstances - 1 - Module - PBXProjectFindModule - Name - Project Batch Find Tool - - - BundleLoadPath - - MaxInstances - n - Module - XCProjectFormatConflictsModule - Name - Project Format Conflicts List - - - BundleLoadPath - - MaxInstances - n - Module - PBXBookmarksModule - Name - Bookmarks Tool - - - BundleLoadPath - - MaxInstances - n - Module - PBXClassBrowserModule - Name - Class Browser - - - BundleLoadPath - - MaxInstances - n - Module - PBXCVSModule - Name - Source Code Control Tool - - - BundleLoadPath - - MaxInstances - n - Module - PBXDebugBreakpointsModule - Name - Debug Breakpoints Tool - - - BundleLoadPath - - MaxInstances - n - Module - XCDockableInspector - Name - Inspector - - - BundleLoadPath - - MaxInstances - n - Module - PBXOpenQuicklyModule - Name - Open Quickly Tool - - - BundleLoadPath - - MaxInstances - 1 - Module - PBXDebugSessionModule - Name - Debugger - - - BundleLoadPath - - MaxInstances - 1 - Module - PBXDebugCLIModule - Name - Debug Console - - - BundleLoadPath - - MaxInstances - n - Module - XCSnapshotModule - Name - Snapshots Tool - - - BundlePath - /Developer/Library/PrivateFrameworks/DevToolsInterface.framework/Resources - Description - CondensedDescriptionKey - DockingSystemVisible - - Extension - mode2v3 - FavBarConfig - - PBXProjectModuleGUID - BC896C1E0DE11BFD00B184F0 - XCBarModuleItemNames - - XCBarModuleItems - - - FirstTimeWindowDisplayed - - Identifier - com.apple.perspectives.project.mode2v3 - MajorVersion - 34 - MinorVersion - 0 - Name - Condensed - Notifications - - - XCObserverAutoDisconnectKey - - XCObserverDefintionKey - - PBXStatusErrorsKey - 0 - - XCObserverFactoryKey - XCPerspectivesSpecificationIdentifier - XCObserverGUIDKey - XCObserverProjectIdentifier - XCObserverNotificationKey - PBXStatusBuildStateMessageNotification - XCObserverTargetKey - XCMainBuildResultsModuleGUID - XCObserverTriggerKey - awakenModuleWithObserver: - XCObserverValidationKey - - PBXStatusErrorsKey - 2 - - - - OpenEditors - - PerspectiveWidths - - -1 - - Perspectives - - - ChosenToolbarItems - - active-combo-popup - debugger-enable-breakpoints - build-and-go - com.apple.ide.PBXToolbarStopButton - NSToolbarFlexibleSpaceItem - get-info - - ControllerClassBaseName - - IconName - WindowOfProject - Identifier - perspective.project - IsVertical - - Layout - - - Proportion - 625pt - Tabs - - - BecomeActive - - ContentConfiguration - - PBXBottomSmartGroupGIDs - - PBXProjectModuleGUID - 1C9437FD063B20B00039CFAC - PBXProjectModuleLabel - Files - PBXProjectStructureProvided - yes - PBXSmartGroupTreeModuleColumnData - - PBXSmartGroupTreeModuleColumnWidthsKey - - 22 - 480 - 20 - 43 - 43 - - PBXSmartGroupTreeModuleColumnsKey_v4 - - SCMStatusColumn - MainColumn - FileBuiltColumn - ErrorsColumn - WarningsColumn - - - PBXSmartGroupTreeModuleOutlineStateKey_v7 - - PBXSmartGroupTreeModuleOutlineStateExpansionKey - - 29B97314FDCFA39411CA2CEA - BCC7617C0E0EF8180050DD95 - BCC7617D0E0EF8260050DD95 - BCC7617B0E0EF7F10050DD95 - BCC8FE7C0EC7E44D00853857 - BCDC9F830EB375CC00ADCFA5 - BCC709540E19C6580054D0EB - BC5BB99E0E207ED10001DFD0 - 29B97315FDCFA39411CA2CEA - 29B97317FDCFA39411CA2CEA - BCDC9F860EB375FB00ADCFA5 - BCC246AB112E3DB500710932 - 29B97323FDCFA39411CA2CEA - 19C28FACFE9D520D11CA2CBB - - PBXSmartGroupTreeModuleOutlineStateSelectionKey - - - 0 - - - PBXSmartGroupTreeModuleOutlineStateVisibleRectKey - {{0, 0}, {608, 500}} - - PBXTopSmartGroupGIDs - - XCIncludePerspectivesSwitch - - - GeometryConfiguration - - Frame - {{10, 27}, {625, 518}} - GroupTreeTableConfiguration - - SCMStatusColumn - 22 - MainColumn - 480 - FileBuiltColumn - 20 - ErrorsColumn - 43 - WarningsColumn - 43 - - RubberWindowFrame - 1518 718 625 586 0 0 2560 1418 - - Module - PBXSmartGroupTreeModule - - - ContentConfiguration - - PBXBottomSmartGroupGIDs - - 1C37FBAC04509CD000000102 - 1C37FAAC04509CD000000102 - - PBXProjectModuleGUID - 1C9437FE063B20B00039CFAC - PBXProjectModuleLabel - Targets - PBXProjectStructureProvided - no - PBXSmartGroupTreeModuleColumnData - - PBXSmartGroupTreeModuleColumnWidthsKey - - 22 - 586 - - PBXSmartGroupTreeModuleColumnsKey_v4 - - SCMStatusColumn - MainColumn - - - PBXSmartGroupTreeModuleOutlineStateKey_v7 - - PBXSmartGroupTreeModuleOutlineStateExpansionKey - - 1C37FBAC04509CD000000102 - 1C37FAAC04509CD000000102 - - PBXSmartGroupTreeModuleOutlineStateSelectionKey - - - 1 - 0 - - - PBXSmartGroupTreeModuleOutlineStateVisibleRectKey - {{0, 0}, {608, 500}} - - PBXTopSmartGroupGIDs - - XCIncludePerspectivesSwitch - - - GeometryConfiguration - - Frame - {{10, 27}, {625, 518}} - GroupTreeTableConfiguration - - SCMStatusColumn - 22 - MainColumn - 586 - - - Module - PBXSmartGroupTreeModule - - - ContentConfiguration - - PBXBottomSmartGroupGIDs - - 1C37FABC05509CD000000102 - 1C37FABC05539CD112110102 - E2644B35053B69B200211256 - 1C37FABC04509CD000100104 - 1CC0EA4004350EF90044410B - 1CC0EA4004350EF90041110B - - PBXProjectModuleGUID - 1C9437FF063B20B00039CFAC - PBXProjectModuleLabel - Other - PBXProjectStructureProvided - no - PBXSmartGroupTreeModuleColumnData - - PBXSmartGroupTreeModuleColumnWidthsKey - - 22 - 586 - - PBXSmartGroupTreeModuleColumnsKey_v4 - - SCMStatusColumn - MainColumn - - - PBXSmartGroupTreeModuleOutlineStateKey_v7 - - PBXSmartGroupTreeModuleOutlineStateExpansionKey - - PBXSmartGroupTreeModuleOutlineStateSelectionKey - - - 0 - - - PBXSmartGroupTreeModuleOutlineStateVisibleRectKey - {{0, 0}, {608, 500}} - - PBXTopSmartGroupGIDs - - XCIncludePerspectivesSwitch - - - GeometryConfiguration - - Frame - {{10, 27}, {625, 518}} - GroupTreeTableConfiguration - - SCMStatusColumn - 22 - MainColumn - 586 - - - Module - PBXSmartGroupTreeModule - - - - - Name - Project - ServiceClasses - - XCModuleDock - XCDockableTabModule - PBXSmartGroupTreeModule - PBXSmartGroupTreeModule - PBXSmartGroupTreeModule - - TableOfContents - - BC1C385C12F773A200408939 - BC1C385D12F773A200408939 - 1C9437FD063B20B00039CFAC - 1C9437FE063B20B00039CFAC - 1C9437FF063B20B00039CFAC - - ToolbarConfigUserDefaultsMinorVersion - 2 - ToolbarConfiguration - xcode.toolbar.config.default.shortV3 - - - PerspectivesBarVisible - - ShelfIsVisible - - SourceDescription - file at '/Developer/Library/PrivateFrameworks/DevToolsInterface.framework/Resources/XCPerspectivesSpecificationMode2.xcperspec' - StatusbarIsVisible - - TimeStamp - 318208867.410182 - ToolbarDisplayMode - 2 - ToolbarIsVisible - - ToolbarSizeMode - 1 - Type - Perspectives - UpdateMessage - - WindowJustification - 0 - WindowOrderList - - BC1C385E12F773A200408939 - 1C530D57069F1CE1000CFCEE - 1C530D54069F1CE1000CFCEE - 1C530D52069F1CE1000CFCEE - /Users/larson/Development/Molecules/Molecules.xcodeproj - - WindowString - 1518 718 625 586 0 0 2560 1418 - WindowToolsV3 - - - FirstTimeWindowDisplayed - - Identifier - windowTool.detail - IsVertical - - Layout - - - Dock - - - ContentConfiguration - - PBXBottomSmartGroupGIDs - - 1C37FBAC04509CD000000102 - 1C37FAAC04509CD000000102 - 1C37FABC05509CD000000102 - 1C37FABC05539CD112110102 - E2644B35053B69B200211256 - 1C37FABC04509CD000100104 - 1CC0EA4004350EF90044410B - 1CC0EA4004350EF90041110B - - PBXProjectModuleGUID - 1CE0B1FE06471DED0097A5F4 - PBXProjectModuleLabel - Files - PBXProjectStructureProvided - yes - PBXSmartGroupTreeModuleColumnData - - PBXSmartGroupTreeModuleColumnWidthsKey - - 211 - - PBXSmartGroupTreeModuleColumnsKey_v4 - - MainColumn - - - PBXSmartGroupTreeModuleOutlineStateKey_v7 - - PBXSmartGroupTreeModuleOutlineStateExpansionKey - - 29B97314FDCFA39411CA2CEA - BCC7617C0E0EF8180050DD95 - 1C37FABC05509CD000000102 - - PBXSmartGroupTreeModuleOutlineStateSelectionKey - - - 20 - 18 - - - PBXSmartGroupTreeModuleOutlineStateVisibleRectKey - {{0, 76}, {211, 366}} - - PBXTopSmartGroupGIDs - - XCIncludePerspectivesSwitch - - - GeometryConfiguration - - Frame - {{0, 0}, {228, 384}} - GroupTreeTableConfiguration - - MainColumn - 211 - - RubberWindowFrame - 0 156 1227 425 0 0 1280 778 - - Module - PBXSmartGroupTreeModule - Proportion - 228pt - - - BecomeActive - - ContentConfiguration - - PBXProjectModuleGUID - 1CA1AED706398EBD00589147 - PBXProjectModuleLabel - Detail - - GeometryConfiguration - - Frame - {{233, 0}, {994, 384}} - RubberWindowFrame - 0 156 1227 425 0 0 1280 778 - - Module - XCDetailModule - Proportion - 994pt - - - Proportion - 384pt - - - Name - Detail - ServiceClasses - - PBXSmartGroupTreeModule - XCDetailModule - - StatusbarIsVisible - - TableOfContents - - 1C335F2C07B51CD20023D4EE - BCF104CA1002D6FB002CD789 - 1CE0B1FE06471DED0097A5F4 - 1CA1AED706398EBD00589147 - - ToolbarConfiguration - xcode.toolbar.config.defaultV3 - WindowString - 0 156 1227 425 0 0 1280 778 - WindowToolGUID - 1C335F2C07B51CD20023D4EE - WindowToolIsVisible - - - - Identifier - MENUSEPARATOR - - - FirstTimeWindowDisplayed - - Identifier - windowTool.build - IsVertical - - Layout - - - Dock - - - ContentConfiguration - - PBXProjectModuleGUID - 1CD0528F0623707200166675 - PBXProjectModuleLabel - - StatusBarVisibility - - - GeometryConfiguration - - Frame - {{0, 0}, {1011, 203}} - RubberWindowFrame - 743 126 1011 722 0 0 2560 1418 - - Module - PBXNavigatorGroup - Proportion - 203pt - - - ContentConfiguration - - PBXProjectModuleGUID - XCMainBuildResultsModuleGUID - PBXProjectModuleLabel - Build Results - XCBuildResultsTrigger_Collapse - 1024 - XCBuildResultsTrigger_Open - 1012 - - GeometryConfiguration - - Frame - {{0, 208}, {1011, 473}} - RubberWindowFrame - 743 126 1011 722 0 0 2560 1418 - - Module - PBXBuildResultsModule - Proportion - 473pt - - - Proportion - 681pt - - - Name - Build Results - ServiceClasses - - PBXBuildResultsModule - - StatusbarIsVisible - - TableOfContents - - 1C530D52069F1CE1000CFCEE - BC1C386112F773A200408939 - 1CD0528F0623707200166675 - XCMainBuildResultsModuleGUID - - ToolbarConfiguration - xcode.toolbar.config.buildV3 - WindowString - 743 126 1011 722 0 0 2560 1418 - WindowToolGUID - 1C530D52069F1CE1000CFCEE - WindowToolIsVisible - - - - FirstTimeWindowDisplayed - - Identifier - windowTool.debugger - IsVertical - - Layout - - - Dock - - - ContentConfiguration - - Debugger - - HorizontalSplitView - - _collapsingFrameDimension - 0.0 - _indexOfCollapsedView - 0 - _percentageOfCollapsedView - 0.0 - isCollapsed - yes - sizes - - {{0, 0}, {606, 555}} - {{0, 555}, {606, 526}} - - - VerticalSplitView - - _collapsingFrameDimension - 0.0 - _indexOfCollapsedView - 0 - _percentageOfCollapsedView - 0.0 - isCollapsed - yes - sizes - - {{0, 0}, {606, 1081}} - {{606, 0}, {836, 1081}} - - - - LauncherConfigVersion - 8 - PBXProjectModuleGUID - 1C162984064C10D400B95A72 - PBXProjectModuleLabel - Debug - GLUTExamples (Underwater) - - GeometryConfiguration - - DebugConsoleVisible - None - DebugConsoleWindowFrame - {{200, 200}, {500, 300}} - DebugSTDIOWindowFrame - {{200, 200}, {500, 300}} - Frame - {{0, 0}, {1442, 1081}} - PBXDebugSessionStackFrameViewKey - - DebugVariablesTableConfiguration - - Name - 240 - Value - 85 - Summary - 256 - - Frame - {{0, 555}, {606, 526}} - RubberWindowFrame - 1065 296 1442 1122 0 0 2560 1418 - - RubberWindowFrame - 1065 296 1442 1122 0 0 2560 1418 - - Module - PBXDebugSessionModule - Proportion - 1081pt - - - Proportion - 1081pt - - - Name - Debugger - ServiceClasses - - PBXDebugSessionModule - - StatusbarIsVisible - - TableOfContents - - 1C530D54069F1CE1000CFCEE - BC1C386212F773A200408939 - 1C162984064C10D400B95A72 - BC1C386312F773A200408939 - BC1C386412F773A200408939 - BC1C386512F773A200408939 - BC1C386612F773A200408939 - BC1C386712F773A200408939 - - ToolbarConfiguration - xcode.toolbar.config.debugV3 - WindowString - 1065 296 1442 1122 0 0 2560 1418 - WindowToolGUID - 1C530D54069F1CE1000CFCEE - WindowToolIsVisible - - - - FirstTimeWindowDisplayed - - Identifier - windowTool.find - IsVertical - - Layout - - - Dock - - - Dock - - - ContentConfiguration - - PBXProjectModuleGUID - 1CDD528C0622207200134675 - PBXProjectModuleLabel - <No Editor> - StatusBarVisibility - - - GeometryConfiguration - - Frame - {{0, 0}, {1511, 533}} - RubberWindowFrame - 820 260 1511 1028 0 0 2560 1418 - - Module - PBXNavigatorGroup - Proportion - 1511pt - - - Proportion - 533pt - - - BecomeActive - - ContentConfiguration - - PBXProjectModuleGUID - 1CD0528E0623707200166675 - PBXProjectModuleLabel - Project Find - - GeometryConfiguration - - Frame - {{0, 538}, {1511, 449}} - RubberWindowFrame - 820 260 1511 1028 0 0 2560 1418 - - Module - PBXProjectFindModule - Proportion - 449pt - - - Proportion - 987pt - - - Name - Project Find - ServiceClasses - - PBXProjectFindModule - - StatusbarIsVisible - - TableOfContents - - 1C530D57069F1CE1000CFCEE - BC1C386812F773A200408939 - BC1C386912F773A200408939 - 1CDD528C0622207200134675 - 1CD0528E0623707200166675 - - WindowString - 820 260 1511 1028 0 0 2560 1418 - WindowToolGUID - 1C530D57069F1CE1000CFCEE - WindowToolIsVisible - - - - Identifier - MENUSEPARATOR - - - FirstTimeWindowDisplayed - - Identifier - windowTool.debuggerConsole - IsVertical - - Layout - - - Dock - - - ContentConfiguration - - PBXProjectModuleGUID - 1C78EAAC065D492600B07095 - PBXProjectModuleLabel - Debugger Console - - GeometryConfiguration - - Frame - {{0, 0}, {1211, 393}} - RubberWindowFrame - 198 87 1211 434 0 0 2560 1418 - - Module - PBXDebugCLIModule - Proportion - 393pt - - - Proportion - 393pt - - - Name - Debugger Console - ServiceClasses - - PBXDebugCLIModule - - StatusbarIsVisible - - TableOfContents - - 1C530D5B069F1CE1000CFCEE - BC192CDA12E0F090005ACE06 - 1C78EAAC065D492600B07095 - - ToolbarConfiguration - xcode.toolbar.config.consoleV3 - WindowString - 198 87 1211 434 0 0 2560 1418 - WindowToolGUID - 1C530D5B069F1CE1000CFCEE - WindowToolIsVisible - - - - Identifier - windowTool.snapshots - Layout - - - Dock - - - Module - XCSnapshotModule - Proportion - 100% - - - Proportion - 100% - - - Name - Snapshots - ServiceClasses - - XCSnapshotModule - - StatusbarIsVisible - Yes - ToolbarConfiguration - xcode.toolbar.config.snapshots - WindowString - 315 824 300 550 0 0 1440 878 - WindowToolIsVisible - Yes - - - FirstTimeWindowDisplayed - - Identifier - windowTool.scm - IsVertical - - Layout - - - Dock - - - ContentConfiguration - - PBXProjectModuleGUID - 1C78EAB2065D492600B07095 - PBXProjectModuleLabel - - StatusBarVisibility - - - GeometryConfiguration - - Frame - {{0, 0}, {452, 0}} - RubberWindowFrame - 812 670 452 308 0 0 1920 1178 - - Module - PBXNavigatorGroup - Proportion - 0pt - - - BecomeActive - - ContentConfiguration - - PBXCVSModuleFilterTypeKey - 1030 - PBXCVSModuleTreeModuleColumnData - - PBXCVSModuleTreeModuleColumnWidthsKey - - 200 - 56 - 63 - 60 - 63 - 139 - - PBXCVSModuleTreeModuleColumnsKey - - Name - Status - Update - Revision - Author - Date - - - PBXProjectModuleGUID - 1CD052920623707200166675 - PBXProjectModuleLabel - SCM Results - - GeometryConfiguration - - Frame - {{0, 5}, {452, 262}} - RubberWindowFrame - 812 670 452 308 0 0 1920 1178 - - Module - PBXCVSModule - Proportion - 262pt - - - Proportion - 267pt - - - Name - SCM - ServiceClasses - - PBXCVSModule - - StatusbarIsVisible - - TableOfContents - - BC754EA30E0EFC0B00A9B89B - BC12E8060F0BC76800A50697 - 1C78EAB2065D492600B07095 - 1CD052920623707200166675 - - ToolbarConfiguration - xcode.toolbar.config.scm - WindowString - 812 670 452 308 0 0 1920 1178 - WindowToolGUID - BC754EA30E0EFC0B00A9B89B - WindowToolIsVisible - - - - FirstTimeWindowDisplayed - - Identifier - windowTool.breakpoints - IsVertical - - Layout - - - Dock - - - BecomeActive - - ContentConfiguration - - PBXBottomSmartGroupGIDs - - 1C77FABC04509CD000000102 - - PBXProjectModuleGUID - 1CE0B1FE06471DED0097A5F4 - PBXProjectModuleLabel - Files - PBXProjectStructureProvided - no - PBXSmartGroupTreeModuleColumnData - - PBXSmartGroupTreeModuleColumnWidthsKey - - 168 - - PBXSmartGroupTreeModuleColumnsKey_v4 - - MainColumn - - - PBXSmartGroupTreeModuleOutlineStateKey_v7 - - PBXSmartGroupTreeModuleOutlineStateExpansionKey - - 1C77FABC04509CD000000102 - 1C3E0DCA080725EA00A55177 - 1C3E0DCC080725EA11A45113 - - PBXSmartGroupTreeModuleOutlineStateSelectionKey - - - 5 - 0 - - - PBXSmartGroupTreeModuleOutlineStateVisibleRectKey - {{0, 0}, {168, 350}} - - PBXTopSmartGroupGIDs - - XCIncludePerspectivesSwitch - - - GeometryConfiguration - - Frame - {{0, 0}, {185, 368}} - GroupTreeTableConfiguration - - MainColumn - 168 - - RubberWindowFrame - 248 154 744 409 0 0 1920 1178 - - Module - PBXSmartGroupTreeModule - Proportion - 185pt - - - ContentConfiguration - - PBXProjectModuleGUID - 1CA1AED706398EBD00589147 - PBXProjectModuleLabel - Detail - - GeometryConfiguration - - Frame - {{190, 0}, {554, 368}} - RubberWindowFrame - 248 154 744 409 0 0 1920 1178 - - Module - XCDetailModule - Proportion - 554pt - - - Proportion - 368pt - - - MajorVersion - 3 - MinorVersion - 0 - Name - Breakpoints - ServiceClasses - - PBXSmartGroupTreeModule - XCDetailModule - - StatusbarIsVisible - - TableOfContents - - BCE401D010AE1A4700206909 - BCE401D110AE1A4700206909 - 1CE0B1FE06471DED0097A5F4 - 1CA1AED706398EBD00589147 - - ToolbarConfiguration - xcode.toolbar.config.breakpointsV3 - WindowString - 248 154 744 409 0 0 1920 1178 - WindowToolGUID - BCE401D010AE1A4700206909 - WindowToolIsVisible - - - - Identifier - windowTool.debugAnimator - Layout - - - Dock - - - Module - PBXNavigatorGroup - Proportion - 100% - - - Proportion - 100% - - - Name - Debug Visualizer - ServiceClasses - - PBXNavigatorGroup - - StatusbarIsVisible - 1 - ToolbarConfiguration - xcode.toolbar.config.debugAnimatorV3 - WindowString - 100 100 700 500 0 0 1280 1002 - - - Identifier - windowTool.bookmarks - Layout - - - Dock - - - Module - PBXBookmarksModule - Proportion - 100% - - - Proportion - 100% - - - Name - Bookmarks - ServiceClasses - - PBXBookmarksModule - - StatusbarIsVisible - 0 - WindowString - 538 42 401 187 0 0 1280 1002 - - - Identifier - windowTool.projectFormatConflicts - Layout - - - Dock - - - Module - XCProjectFormatConflictsModule - Proportion - 100% - - - Proportion - 100% - - - Name - Project Format Conflicts - ServiceClasses - - XCProjectFormatConflictsModule - - StatusbarIsVisible - 0 - WindowContentMinSize - 450 300 - WindowString - 50 850 472 307 0 0 1440 877 - - - Identifier - windowTool.classBrowser - Layout - - - Dock - - - BecomeActive - 1 - ContentConfiguration - - OptionsSetName - Hierarchy, all classes - PBXProjectModuleGUID - 1CA6456E063B45B4001379D8 - PBXProjectModuleLabel - Class Browser - NSObject - - GeometryConfiguration - - ClassesFrame - {{0, 0}, {368, 96}} - ClassesTreeTableConfiguration - - PBXClassNameColumnIdentifier - 208 - PBXClassBookColumnIdentifier - 22 - - Frame - {{0, 0}, {624, 318}} - MembersFrame - {{0, 105}, {368, 395}} - MembersTreeTableConfiguration - - PBXMemberTypeIconColumnIdentifier - 22 - PBXMemberNameColumnIdentifier - 216 - PBXMemberTypeColumnIdentifier - 91 - PBXMemberBookColumnIdentifier - 22 - - PBXModuleWindowStatusBarHidden2 - 1 - RubberWindowFrame - 128 171 624 339 0 0 1440 878 - - Module - PBXClassBrowserModule - Proportion - 319pt - - - Proportion - 319pt - - - Name - Class Browser - ServiceClasses - - PBXClassBrowserModule - - StatusbarIsVisible - 0 - TableOfContents - - 1C530D60069F1CE1000CFCEE - 1C530D61069F1CE1000CFCEE - 1CA6456E063B45B4001379D8 - - ToolbarConfiguration - xcode.toolbar.config.classbrowser - WindowString - 128 171 624 339 0 0 1440 878 - WindowToolGUID - 1C530D60069F1CE1000CFCEE - WindowToolIsVisible - 0 - - - FirstTimeWindowDisplayed - - Identifier - windowTool.refactoring - IncludeInToolsMenu - 0 - IsVertical - - Layout - - - Dock - - - ContentConfiguration - - PBXProjectModuleGUID - BCF4954D1040BA7400BE82D5 - - GeometryConfiguration - - Frame - {{0, 0}, {500, 315}} - RubberWindowFrame - 93 399 500 356 0 0 1280 778 - - Module - XCRefactoringModule - Proportion - 315pt - - - Proportion - 315pt - - - Name - Refactoring - ServiceClasses - - XCRefactoringModule - - StatusbarIsVisible - - TableOfContents - - BCF4954E1040BA7400BE82D5 - BCDFF68011556A0B005C984C - BCF4954D1040BA7400BE82D5 - - WindowString - 93 399 500 356 0 0 1280 778 - WindowToolGUID - BCF4954E1040BA7400BE82D5 - WindowToolIsVisible - - - - - diff --git a/Molecules.xcodeproj/larson.pbxuser b/Molecules.xcodeproj/larson.pbxuser deleted file mode 100644 index ea595d2..0000000 --- a/Molecules.xcodeproj/larson.pbxuser +++ /dev/null @@ -1,548 +0,0 @@ -// !$*UTF8*$! -{ - 1D6058900D05DD3D006BFB54 /* Molecules */ = { - activeExec = 0; - executables = ( - BC896C170DE11BD500B184F0 /* Molecules */, - ); - }; - 29B97313FDCFA39411CA2CEA /* Project object */ = { - activeBuildConfigurationName = Release; - activeExecutable = BC896C170DE11BD500B184F0 /* Molecules */; - activeSDKPreference = iphonesimulator4.2; - activeTarget = 1D6058900D05DD3D006BFB54 /* Molecules */; - addToTargets = ( - 1D6058900D05DD3D006BFB54 /* Molecules */, - ); - breakpoints = ( - ); - codeSenseManager = BC896C200DE11BFE00B184F0 /* Code sense */; - executables = ( - BC896C170DE11BD500B184F0 /* Molecules */, - ); - perUserDictionary = { - "PBXConfiguration.PBXBreakpointsDataSource.v1:1CA1AED706398EBD00589147" = { - PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; - PBXFileTableDataSourceColumnSortingKey = PBXBreakpointsDataSource_BreakpointID; - PBXFileTableDataSourceColumnWidthsKey = ( - 20, - 20, - 198, - 20, - 99, - 99, - 29, - 20, - ); - PBXFileTableDataSourceColumnsKey = ( - PBXBreakpointsDataSource_ActionID, - PBXBreakpointsDataSource_TypeID, - PBXBreakpointsDataSource_BreakpointID, - PBXBreakpointsDataSource_UseID, - PBXBreakpointsDataSource_LocationID, - PBXBreakpointsDataSource_ConditionID, - PBXBreakpointsDataSource_IgnoreCountID, - PBXBreakpointsDataSource_ContinueID, - ); - }; - PBXConfiguration.PBXFileTableDataSource3.PBXBookmarksDataSource = { - PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; - PBXFileTableDataSourceColumnSortingKey = PBXBookmarksDataSource_NameID; - PBXFileTableDataSourceColumnWidthsKey = ( - 200, - 200, - 565, - ); - PBXFileTableDataSourceColumnsKey = ( - PBXBookmarksDataSource_LocationID, - PBXBookmarksDataSource_NameID, - PBXBookmarksDataSource_CommentsID, - ); - }; - PBXConfiguration.PBXFileTableDataSource3.PBXErrorsWarningsDataSource = { - PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; - PBXFileTableDataSourceColumnSortingKey = PBXErrorsWarningsDataSource_LocationID; - PBXFileTableDataSourceColumnWidthsKey = ( - 20, - 300, - 498, - ); - PBXFileTableDataSourceColumnsKey = ( - PBXErrorsWarningsDataSource_TypeID, - PBXErrorsWarningsDataSource_MessageID, - PBXErrorsWarningsDataSource_LocationID, - ); - }; - PBXConfiguration.PBXFileTableDataSource3.PBXFileTableDataSource = { - PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; - PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID; - PBXFileTableDataSourceColumnWidthsKey = ( - 20, - 315, - 20, - 48, - 43, - 43, - 20, - ); - PBXFileTableDataSourceColumnsKey = ( - PBXFileDataSource_FiletypeID, - PBXFileDataSource_Filename_ColumnID, - PBXFileDataSource_Built_ColumnID, - PBXFileDataSource_ObjectSize_ColumnID, - PBXFileDataSource_Errors_ColumnID, - PBXFileDataSource_Warnings_ColumnID, - PBXFileDataSource_Target_ColumnID, - ); - }; - PBXConfiguration.PBXFileTableDataSource3.PBXFindDataSource = { - PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; - PBXFileTableDataSourceColumnSortingKey = PBXFindDataSource_LocationID; - PBXFileTableDataSourceColumnWidthsKey = ( - 797, - 172, - ); - PBXFileTableDataSourceColumnsKey = ( - PBXFindDataSource_MessageID, - PBXFindDataSource_LocationID, - ); - }; - PBXConfiguration.PBXFileTableDataSource3.XCSCMDataSource = { - PBXFileTableDataSourceColumnSortingDirectionKey = "-1"; - PBXFileTableDataSourceColumnSortingKey = PBXFileDataSource_Filename_ColumnID; - PBXFileTableDataSourceColumnWidthsKey = ( - 20, - 20, - 880, - 20, - 48, - 43, - 43, - 20, - ); - PBXFileTableDataSourceColumnsKey = ( - PBXFileDataSource_SCM_ColumnID, - PBXFileDataSource_FiletypeID, - PBXFileDataSource_Filename_ColumnID, - PBXFileDataSource_Built_ColumnID, - PBXFileDataSource_ObjectSize_ColumnID, - PBXFileDataSource_Errors_ColumnID, - PBXFileDataSource_Warnings_ColumnID, - PBXFileDataSource_Target_ColumnID, - ); - }; - PBXPerProjectTemplateStateSaveDate = 318206866; - PBXWorkspaceStateSaveDate = 318206866; - }; - sourceControlManager = BC896C1F0DE11BFE00B184F0 /* Source Control */; - userBookmarkGroup = BCB290D0103CE78B00F0EA2A /* PBXBookmarkGroup */; - userBuildSettings = { - }; - }; - 29B97316FDCFA39411CA2CEA /* main.m */ = { - uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {950, 288}}"; - sepNavSelRange = "{0, 0}"; - sepNavVisRange = "{0, 234}"; - sepNavWindowFrame = "{{15, 0}, {881, 778}}"; - }; - }; - 8D1107310486CEB800E47090 /* English */ = { - uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {734, 1485}}"; - sepNavSelRange = "{1046, 40}"; - sepNavVisRect = "{{0, 0}, {700, 706}}"; - sepNavWindowFrame = "{{963, 169}, {745, 778}}"; - }; - }; - BC146A990EC9173000CE5E72 /* SLSMoleculeCustomDownloadViewController.h */ = { - uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {898, 272}}"; - sepNavSelRange = "{235, 0}"; - sepNavVisRange = "{66, 349}"; - sepNavWindowFrame = "{{115, 0}, {881, 778}}"; - }; - }; - BC146A9A0EC9173000CE5E72 /* SLSMoleculeCustomDownloadViewController.m */ = { - uiCtxt = { - sepNavFolds = "{\n c = (\n {\n r = \"{803, 43}\";\n s = 0;\n },\n {\n r = \"{3237, 1}\";\n s = 0;\n }\n );\n r = \"{0, 5841}\";\n s = 0;\n}"; - sepNavIntBoundsRect = "{{0, 0}, {1181, 3056}}"; - sepNavSelRange = "{1100, 31}"; - sepNavVisRange = "{807, 532}"; - sepNavWindowFrame = "{{346, 0}, {881, 778}}"; - }; - }; - BC1EF3810E36BD6100B09043 /* SLSMoleculeSearchViewController.h */ = { - uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {928, 706}}"; - sepNavSelRange = "{487, 0}"; - sepNavVisRange = "{0, 943}"; - sepNavWindowFrame = "{{7, 0}, {987, 778}}"; - }; - }; - BC1EF3820E36BD6100B09043 /* SLSMoleculeSearchViewController.m */ = { - uiCtxt = { - sepNavFolds = "{\n c = (\n {\n r = \"{1756, 191}\";\n s = 0;\n },\n {\n r = \"{3174, 1846}\";\n s = 0;\n },\n {\n r = \"{5060, 771}\";\n s = 0;\n },\n {\n r = \"{5961, 12}\";\n s = 0;\n },\n {\n r = \"{6067, 333}\";\n s = 0;\n },\n {\n r = \"{6499, 15}\";\n s = 0;\n },\n {\n r = \"{6622, 3500}\";\n s = 0;\n },\n {\n r = \"{11106, 1}\";\n s = 0;\n },\n {\n r = \"{11206, 75}\";\n s = 0;\n },\n {\n r = \"{11561, 138}\";\n s = 0;\n },\n {\n r = \"{11849, 732}\";\n s = 0;\n },\n {\n r = \"{12665, 384}\";\n s = 0;\n },\n {\n r = \"{13148, 61}\";\n s = 0;\n },\n {\n r = \"{13279, 237}\";\n s = 0;\n }\n );\n r = \"{0, 13565}\";\n s = 0;\n}"; - sepNavIntBoundsRect = "{{0, 0}, {1181, 2848}}"; - sepNavSelRange = "{0, 0}"; - sepNavVisRange = "{0, 326}"; - sepNavWindowFrame = "{{7, 0}, {987, 778}}"; - }; - }; - BC2CF8630E5784EC00653418 /* SLSMolecule+PDB.h */ = { - uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {1096, 706}}"; - sepNavSelRange = "{616, 5}"; - sepNavVisRange = "{0, 621}"; - sepNavWindowFrame = "{{67, 0}, {1155, 778}}"; - }; - }; - BC2CF8640E5784ED00653418 /* SLSMolecule+PDB.m */ = { - uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {1727, 16000}}"; - sepNavSelRange = "{48233, 0}"; - sepNavVisRange = "{46166, 3284}"; - sepNavWindowFrame = "{{483, 55}, {1335, 1326}}"; - }; - }; - BC37E7C80E14779E00E11F4F /* SLSMolecule.h */ = { - uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {1450, 1952}}"; - sepNavSelRange = "{4191, 0}"; - sepNavVisRange = "{3242, 1917}"; - sepNavWindowFrame = "{{193, 0}, {1155, 778}}"; - }; - }; - BC37E7C90E14779E00E11F4F /* SLSMolecule.m */ = { - uiCtxt = { - sepNavFolds = "{\n c = (\n {\n r = \"{2213, 105}\";\n s = 0;\n },\n {\n r = \"{2395, 852}\";\n s = 0;\n },\n {\n r = \"{5069, 2986}\";\n s = 0;\n },\n {\n r = \"{8083, 737}\";\n s = 0;\n },\n {\n r = \"{8841, 764}\";\n s = 0;\n },\n {\n r = \"{9666, 356}\";\n s = 0;\n },\n {\n r = \"{10173, 2943}\";\n s = 0;\n },\n {\n r = \"{13160, 380}\";\n s = 0;\n },\n {\n r = \"{13584, 757}\";\n s = 0;\n },\n {\n r = \"{14384, 80}\";\n s = 0;\n },\n {\n r = \"{14506, 69}\";\n s = 0;\n },\n {\n r = \"{14662, 2921}\";\n s = 0;\n },\n {\n r = \"{17748, 2828}\";\n s = 0;\n },\n {\n r = \"{20684, 305}\";\n s = 0;\n },\n {\n r = \"{21049, 300}\";\n s = 0;\n },\n {\n r = \"{21381, 1435}\";\n s = 0;\n },\n {\n r = \"{25281, 1094}\";\n s = 0;\n },\n {\n r = \"{26543, 1559}\";\n s = 0;\n },\n {\n r = \"{28371, 2103}\";\n s = 0;\n },\n {\n r = \"{30523, 2171}\";\n s = 0;\n },\n {\n r = \"{32726, 1990}\";\n s = 0;\n },\n {\n r = \"{34748, 2746}\";\n s = 0;\n },\n {\n r = \"{37538, 2958}\";\n s = 0;\n },\n {\n r = \"{40586, 103}\";\n s = 0;\n },\n {\n r = \"{40724, 200}\";\n s = 0;\n },\n {\n r = \"{40957, 101}\";\n s = 0;\n },\n {\n r = \"{41140, 331}\";\n s = 0;\n },\n {\n r = \"{41499, 1454}\";\n s = 0;\n },\n {\n r = \"{42995, 1698}\";\n s = 0;\n },\n {\n r = \"{44719, 689}\";\n s = 0;\n },\n {\n r = \"{45439, 635}\";\n s = 0;\n }\n );\n r = \"{0, 47426}\";\n s = 0;\n}"; - sepNavIntBoundsRect = "{{0, 0}, {2231, 4416}}"; - sepNavSelRange = "{5732, 6}"; - sepNavVisRange = "{4859, 2531}"; - sepNavWindowFrame = "{{623, 274}, {1155, 778}}"; - }; - }; - BC4FB6650E4E875900B316CD /* VCTitleCase.h */ = { - uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {822, 703}}"; - sepNavSelRange = "{203, 18}"; - sepNavVisRange = "{0, 824}"; - sepNavWindowFrame = "{{16, 0}, {881, 775}}"; - }; - }; - BC4FB6660E4E875900B316CD /* VCTitleCase.m */ = { - uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {822, 2880}}"; - sepNavSelRange = "{3048, 0}"; - sepNavVisRange = "{1949, 2137}"; - sepNavWindowFrame = "{{312, 3}, {881, 775}}"; - }; - }; - BC4FB75C0E4F6BAF00B316CD /* greenButton.png */ = { - uiCtxt = { - sepNavWindowFrame = "{{15, -5}, {1069, 1178}}"; - }; - }; - BC4FB7B90E4F80DB00B316CD /* redButton.png */ = { - uiCtxt = { - sepNavWindowFrame = "{{15, -5}, {1069, 1178}}"; - }; - }; - BC540D0F1145E04500045DE1 /* MoleculesIcon72.png */ = { - uiCtxt = { - sepNavWindowFrame = "{{15, 0}, {885, 778}}"; - }; - }; - BC5BB8BB0E204DC20001DFD0 /* SLSMoleculeDetailViewController.h */ = { - uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {761, 706}}"; - sepNavSelRange = "{202, 108}"; - sepNavVisRange = "{0, 741}"; - sepNavWindowFrame = "{{15, -5}, {765, 778}}"; - }; - }; - BC5BB8BC0E204DC20001DFD0 /* SLSMoleculeDetailViewController.m */ = { - uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {999, 5872}}"; - sepNavSelRange = "{3857, 6}"; - sepNavVisRange = "{3595, 687}"; - sepNavWindowFrame = "{{229, 1}, {998, 777}}"; - }; - }; - BC5BBCD30E219AB90001DFD0 /* MoleculesIcon.png */ = { - uiCtxt = { - sepNavWindowFrame = "{{15, 0}, {885, 778}}"; - }; - }; - BC61CC2D0E2463180074BE04 /* SLSCellTextView.h */ = { - uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {939, 705}}"; - sepNavSelRange = "{412, 15}"; - sepNavVisRange = "{0, 527}"; - sepNavWindowFrame = "{{15, 0}, {998, 777}}"; - }; - }; - BC61CC2E0E2463180074BE04 /* SLSCellTextView.m */ = { - uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {939, 960}}"; - sepNavSelRange = "{563, 0}"; - sepNavVisRange = "{421, 865}"; - sepNavWindowFrame = "{{15, 0}, {998, 777}}"; - }; - }; - BC61CC2F0E2463180074BE04 /* SLSTextViewController.h */ = { - uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {939, 705}}"; - sepNavSelRange = "{339, 21}"; - sepNavVisRange = "{0, 493}"; - sepNavWindowFrame = "{{15, 0}, {998, 777}}"; - }; - }; - BC61CC300E2463180074BE04 /* SLSTextViewController.m */ = { - uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {939, 1776}}"; - sepNavSelRange = "{1072, 15}"; - sepNavVisRange = "{586, 1169}"; - sepNavWindowFrame = "{{15, 0}, {998, 777}}"; - }; - }; - BC6A9FCB11CAA8CB00E62D55 /* MoleculesIcon@2x.png */ = { - uiCtxt = { - sepNavWindowFrame = "{{61, 193}, {846, 1178}}"; - }; - }; - BC896C170DE11BD500B184F0 /* Molecules */ = { - isa = PBXExecutable; - activeArgIndices = ( - ); - argumentStrings = ( - ); - autoAttachOnCrash = 1; - breakpointsEnabled = 0; - configStateDict = { - }; - customDataFormattersEnabled = 1; - dataTipCustomDataFormattersEnabled = 1; - dataTipShowTypeColumn = 1; - dataTipSortType = 0; - debuggerPlugin = GDBDebugging; - disassemblyDisplayState = 0; - dylibVariantSuffix = ""; - enableDebugStr = 1; - environmentEntries = ( - ); - executableSystemSymbolLevel = 0; - executableUserSymbolLevel = 0; - libgmallocEnabled = 0; - name = Molecules; - savedGlobals = { - }; - showTypeColumn = 0; - sourceDirectories = ( - ); - variableFormatDictionary = { - }; - }; - BC896C1F0DE11BFE00B184F0 /* Source Control */ = { - isa = PBXSourceControlManager; - fallbackIsa = XCSourceControlManager; - isSCMEnabled = 0; - scmConfiguration = { - repositoryName = Molecules; - repositoryNamesForRoots = { - "" = Molecules; - }; - }; - }; - BC896C200DE11BFE00B184F0 /* Code sense */ = { - isa = PBXCodeSenseManager; - indexTemplatePath = ""; - }; - BCB290D0103CE78B00F0EA2A /* PBXBookmarkGroup */ = { - isa = PBXBookmarkGroup; - children = ( - BCB290E0103CF7B200F0EA2A /* PBXTextBookmark */, - ); - name = Root; - }; - BCB290E0103CF7B200F0EA2A /* PBXTextBookmark */ = { - isa = PBXTextBookmark; - fRef = BC37E7C90E14779E00E11F4F /* SLSMolecule.m */; - rLen = 20; - rLoc = 45692; - rType = 0; - }; - BCC281280E1B1FCD00978ECA /* NSData+Gzip.h */ = { - uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {894, 234}}"; - sepNavSelRange = "{421, 0}"; - sepNavVisRange = "{0, 0}"; - sepNavWindowFrame = "{{231, 0}, {998, 777}}"; - }; - }; - BCC281290E1B1FCD00978ECA /* NSData+Gzip.m */ = { - uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {894, 1776}}"; - sepNavSelRange = "{435, 0}"; - sepNavVisRange = "{1769, 981}"; - sepNavWindowFrame = "{{161, 0}, {891, 778}}"; - }; - }; - BCC286BD0E1D8F9300978ECA /* SLSMoleculeAppDelegate.h */ = { - uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {924, 1024}}"; - sepNavSelRange = "{883, 0}"; - sepNavVisRange = "{191, 1289}"; - sepNavWindowFrame = "{{217, 0}, {983, 778}}"; - }; - }; - BCC286BE0E1D8F9300978ECA /* SLSMoleculeAppDelegate.m */ = { - uiCtxt = { - sepNavFolds = "{\n c = (\n {\n r = \"{3295, 439}\";\n s = 0;\n },\n {\n r = \"{3826, 468}\";\n s = 0;\n },\n {\n r = \"{12980, 167}\";\n s = 0;\n },\n {\n r = \"{13182, 166}\";\n s = 0;\n },\n {\n r = \"{13383, 3}\";\n s = 0;\n },\n {\n r = \"{13419, 94}\";\n s = 0;\n },\n {\n r = \"{17473, 303}\";\n s = 0;\n },\n {\n r = \"{20582, 419}\";\n s = 0;\n },\n {\n r = \"{21085, 285}\";\n s = 0;\n },\n {\n r = \"{21469, 1147}\";\n s = 0;\n },\n {\n r = \"{22686, 444}\";\n s = 0;\n }\n );\n r = \"{0, 23138}\";\n s = 0;\n}"; - sepNavIntBoundsRect = "{{0, 0}, {1132, 8656}}"; - sepNavSelRange = "{8925, 18}"; - sepNavVisRange = "{7865, 2188}"; - sepNavWindowFrame = "{{909, 99}, {865, 1102}}"; - }; - }; - BCC286BF0E1D8F9300978ECA /* SLSMoleculeRootViewController.h */ = { - uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {1096, 912}}"; - sepNavSelRange = "{1551, 36}"; - sepNavVisRange = "{390, 1326}"; - sepNavWindowFrame = "{{545, 244}, {1155, 778}}"; - }; - }; - BCC286C00E1D8F9300978ECA /* SLSMoleculeRootViewController.m */ = { - uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {1096, 5440}}"; - sepNavSelRange = "{7635, 0}"; - sepNavVisRange = "{6969, 1482}"; - sepNavWindowFrame = "{{9, 0}, {1155, 778}}"; - }; - }; - BCC286C30E1D8FB100978ECA /* SLSMoleculeGLView.h */ = { - uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {944, 956}}"; - sepNavSelRange = "{462, 0}"; - sepNavVisRange = "{0, 1466}"; - sepNavWindowFrame = "{{616, 0}, {1003, 1028}}"; - }; - }; - BCC286C40E1D8FB100978ECA /* SLSMoleculeGLView.m */ = { - uiCtxt = { - sepNavFolds = "{\n c = (\n {\n r = \"{686, 30}\";\n s = 0;\n },\n {\n r = \"{1900, 649}\";\n s = 0;\n },\n {\n r = \"{3836, 314}\";\n s = 0;\n },\n {\n r = \"{4417, 340}\";\n s = 0;\n }\n );\n r = \"{0, 7125}\";\n s = 0;\n}"; - sepNavIntBoundsRect = "{{0, 0}, {1209, 3216}}"; - sepNavSelRange = "{4726, 0}"; - sepNavVisRange = "{3765, 2025}"; - sepNavWindowFrame = "{{616, 0}, {1003, 1028}}"; - }; - }; - BCC286C60E1D8FB100978ECA /* SLSMoleculeGLViewController.h */ = { - uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {1286, 1392}}"; - sepNavSelRange = "{940, 0}"; - sepNavVisRange = "{287, 2363}"; - sepNavWindowFrame = "{{321, 390}, {938, 1028}}"; - }; - }; - BCC286C70E1D8FB100978ECA /* SLSMoleculeGLViewController.m */ = { - uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {1692, 13184}}"; - sepNavSelRange = "{7600, 13}"; - sepNavVisRange = "{7128, 954}"; - sepNavWindowFrame = "{{929, 111}, {938, 1028}}"; - }; - }; - BCC286CC0E1D905400978ECA /* SLSMoleculeDownloadViewController.h */ = { - uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {1114, 864}}"; - sepNavSelRange = "{1086, 0}"; - sepNavVisRange = "{60, 1920}"; - sepNavWindowFrame = "{{3, 0}, {1173, 777}}"; - }; - }; - BCC286CD0E1D905400978ECA /* SLSMoleculeDownloadViewController.m */ = { - uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {2161, 6256}}"; - sepNavSelRange = "{13901, 0}"; - sepNavVisRange = "{0, 1180}"; - sepNavWindowFrame = "{{54, 1}, {1173, 777}}"; - }; - }; - BCC286CF0E1D905400978ECA /* SLSMoleculeTableViewController.h */ = { - uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {803, 752}}"; - sepNavSelRange = "{1146, 13}"; - sepNavVisRange = "{0, 1414}"; - sepNavWindowFrame = "{{372, 0}, {727, 778}}"; - }; - }; - BCC286D00E1D905400978ECA /* SLSMoleculeTableViewController.m */ = { - uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {1853, 4496}}"; - sepNavSelRange = "{1154, 5}"; - sepNavVisRange = "{529, 1342}"; - sepNavWindowFrame = "{{1550, 240}, {846, 1178}}"; - }; - }; - BCC8FE7E0EC7E4CA00853857 /* SLSMoleculeDataSourceViewController.h */ = { - uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {822, 706}}"; - sepNavSelRange = "{317, 0}"; - sepNavVisRange = "{0, 399}"; - sepNavWindowFrame = "{{15, 0}, {881, 778}}"; - }; - }; - BCC8FE7F0EC7E4CA00853857 /* SLSMoleculeDataSourceViewController.m */ = { - uiCtxt = { - sepNavFolds = "{\n c = (\n {\n r = \"{932, 22}\";\n s = 0;\n },\n {\n r = \"{1187, 15}\";\n s = 0;\n },\n {\n r = \"{1297, 15}\";\n s = 0;\n },\n {\n r = \"{1421, 775}\";\n s = 0;\n },\n {\n r = \"{3186, 1}\";\n s = 0;\n }\n );\n r = \"{0, 3235}\";\n s = 0;\n}"; - sepNavIntBoundsRect = "{{0, 0}, {1132, 1584}}"; - sepNavSelRange = "{2404, 0}"; - sepNavVisRange = "{1371, 1041}"; - sepNavWindowFrame = "{{346, 0}, {881, 778}}"; - }; - }; - BCD12CF711308E1000737166 /* SLSMoleculeiPadRootViewController.h */ = { - uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {1168, 688}}"; - sepNavSelRange = "{386, 28}"; - sepNavVisRange = "{0, 1073}"; - sepNavWindowFrame = "{{0, 1}, {1227, 760}}"; - }; - }; - BCD12CF811308E1000737166 /* SLSMoleculeiPadRootViewController.m */ = { - uiCtxt = { - sepNavFolds = "{\n c = (\n {\n r = \"{4808, 60}\";\n s = 0;\n },\n {\n r = \"{4905, 164}\";\n s = 0;\n },\n {\n r = \"{5096, 116}\";\n s = 0;\n },\n {\n r = \"{5233, 204}\";\n s = 0;\n },\n {\n r = \"{5990, 488}\";\n s = 0;\n },\n {\n r = \"{6522, 1141}\";\n s = 0;\n },\n {\n r = \"{7795, 195}\";\n s = 0;\n },\n {\n r = \"{8115, 343}\";\n s = 0;\n },\n {\n r = \"{8574, 249}\";\n s = 0;\n },\n {\n r = \"{8888, 378}\";\n s = 0;\n },\n {\n r = \"{11408, 331}\";\n s = 0;\n },\n {\n r = \"{12860, 214}\";\n s = 0;\n }\n );\n r = \"{0, 13122}\";\n s = 0;\n}"; - sepNavIntBoundsRect = "{{0, 0}, {1370, 3744}}"; - sepNavSelRange = "{4075, 0}"; - sepNavVisRange = "{1541, 2782}"; - sepNavWindowFrame = "{{235, 127}, {1227, 760}}"; - }; - }; - BCDC9F870EB3760300ADCFA5 /* French */ = { - uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {956, 1515}}"; - sepNavSelRange = "{508, 0}"; - sepNavVisRect = "{{0, 28}, {956, 706}}"; - sepNavWindowFrame = "{{312, 0}, {1001, 778}}"; - }; - }; - BCDC9F9A0EB37B1E00ADCFA5 /* English */ = { - uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {1000, 945}}"; - sepNavSelRange = "{2667, 4}"; - sepNavVisRange = "{2365, 537}"; - sepNavVisRect = "{{0, 239}, {700, 706}}"; - sepNavWindowFrame = "{{15, 0}, {745, 778}}"; - }; - }; - BCDC9F9E0EB37B4100ADCFA5 /* French */ = { - uiCtxt = { - sepNavIntBoundsRect = "{{0, 0}, {1013, 910}}"; - sepNavSelRange = "{302, 0}"; - sepNavVisRange = "{0, 2992}"; - sepNavWindowFrame = "{{61, 353}, {881, 778}}"; - }; - }; -} diff --git a/Molecules.xcodeproj/project.pbxproj b/Molecules.xcodeproj/project.pbxproj old mode 100755 new mode 100644 index eae166c..86af458 --- a/Molecules.xcodeproj/project.pbxproj +++ b/Molecules.xcodeproj/project.pbxproj @@ -3,804 +3,805 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 56; objects = { /* Begin PBXBuildFile section */ - 1D3623EC0D0F72F000981E51 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D3623EB0D0F72F000981E51 /* CoreGraphics.framework */; }; - 1D60589B0D05DD56006BFB54 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; }; - 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D30AB110D05D00D00671497 /* Foundation.framework */; }; - 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */; settings = {ATTRIBUTES = (Weak, ); }; }; - 28FD15000DC6FC520079059D /* OpenGLES.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28FD14FF0DC6FC520079059D /* OpenGLES.framework */; }; - 28FD15080DC6FC5B0079059D /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28FD15070DC6FC5B0079059D /* QuartzCore.framework */; }; - BC1175B220F1514C00CCEAC9 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BC1175B120F1514C00CCEAC9 /* LaunchScreen.storyboard */; }; - BC146A9B0EC9173000CE5E72 /* SLSMoleculeCustomDownloadViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BC146A9A0EC9173000CE5E72 /* SLSMoleculeCustomDownloadViewController.m */; }; - BC157287136CBE9200EEFBEC /* 3QE5.pdb.gz in Resources */ = {isa = PBXBuildFile; fileRef = BC157286136CBE9200EEFBEC /* 3QE5.pdb.gz */; }; - BC15728A136CC58A00EEFBEC /* SLSMoleculeLibraryTableCell.m in Sources */ = {isa = PBXBuildFile; fileRef = BC157289136CC58A00EEFBEC /* SLSMoleculeLibraryTableCell.m */; }; - BC15728D136CD96800EEFBEC /* SLSMoleculeWebDetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BC15728C136CD96800EEFBEC /* SLSMoleculeWebDetailViewController.m */; }; - BC1EF3830E36BD6100B09043 /* SLSMoleculeSearchViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BC1EF3820E36BD6100B09043 /* SLSMoleculeSearchViewController.m */; }; - BC2795BA1162500A006DE4C8 /* Default-Landscape.png in Resources */ = {isa = PBXBuildFile; fileRef = BC2795B81162500A006DE4C8 /* Default-Landscape.png */; }; - BC2795BB1162500A006DE4C8 /* Default-Portrait.png in Resources */ = {isa = PBXBuildFile; fileRef = BC2795B91162500A006DE4C8 /* Default-Portrait.png */; }; - BC2795BD11625065006DE4C8 /* Default.png in Resources */ = {isa = PBXBuildFile; fileRef = BC2795BC11625065006DE4C8 /* Default.png */; }; - BC281A6B11727A89005B7946 /* Document-molecules-64.png in Resources */ = {isa = PBXBuildFile; fileRef = BC281A6911727A89005B7946 /* Document-molecules-64.png */; }; - BC281A6C11727A89005B7946 /* Document-molecules-320.png in Resources */ = {isa = PBXBuildFile; fileRef = BC281A6A11727A89005B7946 /* Document-molecules-320.png */; }; - BC286E1011C4751E005D720C /* RotationIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = BC286E0F11C4751E005D720C /* RotationIcon@2x.png */; }; - BC286E2611C47956005D720C /* RotationIconSelected@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = BC286E2511C47956005D720C /* RotationIconSelected@2x.png */; }; - BC2CF8650E5784ED00653418 /* SLSMolecule+PDB.m in Sources */ = {isa = PBXBuildFile; fileRef = BC2CF8640E5784ED00653418 /* SLSMolecule+PDB.m */; }; - BC33740C1370C6830036428C /* SLSMolecule+SDF.m in Sources */ = {isa = PBXBuildFile; fileRef = BC33740B1370C6830036428C /* SLSMolecule+SDF.m */; }; - BC37E7CA0E14779E00E11F4F /* SLSMolecule.m in Sources */ = {isa = PBXBuildFile; fileRef = BC37E7C90E14779E00E11F4F /* SLSMolecule.m */; }; - BC4E97C20E69F736006CD291 /* libsqlite3.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = BC4E97C10E69F736006CD291 /* libsqlite3.dylib */; }; - BC4E97FA0E69F8E1006CD291 /* molecules.sql in Resources */ = {isa = PBXBuildFile; fileRef = BC4E97F90E69F8E1006CD291 /* molecules.sql */; }; - BC4FB6670E4E875900B316CD /* VCTitleCase.m in Sources */ = {isa = PBXBuildFile; fileRef = BC4FB6660E4E875900B316CD /* VCTitleCase.m */; }; - BC4FB75D0E4F6BB000B316CD /* greenButton.png in Resources */ = {isa = PBXBuildFile; fileRef = BC4FB75C0E4F6BAF00B316CD /* greenButton.png */; }; - BC4FB7BA0E4F80DB00B316CD /* redButton.png in Resources */ = {isa = PBXBuildFile; fileRef = BC4FB7B90E4F80DB00B316CD /* redButton.png */; }; - BC540D101145E04600045DE1 /* MoleculesIcon72.png in Resources */ = {isa = PBXBuildFile; fileRef = BC540D0F1145E04500045DE1 /* MoleculesIcon72.png */; }; - BC54D56E151A3951003F4A41 /* VisualizationIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = BC54D56D151A3951003F4A41 /* VisualizationIcon@2x.png */; }; - BC54D571151A3C2E003F4A41 /* RotationIconiPad@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = BC54D56F151A3C2E003F4A41 /* RotationIconiPad@2x.png */; }; - BC54D572151A3C2E003F4A41 /* RotationIconiPadCancel@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = BC54D570151A3C2E003F4A41 /* RotationIconiPadCancel@2x.png */; }; - BC54D574151A4953003F4A41 /* MoleculesIcon72@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = BC54D573151A4953003F4A41 /* MoleculesIcon72@2x.png */; }; - BC54D577151A4C91003F4A41 /* Default-Landscape@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = BC54D575151A4C91003F4A41 /* Default-Landscape@2x.png */; }; - BC54D578151A4C91003F4A41 /* Default-Portrait@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = BC54D576151A4C91003F4A41 /* Default-Portrait@2x.png */; }; - BC57EBFB1358DE9000FA1060 /* GLProgram.m in Sources */ = {isa = PBXBuildFile; fileRef = BC57EBFA1358DE9000FA1060 /* GLProgram.m */; }; - BC5BB79C0E200C3B0001DFD0 /* 1BNA.pdb.gz in Resources */ = {isa = PBXBuildFile; fileRef = BC5BB79B0E200C3B0001DFD0 /* 1BNA.pdb.gz */; }; - BC5BB7DF0E2020B10001DFD0 /* 1EVE.pdb.gz in Resources */ = {isa = PBXBuildFile; fileRef = BC5BB7DD0E2020B10001DFD0 /* 1EVE.pdb.gz */; }; - BC5BB8BD0E204DC20001DFD0 /* SLSMoleculeDetailViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BC5BB8BC0E204DC20001DFD0 /* SLSMoleculeDetailViewController.m */; }; - BC5BBBFA0E216A170001DFD0 /* 1TRZ.pdb.gz in Resources */ = {isa = PBXBuildFile; fileRef = BC5BBBF90E216A170001DFD0 /* 1TRZ.pdb.gz */; }; - BC5BBCD40E219AB90001DFD0 /* MoleculesIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = BC5BBCD30E219AB90001DFD0 /* MoleculesIcon.png */; }; - BC61CAFC0E2317EE0074BE04 /* 4TRA.pdb.gz in Resources */ = {isa = PBXBuildFile; fileRef = BC61CAFB0E2317EE0074BE04 /* 4TRA.pdb.gz */; }; - BC61CC310E2463180074BE04 /* SLSCellTextView.m in Sources */ = {isa = PBXBuildFile; fileRef = BC61CC2E0E2463180074BE04 /* SLSCellTextView.m */; }; - BC61CC320E2463180074BE04 /* SLSTextViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BC61CC300E2463180074BE04 /* SLSTextViewController.m */; }; - BC67B8BC137B5DF10075A361 /* SphereAOLookup.fsh in Resources */ = {isa = PBXBuildFile; fileRef = BC67B8BA137B5DF10075A361 /* SphereAOLookup.fsh */; }; - BC67B8BD137B5DF10075A361 /* SphereAOLookup.vsh in Resources */ = {isa = PBXBuildFile; fileRef = BC67B8BB137B5DF10075A361 /* SphereAOLookup.vsh */; }; - BC67B8BF137C4B7A0075A361 /* Default@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = BC67B8BE137C4B7A0075A361 /* Default@2x.png */; }; - BC6A9FCC11CAA8CB00E62D55 /* MoleculesIcon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = BC6A9FCB11CAA8CB00E62D55 /* MoleculesIcon@2x.png */; }; - BC835184117545C600B478D5 /* 69-display.png in Resources */ = {isa = PBXBuildFile; fileRef = BC835183117545C600B478D5 /* 69-display.png */; }; - BCA2E7E2135A1B9B00D7206C /* CylinderDepth.fsh in Resources */ = {isa = PBXBuildFile; fileRef = BCA2E7DA135A1B9B00D7206C /* CylinderDepth.fsh */; }; - BCA2E7E3135A1B9B00D7206C /* CylinderDepth.vsh in Resources */ = {isa = PBXBuildFile; fileRef = BCA2E7DB135A1B9B00D7206C /* CylinderDepth.vsh */; }; - BCA2E7E4135A1B9B00D7206C /* CylinderRaytracing.fsh in Resources */ = {isa = PBXBuildFile; fileRef = BCA2E7DC135A1B9B00D7206C /* CylinderRaytracing.fsh */; }; - BCA2E7E5135A1B9B00D7206C /* CylinderRaytracing.vsh in Resources */ = {isa = PBXBuildFile; fileRef = BCA2E7DD135A1B9B00D7206C /* CylinderRaytracing.vsh */; }; - BCA2E7E6135A1B9B00D7206C /* SphereDepth.fsh in Resources */ = {isa = PBXBuildFile; fileRef = BCA2E7DE135A1B9B00D7206C /* SphereDepth.fsh */; }; - BCA2E7E7135A1B9B00D7206C /* SphereDepth.vsh in Resources */ = {isa = PBXBuildFile; fileRef = BCA2E7DF135A1B9B00D7206C /* SphereDepth.vsh */; }; - BCA2E7E8135A1B9B00D7206C /* SphereRaytracing.fsh in Resources */ = {isa = PBXBuildFile; fileRef = BCA2E7E0135A1B9B00D7206C /* SphereRaytracing.fsh */; }; - BCA2E7E9135A1B9B00D7206C /* SphereRaytracing.vsh in Resources */ = {isa = PBXBuildFile; fileRef = BCA2E7E1135A1B9B00D7206C /* SphereRaytracing.vsh */; }; - BCA2E811135BB90F00D7206C /* caffeine.pdb.gz in Resources */ = {isa = PBXBuildFile; fileRef = BCA2E810135BB90F00D7206C /* caffeine.pdb.gz */; }; - BCA2E814135BBA3000D7206C /* SphereAmbientOcclusion.fsh in Resources */ = {isa = PBXBuildFile; fileRef = BCA2E812135BBA3000D7206C /* SphereAmbientOcclusion.fsh */; }; - BCA2E815135BBA3000D7206C /* SphereAmbientOcclusion.vsh in Resources */ = {isa = PBXBuildFile; fileRef = BCA2E813135BBA3000D7206C /* SphereAmbientOcclusion.vsh */; }; - BCA2E81E135E54C500D7206C /* CylinderAmbientOcclusion.fsh in Resources */ = {isa = PBXBuildFile; fileRef = BCA2E81C135E54C400D7206C /* CylinderAmbientOcclusion.fsh */; }; - BCA2E81F135E54C500D7206C /* CylinderAmbientOcclusion.vsh in Resources */ = {isa = PBXBuildFile; fileRef = BCA2E81D135E54C400D7206C /* CylinderAmbientOcclusion.vsh */; }; - BCB3A67A13553F9D000A286B /* SLSOpenGLESRenderer.m in Sources */ = {isa = PBXBuildFile; fileRef = BCB3A67913553F9D000A286B /* SLSOpenGLESRenderer.m */; }; - BCB3A67D13553FC1000A286B /* SLSOpenGLES11Renderer.m in Sources */ = {isa = PBXBuildFile; fileRef = BCB3A67C13553FC1000A286B /* SLSOpenGLES11Renderer.m */; }; - BCB3A68013553FD2000A286B /* SLSOpenGLES20Renderer.m in Sources */ = {isa = PBXBuildFile; fileRef = BCB3A67F13553FD2000A286B /* SLSOpenGLES20Renderer.m */; }; - BCC2812A0E1B1FCD00978ECA /* NSData+Gzip.m in Sources */ = {isa = PBXBuildFile; fileRef = BCC281290E1B1FCD00978ECA /* NSData+Gzip.m */; }; - BCC286C10E1D8F9300978ECA /* SLSMoleculeAppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = BCC286BE0E1D8F9300978ECA /* SLSMoleculeAppDelegate.m */; }; - BCC286C20E1D8F9300978ECA /* SLSMoleculeRootViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BCC286C00E1D8F9300978ECA /* SLSMoleculeRootViewController.m */; }; - BCC286C80E1D8FB100978ECA /* SLSMoleculeGLView.m in Sources */ = {isa = PBXBuildFile; fileRef = BCC286C40E1D8FB100978ECA /* SLSMoleculeGLView.m */; }; - BCC286CA0E1D8FB100978ECA /* SLSMoleculeGLViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BCC286C70E1D8FB100978ECA /* SLSMoleculeGLViewController.m */; }; - BCC286D20E1D905400978ECA /* SLSMoleculeDownloadController.m in Sources */ = {isa = PBXBuildFile; fileRef = BCC286CD0E1D905400978ECA /* SLSMoleculeDownloadController.m */; }; - BCC286D40E1D905400978ECA /* SLSMoleculeTableViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BCC286D00E1D905400978ECA /* SLSMoleculeTableViewController.m */; }; - BCC8FE800EC7E4CA00853857 /* SLSMoleculeDataSourceViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BCC8FE7F0EC7E4CA00853857 /* SLSMoleculeDataSourceViewController.m */; }; - BCCB1EFB1361E6BC006F00A0 /* PlainDisplay.fsh in Resources */ = {isa = PBXBuildFile; fileRef = BCCB1EF91361E6BC006F00A0 /* PlainDisplay.fsh */; }; - BCCB1EFC1361E6BC006F00A0 /* PlainDisplay.vsh in Resources */ = {isa = PBXBuildFile; fileRef = BCCB1EFA1361E6BC006F00A0 /* PlainDisplay.vsh */; }; - BCD12CFA11308E1000737166 /* SLSMoleculeiPadRootViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD12CF811308E1000737166 /* SLSMoleculeiPadRootViewController.m */; }; - BCD301631391D0C000C26201 /* SphereDepthWrite.fsh in Resources */ = {isa = PBXBuildFile; fileRef = BCD301611391D0C000C26201 /* SphereDepthWrite.fsh */; }; - BCD301641391D0C000C26201 /* SphereDepthWrite.vsh in Resources */ = {isa = PBXBuildFile; fileRef = BCD301621391D0C000C26201 /* SphereDepthWrite.vsh */; }; - BCD40722151D0CFA00E622EA /* SLSAtomColorKeyController.m in Sources */ = {isa = PBXBuildFile; fileRef = BCD40721151D0CFA00E622EA /* SLSAtomColorKeyController.m */; }; - BCD4072F151D702600E622EA /* 98-palette.png in Resources */ = {isa = PBXBuildFile; fileRef = BCD4072D151D702600E622EA /* 98-palette.png */; }; - BCD40730151D702600E622EA /* 98-palette@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = BCD4072E151D702600E622EA /* 98-palette@2x.png */; }; - BCDC9F9D0EB37B3400ADCFA5 /* Localized.strings in Resources */ = {isa = PBXBuildFile; fileRef = BCDC9F9C0EB37B3400ADCFA5 /* Localized.strings */; }; - BCDFF560115469C1005C984C /* RotationIconiPad.png in Resources */ = {isa = PBXBuildFile; fileRef = BCDFF55F115469BF005C984C /* RotationIconiPad.png */; }; - BCDFF57311546FB2005C984C /* RotationIconiPadCancel.png in Resources */ = {isa = PBXBuildFile; fileRef = BCDFF57211546FB2005C984C /* RotationIconiPadCancel.png */; }; - BCDFF580115473AD005C984C /* VisualizationIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = BCDFF57F115473AD005C984C /* VisualizationIcon.png */; }; - BCDFF5AE1154819A005C984C /* 57-download.png in Resources */ = {isa = PBXBuildFile; fileRef = BCDFF5AD1154819A005C984C /* 57-download.png */; }; - BCF495221040945400BE82D5 /* RotationIcon.png in Resources */ = {isa = PBXBuildFile; fileRef = BCF495211040945400BE82D5 /* RotationIcon.png */; }; - BCF496CC1040FB9D00BE82D5 /* RotationIconSelected.png in Resources */ = {isa = PBXBuildFile; fileRef = BCF496CB1040FB9D00BE82D5 /* RotationIconSelected.png */; }; + BC5807052A56395400313289 /* MoleculesApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5807042A56395400313289 /* MoleculesApp.swift */; }; + BC5807072A56395400313289 /* MoleculeDisplayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5807062A56395400313289 /* MoleculeDisplayView.swift */; }; + BC5807092A56395900313289 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BC5807082A56395900313289 /* Assets.xcassets */; }; + BC58070C2A56395900313289 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BC58070B2A56395900313289 /* Preview Assets.xcassets */; }; + BC5807162A56395900313289 /* PDBFileTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5807152A56395900313289 /* PDBFileTests.swift */; }; + BC5807202A56395900313289 /* MoleculesUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC58071F2A56395900313289 /* MoleculesUITests.swift */; }; + BC5807222A56395900313289 /* MoleculesUITestsLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5807212A56395900313289 /* MoleculesUITestsLaunchTests.swift */; }; + BC5F1F712A579BB000416000 /* Gzip in Frameworks */ = {isa = PBXBuildFile; productRef = BC5F1F702A579BB000416000 /* Gzip */; }; + BC5F1F742A579BEE00416000 /* PDBFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1F732A579BEE00416000 /* PDBFile.swift */; }; + BC5F1F772A58D40500416000 /* MolecularStructure.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1F762A58D40500416000 /* MolecularStructure.swift */; }; + BC5F1F7A2A58EB5500416000 /* Atom.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1F792A58EB5500416000 /* Atom.swift */; }; + BC5F1F7D2A58EB6300416000 /* Bond.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1F7C2A58EB6300416000 /* Bond.swift */; }; + BC5F1F802A58EC5F00416000 /* Coordinate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1F7F2A58EC5F00416000 /* Coordinate.swift */; }; + BC5F1F8D2A59DAC000416000 /* TheoreticalBearing.pdb in Resources */ = {isa = PBXBuildFile; fileRef = BC5F1F822A59DABF00416000 /* TheoreticalBearing.pdb */; }; + BC5F1F8E2A59DAC000416000 /* TheoreticalBearing.pdb in Resources */ = {isa = PBXBuildFile; fileRef = BC5F1F822A59DABF00416000 /* TheoreticalBearing.pdb */; }; + BC5F1F952A59DAC000416000 /* Heme.sdf in Resources */ = {isa = PBXBuildFile; fileRef = BC5F1F862A59DAC000416000 /* Heme.sdf */; }; + BC5F1F962A59DAC000416000 /* Heme.sdf in Resources */ = {isa = PBXBuildFile; fileRef = BC5F1F862A59DAC000416000 /* Heme.sdf */; }; + BC5F1F9B2A59DAC000416000 /* Nanotube.pdb in Resources */ = {isa = PBXBuildFile; fileRef = BC5F1F892A59DAC000416000 /* Nanotube.pdb */; }; + BC5F1F9C2A59DAC000416000 /* Nanotube.pdb in Resources */ = {isa = PBXBuildFile; fileRef = BC5F1F892A59DAC000416000 /* Nanotube.pdb */; }; + BC5F1FA42A59E96E00416000 /* MoleculeDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1FA32A59E96E00416000 /* MoleculeDocument.swift */; }; + BC5F1FB12A5A0D4300416000 /* DNA.pdb in Resources */ = {isa = PBXBuildFile; fileRef = BC5F1FA82A5A0D4300416000 /* DNA.pdb */; }; + BC5F1FB22A5A0D4300416000 /* DNA.pdb in Resources */ = {isa = PBXBuildFile; fileRef = BC5F1FA82A5A0D4300416000 /* DNA.pdb */; }; + BC5F1FB32A5A0D4300416000 /* Insulin.pdb in Resources */ = {isa = PBXBuildFile; fileRef = BC5F1FA92A5A0D4300416000 /* Insulin.pdb */; }; + BC5F1FB42A5A0D4300416000 /* Insulin.pdb in Resources */ = {isa = PBXBuildFile; fileRef = BC5F1FA92A5A0D4300416000 /* Insulin.pdb */; }; + BC5F1FB52A5A0D4300416000 /* Buckminsterfullerene.sdf in Resources */ = {isa = PBXBuildFile; fileRef = BC5F1FAA2A5A0D4300416000 /* Buckminsterfullerene.sdf */; }; + BC5F1FB62A5A0D4300416000 /* Buckminsterfullerene.sdf in Resources */ = {isa = PBXBuildFile; fileRef = BC5F1FAA2A5A0D4300416000 /* Buckminsterfullerene.sdf */; }; + BC5F1FB72A5A0D4300416000 /* Caffeine.pdb in Resources */ = {isa = PBXBuildFile; fileRef = BC5F1FAB2A5A0D4300416000 /* Caffeine.pdb */; }; + BC5F1FB82A5A0D4300416000 /* Caffeine.pdb in Resources */ = {isa = PBXBuildFile; fileRef = BC5F1FAB2A5A0D4300416000 /* Caffeine.pdb */; }; + BC5F1FB92A5A0D4300416000 /* TransferRNA.pdb in Resources */ = {isa = PBXBuildFile; fileRef = BC5F1FAC2A5A0D4300416000 /* TransferRNA.pdb */; }; + BC5F1FBA2A5A0D4300416000 /* TransferRNA.pdb in Resources */ = {isa = PBXBuildFile; fileRef = BC5F1FAC2A5A0D4300416000 /* TransferRNA.pdb */; }; + BC5F1FCC2A5A29D400416000 /* MetalRenderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1FCB2A5A29D400416000 /* MetalRenderView.swift */; }; + BC5F1FCD2A5A29D400416000 /* MetalRenderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1FCB2A5A29D400416000 /* MetalRenderView.swift */; }; + BC5F1FCF2A5A34FD00416000 /* MoleculeMetadataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1FCE2A5A34FD00416000 /* MoleculeMetadataView.swift */; }; + BC5F1FD02A5A34FD00416000 /* MoleculeMetadataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1FCE2A5A34FD00416000 /* MoleculeMetadataView.swift */; }; + BC5F1FD22A5A4B5300416000 /* SDFFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1FD12A5A4B5300416000 /* SDFFile.swift */; }; + BC5F1FD52A5A4C5800416000 /* XYZFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1FD42A5A4C5800416000 /* XYZFile.swift */; }; + BC5F1FD82A5A4CAB00416000 /* SDFFileTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1FD72A5A4CAB00416000 /* SDFFileTests.swift */; }; + BC5F1FDA2A5A4EA900416000 /* XYZFileTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1FD92A5A4EA900416000 /* XYZFileTests.swift */; }; + BC5F1FDF2A5B097E00416000 /* Acetylcholinesterase.pdb.gz in Resources */ = {isa = PBXBuildFile; fileRef = BC5F1FDC2A5B097E00416000 /* Acetylcholinesterase.pdb.gz */; }; + BC5F1FE02A5B097E00416000 /* Acetylcholinesterase.pdb.gz in Resources */ = {isa = PBXBuildFile; fileRef = BC5F1FDC2A5B097E00416000 /* Acetylcholinesterase.pdb.gz */; }; + BC5F1FE12A5B097E00416000 /* 3QE5.pdb.gz in Resources */ = {isa = PBXBuildFile; fileRef = BC5F1FDD2A5B097E00416000 /* 3QE5.pdb.gz */; }; + BC5F1FE22A5B097E00416000 /* 3QE5.pdb.gz in Resources */ = {isa = PBXBuildFile; fileRef = BC5F1FDD2A5B097E00416000 /* 3QE5.pdb.gz */; }; + BC5F1FE32A5B097E00416000 /* TheoreticalXenonPump.pdb.gz in Resources */ = {isa = PBXBuildFile; fileRef = BC5F1FDE2A5B097E00416000 /* TheoreticalXenonPump.pdb.gz */; }; + BC5F1FE42A5B097E00416000 /* TheoreticalXenonPump.pdb.gz in Resources */ = {isa = PBXBuildFile; fileRef = BC5F1FDE2A5B097E00416000 /* TheoreticalXenonPump.pdb.gz */; }; + BC5F1FE92A5B099400416000 /* TransferRNA.pdb.gz in Resources */ = {isa = PBXBuildFile; fileRef = BC5F1FE52A5B099400416000 /* TransferRNA.pdb.gz */; }; + BC5F1FEA2A5B099400416000 /* SampleMolecule.xyz in Resources */ = {isa = PBXBuildFile; fileRef = BC5F1FE62A5B099400416000 /* SampleMolecule.xyz */; }; + BC5F1FEB2A5B099400416000 /* Caffeine.pdb.gz in Resources */ = {isa = PBXBuildFile; fileRef = BC5F1FE72A5B099400416000 /* Caffeine.pdb.gz */; }; + BC5F1FEC2A5B099400416000 /* DNA.pdb.gz in Resources */ = {isa = PBXBuildFile; fileRef = BC5F1FE82A5B099400416000 /* DNA.pdb.gz */; }; + BC5F1FEF2A5B51AF00416000 /* SphereRaytracing.metal in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1FEE2A5B51AF00416000 /* SphereRaytracing.metal */; }; + BC5F1FF12A5B63B800416000 /* MetalRenderingDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1FF02A5B63B800416000 /* MetalRenderingDevice.swift */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + BC5807122A56395900313289 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BC5806F92A56395400313289 /* Project object */; + proxyType = 1; + remoteGlobalIDString = BC5807002A56395400313289; + remoteInfo = Molecules; + }; + BC58071C2A56395900313289 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BC5806F92A56395400313289 /* Project object */; + proxyType = 1; + remoteGlobalIDString = BC5807002A56395400313289; + remoteInfo = Molecules; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXFileReference section */ - 1D30AB110D05D00D00671497 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; - 1D3623EB0D0F72F000981E51 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; - 1D6058910D05DD3D006BFB54 /* Molecules.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Molecules.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; }; - 28FD14FF0DC6FC520079059D /* OpenGLES.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = OpenGLES.framework; path = System/Library/Frameworks/OpenGLES.framework; sourceTree = SDKROOT; }; - 28FD15070DC6FC5B0079059D /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = System/Library/Frameworks/QuartzCore.framework; sourceTree = SDKROOT; }; - 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; - 8D1107310486CEB800E47090 /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = English; path = English.lproj/Info.plist; sourceTree = ""; }; - BC1175B120F1514C00CCEAC9 /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; - BC146A990EC9173000CE5E72 /* SLSMoleculeCustomDownloadViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SLSMoleculeCustomDownloadViewController.h; sourceTree = ""; }; - BC146A9A0EC9173000CE5E72 /* SLSMoleculeCustomDownloadViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SLSMoleculeCustomDownloadViewController.m; sourceTree = ""; }; - BC157286136CBE9200EEFBEC /* 3QE5.pdb.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = 3QE5.pdb.gz; sourceTree = ""; }; - BC157288136CC58A00EEFBEC /* SLSMoleculeLibraryTableCell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SLSMoleculeLibraryTableCell.h; sourceTree = ""; }; - BC157289136CC58A00EEFBEC /* SLSMoleculeLibraryTableCell.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SLSMoleculeLibraryTableCell.m; sourceTree = ""; }; - BC15728B136CD96800EEFBEC /* SLSMoleculeWebDetailViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SLSMoleculeWebDetailViewController.h; sourceTree = ""; }; - BC15728C136CD96800EEFBEC /* SLSMoleculeWebDetailViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SLSMoleculeWebDetailViewController.m; sourceTree = ""; }; - BC1EF3810E36BD6100B09043 /* SLSMoleculeSearchViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SLSMoleculeSearchViewController.h; sourceTree = ""; }; - BC1EF3820E36BD6100B09043 /* SLSMoleculeSearchViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SLSMoleculeSearchViewController.m; sourceTree = ""; }; - BC2795B81162500A006DE4C8 /* Default-Landscape.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Landscape.png"; sourceTree = ""; }; - BC2795B91162500A006DE4C8 /* Default-Portrait.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Portrait.png"; sourceTree = ""; }; - BC2795BC11625065006DE4C8 /* Default.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Default.png; sourceTree = ""; }; - BC281A6911727A89005B7946 /* Document-molecules-64.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Document-molecules-64.png"; sourceTree = ""; }; - BC281A6A11727A89005B7946 /* Document-molecules-320.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Document-molecules-320.png"; sourceTree = ""; }; - BC286E0F11C4751E005D720C /* RotationIcon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "RotationIcon@2x.png"; sourceTree = ""; }; - BC286E2511C47956005D720C /* RotationIconSelected@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "RotationIconSelected@2x.png"; sourceTree = ""; }; - BC2CF8630E5784EC00653418 /* SLSMolecule+PDB.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "SLSMolecule+PDB.h"; sourceTree = ""; }; - BC2CF8640E5784ED00653418 /* SLSMolecule+PDB.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "SLSMolecule+PDB.m"; sourceTree = ""; }; - BC33740A1370C6830036428C /* SLSMolecule+SDF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "SLSMolecule+SDF.h"; sourceTree = ""; }; - BC33740B1370C6830036428C /* SLSMolecule+SDF.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "SLSMolecule+SDF.m"; sourceTree = ""; }; - BC37E7C80E14779E00E11F4F /* SLSMolecule.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SLSMolecule.h; sourceTree = ""; }; - BC37E7C90E14779E00E11F4F /* SLSMolecule.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SLSMolecule.m; sourceTree = ""; }; - BC4E97C10E69F736006CD291 /* libsqlite3.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libsqlite3.dylib; path = usr/lib/libsqlite3.dylib; sourceTree = SDKROOT; }; - BC4E97F90E69F8E1006CD291 /* molecules.sql */ = {isa = PBXFileReference; lastKnownFileType = file; path = molecules.sql; sourceTree = ""; }; - BC4FB6650E4E875900B316CD /* VCTitleCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VCTitleCase.h; sourceTree = ""; }; - BC4FB6660E4E875900B316CD /* VCTitleCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VCTitleCase.m; sourceTree = ""; }; - BC4FB75C0E4F6BAF00B316CD /* greenButton.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = greenButton.png; sourceTree = ""; }; - BC4FB7B90E4F80DB00B316CD /* redButton.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = redButton.png; sourceTree = ""; }; - BC540D0F1145E04500045DE1 /* MoleculesIcon72.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = MoleculesIcon72.png; sourceTree = ""; }; - BC54D56D151A3951003F4A41 /* VisualizationIcon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "VisualizationIcon@2x.png"; sourceTree = ""; }; - BC54D56F151A3C2E003F4A41 /* RotationIconiPad@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "RotationIconiPad@2x.png"; sourceTree = ""; }; - BC54D570151A3C2E003F4A41 /* RotationIconiPadCancel@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "RotationIconiPadCancel@2x.png"; sourceTree = ""; }; - BC54D573151A4953003F4A41 /* MoleculesIcon72@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "MoleculesIcon72@2x.png"; sourceTree = ""; }; - BC54D575151A4C91003F4A41 /* Default-Landscape@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Landscape@2x.png"; sourceTree = ""; }; - BC54D576151A4C91003F4A41 /* Default-Portrait@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default-Portrait@2x.png"; sourceTree = ""; }; - BC57EBF91358DE9000FA1060 /* GLProgram.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GLProgram.h; sourceTree = ""; }; - BC57EBFA1358DE9000FA1060 /* GLProgram.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GLProgram.m; sourceTree = ""; }; - BC5BB79B0E200C3B0001DFD0 /* 1BNA.pdb.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = 1BNA.pdb.gz; sourceTree = ""; }; - BC5BB7DD0E2020B10001DFD0 /* 1EVE.pdb.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = 1EVE.pdb.gz; sourceTree = ""; }; - BC5BB8BB0E204DC20001DFD0 /* SLSMoleculeDetailViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SLSMoleculeDetailViewController.h; sourceTree = ""; }; - BC5BB8BC0E204DC20001DFD0 /* SLSMoleculeDetailViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SLSMoleculeDetailViewController.m; sourceTree = ""; }; - BC5BBBF90E216A170001DFD0 /* 1TRZ.pdb.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = 1TRZ.pdb.gz; sourceTree = ""; }; - BC5BBCD30E219AB90001DFD0 /* MoleculesIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = MoleculesIcon.png; sourceTree = ""; }; - BC61CAFB0E2317EE0074BE04 /* 4TRA.pdb.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = 4TRA.pdb.gz; sourceTree = ""; }; - BC61CC2D0E2463180074BE04 /* SLSCellTextView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SLSCellTextView.h; sourceTree = ""; }; - BC61CC2E0E2463180074BE04 /* SLSCellTextView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SLSCellTextView.m; sourceTree = ""; }; - BC61CC2F0E2463180074BE04 /* SLSTextViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SLSTextViewController.h; sourceTree = ""; }; - BC61CC300E2463180074BE04 /* SLSTextViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SLSTextViewController.m; sourceTree = ""; }; - BC67B8BA137B5DF10075A361 /* SphereAOLookup.fsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = SphereAOLookup.fsh; path = Shaders/SphereAOLookup.fsh; sourceTree = ""; }; - BC67B8BB137B5DF10075A361 /* SphereAOLookup.vsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = SphereAOLookup.vsh; path = Shaders/SphereAOLookup.vsh; sourceTree = ""; }; - BC67B8BE137C4B7A0075A361 /* Default@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Default@2x.png"; sourceTree = ""; }; - BC6A9FCB11CAA8CB00E62D55 /* MoleculesIcon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "MoleculesIcon@2x.png"; sourceTree = ""; }; - BC835183117545C600B478D5 /* 69-display.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "69-display.png"; sourceTree = ""; }; - BCA2E7DA135A1B9B00D7206C /* CylinderDepth.fsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = CylinderDepth.fsh; path = Shaders/CylinderDepth.fsh; sourceTree = ""; }; - BCA2E7DB135A1B9B00D7206C /* CylinderDepth.vsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = CylinderDepth.vsh; path = Shaders/CylinderDepth.vsh; sourceTree = ""; }; - BCA2E7DC135A1B9B00D7206C /* CylinderRaytracing.fsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = CylinderRaytracing.fsh; path = Shaders/CylinderRaytracing.fsh; sourceTree = ""; }; - BCA2E7DD135A1B9B00D7206C /* CylinderRaytracing.vsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = CylinderRaytracing.vsh; path = Shaders/CylinderRaytracing.vsh; sourceTree = ""; }; - BCA2E7DE135A1B9B00D7206C /* SphereDepth.fsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = SphereDepth.fsh; path = Shaders/SphereDepth.fsh; sourceTree = ""; }; - BCA2E7DF135A1B9B00D7206C /* SphereDepth.vsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = SphereDepth.vsh; path = Shaders/SphereDepth.vsh; sourceTree = ""; }; - BCA2E7E0135A1B9B00D7206C /* SphereRaytracing.fsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = SphereRaytracing.fsh; path = Shaders/SphereRaytracing.fsh; sourceTree = ""; }; - BCA2E7E1135A1B9B00D7206C /* SphereRaytracing.vsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = SphereRaytracing.vsh; path = Shaders/SphereRaytracing.vsh; sourceTree = ""; }; - BCA2E810135BB90F00D7206C /* caffeine.pdb.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; path = caffeine.pdb.gz; sourceTree = ""; }; - BCA2E812135BBA3000D7206C /* SphereAmbientOcclusion.fsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = SphereAmbientOcclusion.fsh; path = Shaders/SphereAmbientOcclusion.fsh; sourceTree = ""; }; - BCA2E813135BBA3000D7206C /* SphereAmbientOcclusion.vsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = SphereAmbientOcclusion.vsh; path = Shaders/SphereAmbientOcclusion.vsh; sourceTree = ""; }; - BCA2E81C135E54C400D7206C /* CylinderAmbientOcclusion.fsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = CylinderAmbientOcclusion.fsh; path = Shaders/CylinderAmbientOcclusion.fsh; sourceTree = ""; }; - BCA2E81D135E54C400D7206C /* CylinderAmbientOcclusion.vsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = CylinderAmbientOcclusion.vsh; path = Shaders/CylinderAmbientOcclusion.vsh; sourceTree = ""; }; - BCB3A67813553F9D000A286B /* SLSOpenGLESRenderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SLSOpenGLESRenderer.h; sourceTree = ""; }; - BCB3A67913553F9D000A286B /* SLSOpenGLESRenderer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SLSOpenGLESRenderer.m; sourceTree = ""; }; - BCB3A67B13553FC0000A286B /* SLSOpenGLES11Renderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SLSOpenGLES11Renderer.h; sourceTree = ""; }; - BCB3A67C13553FC1000A286B /* SLSOpenGLES11Renderer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SLSOpenGLES11Renderer.m; sourceTree = ""; }; - BCB3A67E13553FD2000A286B /* SLSOpenGLES20Renderer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SLSOpenGLES20Renderer.h; sourceTree = ""; }; - BCB3A67F13553FD2000A286B /* SLSOpenGLES20Renderer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SLSOpenGLES20Renderer.m; sourceTree = ""; }; - BCC246AD112E3DB700710932 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = "Resources-iPad/English.lproj/SLSMoleculeDownloadView.xib"; sourceTree = ""; }; - BCC246AE112E3DB700710932 /* French */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = French; path = "Resources-iPad/French.lproj/SLSMoleculeDownloadView.xib"; sourceTree = ""; }; - BCC281280E1B1FCD00978ECA /* NSData+Gzip.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSData+Gzip.h"; sourceTree = ""; }; - BCC281290E1B1FCD00978ECA /* NSData+Gzip.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSData+Gzip.m"; sourceTree = ""; }; - BCC286BD0E1D8F9300978ECA /* SLSMoleculeAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SLSMoleculeAppDelegate.h; sourceTree = SOURCE_ROOT; }; - BCC286BE0E1D8F9300978ECA /* SLSMoleculeAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SLSMoleculeAppDelegate.m; sourceTree = SOURCE_ROOT; }; - BCC286BF0E1D8F9300978ECA /* SLSMoleculeRootViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SLSMoleculeRootViewController.h; sourceTree = SOURCE_ROOT; }; - BCC286C00E1D8F9300978ECA /* SLSMoleculeRootViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SLSMoleculeRootViewController.m; sourceTree = SOURCE_ROOT; }; - BCC286C30E1D8FB100978ECA /* SLSMoleculeGLView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SLSMoleculeGLView.h; sourceTree = SOURCE_ROOT; }; - BCC286C40E1D8FB100978ECA /* SLSMoleculeGLView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SLSMoleculeGLView.m; sourceTree = SOURCE_ROOT; }; - BCC286C60E1D8FB100978ECA /* SLSMoleculeGLViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SLSMoleculeGLViewController.h; sourceTree = SOURCE_ROOT; }; - BCC286C70E1D8FB100978ECA /* SLSMoleculeGLViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SLSMoleculeGLViewController.m; sourceTree = SOURCE_ROOT; }; - BCC286CC0E1D905400978ECA /* SLSMoleculeDownloadController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SLSMoleculeDownloadController.h; sourceTree = ""; }; - BCC286CD0E1D905400978ECA /* SLSMoleculeDownloadController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SLSMoleculeDownloadController.m; sourceTree = ""; }; - BCC286CF0E1D905400978ECA /* SLSMoleculeTableViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SLSMoleculeTableViewController.h; sourceTree = SOURCE_ROOT; }; - BCC286D00E1D905400978ECA /* SLSMoleculeTableViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SLSMoleculeTableViewController.m; sourceTree = SOURCE_ROOT; }; - BCC286D50E1D909600978ECA /* Molecules_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Molecules_Prefix.pch; sourceTree = SOURCE_ROOT; }; - BCC8FE7E0EC7E4CA00853857 /* SLSMoleculeDataSourceViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SLSMoleculeDataSourceViewController.h; sourceTree = ""; }; - BCC8FE7F0EC7E4CA00853857 /* SLSMoleculeDataSourceViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SLSMoleculeDataSourceViewController.m; sourceTree = ""; }; - BCCB1EF91361E6BC006F00A0 /* PlainDisplay.fsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = PlainDisplay.fsh; path = Shaders/PlainDisplay.fsh; sourceTree = ""; }; - BCCB1EFA1361E6BC006F00A0 /* PlainDisplay.vsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = PlainDisplay.vsh; path = Shaders/PlainDisplay.vsh; sourceTree = ""; }; - BCD12CF711308E1000737166 /* SLSMoleculeiPadRootViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SLSMoleculeiPadRootViewController.h; sourceTree = ""; }; - BCD12CF811308E1000737166 /* SLSMoleculeiPadRootViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SLSMoleculeiPadRootViewController.m; sourceTree = ""; }; - BCD301611391D0C000C26201 /* SphereDepthWrite.fsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = SphereDepthWrite.fsh; path = Shaders/SphereDepthWrite.fsh; sourceTree = ""; }; - BCD301621391D0C000C26201 /* SphereDepthWrite.vsh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.glsl; name = SphereDepthWrite.vsh; path = Shaders/SphereDepthWrite.vsh; sourceTree = ""; }; - BCD40720151D0CFA00E622EA /* SLSAtomColorKeyController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SLSAtomColorKeyController.h; sourceTree = ""; }; - BCD40721151D0CFA00E622EA /* SLSAtomColorKeyController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SLSAtomColorKeyController.m; sourceTree = ""; }; - BCD4072D151D702600E622EA /* 98-palette.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "98-palette.png"; sourceTree = ""; }; - BCD4072E151D702600E622EA /* 98-palette@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "98-palette@2x.png"; sourceTree = ""; }; - BCDC9F870EB3760300ADCFA5 /* French */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = French; path = French.lproj/Info.plist; sourceTree = ""; }; - BCDC9F9A0EB37B1E00ADCFA5 /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/Localized.strings; sourceTree = ""; }; - BCDC9F9E0EB37B4100ADCFA5 /* French */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = French; path = French.lproj/Localized.strings; sourceTree = ""; }; - BCDFF55F115469BF005C984C /* RotationIconiPad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = RotationIconiPad.png; sourceTree = ""; }; - BCDFF57211546FB2005C984C /* RotationIconiPadCancel.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = RotationIconiPadCancel.png; sourceTree = ""; }; - BCDFF57F115473AD005C984C /* VisualizationIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = VisualizationIcon.png; sourceTree = ""; }; - BCDFF5AD1154819A005C984C /* 57-download.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "57-download.png"; sourceTree = ""; }; - BCF495211040945400BE82D5 /* RotationIcon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = RotationIcon.png; sourceTree = ""; }; - BCF496CB1040FB9D00BE82D5 /* RotationIconSelected.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = RotationIconSelected.png; sourceTree = ""; }; + BC5807012A56395400313289 /* Molecules.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Molecules.app; sourceTree = BUILT_PRODUCTS_DIR; }; + BC5807042A56395400313289 /* MoleculesApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculesApp.swift; sourceTree = ""; }; + BC5807062A56395400313289 /* MoleculeDisplayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeDisplayView.swift; sourceTree = ""; }; + BC5807082A56395900313289 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + BC58070B2A56395900313289 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + BC5807112A56395900313289 /* MoleculesTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MoleculesTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + BC5807152A56395900313289 /* PDBFileTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PDBFileTests.swift; sourceTree = ""; }; + BC58071B2A56395900313289 /* MoleculesUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = MoleculesUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + BC58071F2A56395900313289 /* MoleculesUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculesUITests.swift; sourceTree = ""; }; + BC5807212A56395900313289 /* MoleculesUITestsLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculesUITestsLaunchTests.swift; sourceTree = ""; }; + BC5F1F6E2A578BA000416000 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; + BC5F1F732A579BEE00416000 /* PDBFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PDBFile.swift; sourceTree = ""; }; + BC5F1F762A58D40500416000 /* MolecularStructure.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MolecularStructure.swift; sourceTree = ""; }; + BC5F1F792A58EB5500416000 /* Atom.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Atom.swift; sourceTree = ""; }; + BC5F1F7C2A58EB6300416000 /* Bond.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bond.swift; sourceTree = ""; }; + BC5F1F7F2A58EC5F00416000 /* Coordinate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coordinate.swift; sourceTree = ""; }; + BC5F1F822A59DABF00416000 /* TheoreticalBearing.pdb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = TheoreticalBearing.pdb; path = BuiltInMolecules/TheoreticalBearing.pdb; sourceTree = ""; }; + BC5F1F862A59DAC000416000 /* Heme.sdf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = Heme.sdf; path = BuiltInMolecules/Heme.sdf; sourceTree = ""; }; + BC5F1F892A59DAC000416000 /* Nanotube.pdb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = Nanotube.pdb; path = BuiltInMolecules/Nanotube.pdb; sourceTree = ""; }; + BC5F1FA32A59E96E00416000 /* MoleculeDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeDocument.swift; sourceTree = ""; }; + BC5F1FA82A5A0D4300416000 /* DNA.pdb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = DNA.pdb; path = BuiltInMolecules/DNA.pdb; sourceTree = ""; }; + BC5F1FA92A5A0D4300416000 /* Insulin.pdb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = Insulin.pdb; path = BuiltInMolecules/Insulin.pdb; sourceTree = ""; }; + BC5F1FAA2A5A0D4300416000 /* Buckminsterfullerene.sdf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = Buckminsterfullerene.sdf; path = BuiltInMolecules/Buckminsterfullerene.sdf; sourceTree = ""; }; + BC5F1FAB2A5A0D4300416000 /* Caffeine.pdb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = Caffeine.pdb; path = BuiltInMolecules/Caffeine.pdb; sourceTree = ""; }; + BC5F1FAC2A5A0D4300416000 /* TransferRNA.pdb */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = TransferRNA.pdb; path = BuiltInMolecules/TransferRNA.pdb; sourceTree = ""; }; + BC5F1FCB2A5A29D400416000 /* MetalRenderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetalRenderView.swift; sourceTree = ""; }; + BC5F1FCE2A5A34FD00416000 /* MoleculeMetadataView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeMetadataView.swift; sourceTree = ""; }; + BC5F1FD12A5A4B5300416000 /* SDFFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDFFile.swift; sourceTree = ""; }; + BC5F1FD42A5A4C5800416000 /* XYZFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XYZFile.swift; sourceTree = ""; }; + BC5F1FD72A5A4CAB00416000 /* SDFFileTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SDFFileTests.swift; sourceTree = ""; }; + BC5F1FD92A5A4EA900416000 /* XYZFileTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XYZFileTests.swift; sourceTree = ""; }; + BC5F1FDC2A5B097E00416000 /* Acetylcholinesterase.pdb.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; name = Acetylcholinesterase.pdb.gz; path = BuiltInMolecules/Acetylcholinesterase.pdb.gz; sourceTree = ""; }; + BC5F1FDD2A5B097E00416000 /* 3QE5.pdb.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; name = 3QE5.pdb.gz; path = BuiltInMolecules/3QE5.pdb.gz; sourceTree = ""; }; + BC5F1FDE2A5B097E00416000 /* TheoreticalXenonPump.pdb.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; name = TheoreticalXenonPump.pdb.gz; path = BuiltInMolecules/TheoreticalXenonPump.pdb.gz; sourceTree = ""; }; + BC5F1FE52A5B099400416000 /* TransferRNA.pdb.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; name = TransferRNA.pdb.gz; path = TestMolecules/TransferRNA.pdb.gz; sourceTree = ""; }; + BC5F1FE62A5B099400416000 /* SampleMolecule.xyz */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = SampleMolecule.xyz; path = TestMolecules/SampleMolecule.xyz; sourceTree = ""; }; + BC5F1FE72A5B099400416000 /* Caffeine.pdb.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; name = Caffeine.pdb.gz; path = TestMolecules/Caffeine.pdb.gz; sourceTree = ""; }; + BC5F1FE82A5B099400416000 /* DNA.pdb.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; name = DNA.pdb.gz; path = TestMolecules/DNA.pdb.gz; sourceTree = ""; }; + BC5F1FEE2A5B51AF00416000 /* SphereRaytracing.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = SphereRaytracing.metal; sourceTree = ""; }; + BC5F1FF02A5B63B800416000 /* MetalRenderingDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetalRenderingDevice.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 1D60588F0D05DD3D006BFB54 /* Frameworks */ = { + BC5806FE2A56395400313289 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + BC5F1F712A579BB000416000 /* Gzip in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BC58070E2A56395900313289 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BC5807182A56395900313289 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 1D60589F0D05DD5A006BFB54 /* Foundation.framework in Frameworks */, - 1DF5F4E00D08C38300B7A737 /* UIKit.framework in Frameworks */, - 1D3623EC0D0F72F000981E51 /* CoreGraphics.framework in Frameworks */, - 28FD15000DC6FC520079059D /* OpenGLES.framework in Frameworks */, - 28FD15080DC6FC5B0079059D /* QuartzCore.framework in Frameworks */, - BC4E97C20E69F736006CD291 /* libsqlite3.dylib in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 19C28FACFE9D520D11CA2CBB /* Products */ = { + BC5806F82A56395400313289 = { isa = PBXGroup; children = ( - 1D6058910D05DD3D006BFB54 /* Molecules.app */, + BC5807032A56395400313289 /* Molecules */, + BC5807142A56395900313289 /* MoleculesTests */, + BC58071E2A56395900313289 /* MoleculesUITests */, + BC5F1F5B2A578A3900416000 /* BuiltInMolecules */, + BC5807022A56395400313289 /* Products */, ); - name = Products; sourceTree = ""; }; - 29B97314FDCFA39411CA2CEA /* CustomTemplate */ = { + BC5807022A56395400313289 /* Products */ = { isa = PBXGroup; children = ( - BCC7617C0E0EF8180050DD95 /* Application Control */, - BCC7617D0E0EF8260050DD95 /* Molecules */, - BCC7617B0E0EF7F10050DD95 /* 3D Molecule Display */, - BCC8FE7C0EC7E44D00853857 /* Searching and Retrieving Molecules */, - BCC709540E19C6580054D0EB /* Downloaded Molecules Table */, - BC5BB99E0E207ED10001DFD0 /* Molecule Details View */, - 29B97315FDCFA39411CA2CEA /* Other Sources */, - 29B97317FDCFA39411CA2CEA /* Resources */, - BCC246AB112E3DB500710932 /* Resources-iPad */, - 29B97323FDCFA39411CA2CEA /* Frameworks */, - 19C28FACFE9D520D11CA2CBB /* Products */, - ); - name = CustomTemplate; - sourceTree = ""; - }; - 29B97315FDCFA39411CA2CEA /* Other Sources */ = { - isa = PBXGroup; - children = ( - BCC286D50E1D909600978ECA /* Molecules_Prefix.pch */, - 29B97316FDCFA39411CA2CEA /* main.m */, + BC5807012A56395400313289 /* Molecules.app */, + BC5807112A56395900313289 /* MoleculesTests.xctest */, + BC58071B2A56395900313289 /* MoleculesUITests.xctest */, ); - name = "Other Sources"; - sourceTree = ""; - }; - 29B97317FDCFA39411CA2CEA /* Resources */ = { - isa = PBXGroup; - children = ( - BCD4072D151D702600E622EA /* 98-palette.png */, - BCD4072E151D702600E622EA /* 98-palette@2x.png */, - BC2795BC11625065006DE4C8 /* Default.png */, - BC67B8BE137C4B7A0075A361 /* Default@2x.png */, - BC2795B81162500A006DE4C8 /* Default-Landscape.png */, - BC54D575151A4C91003F4A41 /* Default-Landscape@2x.png */, - BC2795B91162500A006DE4C8 /* Default-Portrait.png */, - BC54D576151A4C91003F4A41 /* Default-Portrait@2x.png */, - BC54D56F151A3C2E003F4A41 /* RotationIconiPad@2x.png */, - BC54D570151A3C2E003F4A41 /* RotationIconiPadCancel@2x.png */, - BC6A9FCB11CAA8CB00E62D55 /* MoleculesIcon@2x.png */, - BC835183117545C600B478D5 /* 69-display.png */, - BC281A6911727A89005B7946 /* Document-molecules-64.png */, - BC281A6A11727A89005B7946 /* Document-molecules-320.png */, - BCDFF5AD1154819A005C984C /* 57-download.png */, - BCF496CB1040FB9D00BE82D5 /* RotationIconSelected.png */, - BC286E2511C47956005D720C /* RotationIconSelected@2x.png */, - BCF495211040945400BE82D5 /* RotationIcon.png */, - BC286E0F11C4751E005D720C /* RotationIcon@2x.png */, - BCDC9F9C0EB37B3400ADCFA5 /* Localized.strings */, - BC4E97F90E69F8E1006CD291 /* molecules.sql */, - BC4FB7B90E4F80DB00B316CD /* redButton.png */, - BC4FB75C0E4F6BAF00B316CD /* greenButton.png */, - BC61CAFB0E2317EE0074BE04 /* 4TRA.pdb.gz */, - BC5BBCD30E219AB90001DFD0 /* MoleculesIcon.png */, - BC540D0F1145E04500045DE1 /* MoleculesIcon72.png */, - BC54D573151A4953003F4A41 /* MoleculesIcon72@2x.png */, - BC5BBBF90E216A170001DFD0 /* 1TRZ.pdb.gz */, - BC5BB7DD0E2020B10001DFD0 /* 1EVE.pdb.gz */, - BC5BB79B0E200C3B0001DFD0 /* 1BNA.pdb.gz */, - BC157286136CBE9200EEFBEC /* 3QE5.pdb.gz */, - BCA2E810135BB90F00D7206C /* caffeine.pdb.gz */, - BCDC9F860EB375FB00ADCFA5 /* Info.plist */, - BCDFF55F115469BF005C984C /* RotationIconiPad.png */, - BCDFF57211546FB2005C984C /* RotationIconiPadCancel.png */, - BCDFF57F115473AD005C984C /* VisualizationIcon.png */, - BC54D56D151A3951003F4A41 /* VisualizationIcon@2x.png */, - BC1175B120F1514C00CCEAC9 /* LaunchScreen.storyboard */, - ); - name = Resources; + name = Products; sourceTree = ""; }; - 29B97323FDCFA39411CA2CEA /* Frameworks */ = { + BC5807032A56395400313289 /* Molecules */ = { isa = PBXGroup; children = ( - BC4E97C10E69F736006CD291 /* libsqlite3.dylib */, - 28FD15070DC6FC5B0079059D /* QuartzCore.framework */, - 28FD14FF0DC6FC520079059D /* OpenGLES.framework */, - 1D3623EB0D0F72F000981E51 /* CoreGraphics.framework */, - 1DF5F4DF0D08C38300B7A737 /* UIKit.framework */, - 1D30AB110D05D00D00671497 /* Foundation.framework */, - ); - name = Frameworks; + BC5807042A56395400313289 /* MoleculesApp.swift */, + BC5F1F722A579BCE00416000 /* FileProcessing */, + BC5F1FA62A59FB1F00416000 /* Rendering */, + BC5F1F6E2A578BA000416000 /* Info.plist */, + BC5807082A56395900313289 /* Assets.xcassets */, + BC58070A2A56395900313289 /* Preview Content */, + ); + path = Molecules; sourceTree = ""; }; - BC5BB99E0E207ED10001DFD0 /* Molecule Details View */ = { + BC58070A2A56395900313289 /* Preview Content */ = { isa = PBXGroup; children = ( - BC61CC2D0E2463180074BE04 /* SLSCellTextView.h */, - BC61CC2E0E2463180074BE04 /* SLSCellTextView.m */, - BC61CC2F0E2463180074BE04 /* SLSTextViewController.h */, - BC61CC300E2463180074BE04 /* SLSTextViewController.m */, - BC5BB8BB0E204DC20001DFD0 /* SLSMoleculeDetailViewController.h */, - BC5BB8BC0E204DC20001DFD0 /* SLSMoleculeDetailViewController.m */, - ); - name = "Molecule Details View "; + BC58070B2A56395900313289 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; sourceTree = ""; }; - BCA2E7D9135A1B7B00D7206C /* Shaders */ = { + BC5807142A56395900313289 /* MoleculesTests */ = { isa = PBXGroup; children = ( - BCCB1EFA1361E6BC006F00A0 /* PlainDisplay.vsh */, - BCCB1EF91361E6BC006F00A0 /* PlainDisplay.fsh */, - BCA2E7DB135A1B9B00D7206C /* CylinderDepth.vsh */, - BCA2E7DA135A1B9B00D7206C /* CylinderDepth.fsh */, - BCA2E7DD135A1B9B00D7206C /* CylinderRaytracing.vsh */, - BCA2E7DC135A1B9B00D7206C /* CylinderRaytracing.fsh */, - BCA2E81D135E54C400D7206C /* CylinderAmbientOcclusion.vsh */, - BCA2E81C135E54C400D7206C /* CylinderAmbientOcclusion.fsh */, - BCD301621391D0C000C26201 /* SphereDepthWrite.vsh */, - BCD301611391D0C000C26201 /* SphereDepthWrite.fsh */, - BCA2E7DF135A1B9B00D7206C /* SphereDepth.vsh */, - BCA2E7DE135A1B9B00D7206C /* SphereDepth.fsh */, - BCA2E7E1135A1B9B00D7206C /* SphereRaytracing.vsh */, - BCA2E7E0135A1B9B00D7206C /* SphereRaytracing.fsh */, - BCA2E813135BBA3000D7206C /* SphereAmbientOcclusion.vsh */, - BCA2E812135BBA3000D7206C /* SphereAmbientOcclusion.fsh */, - BC67B8BB137B5DF10075A361 /* SphereAOLookup.vsh */, - BC67B8BA137B5DF10075A361 /* SphereAOLookup.fsh */, - ); - name = Shaders; + BC5F1FDB2A5B088D00416000 /* TestMolecules */, + BC5807152A56395900313289 /* PDBFileTests.swift */, + BC5F1FD72A5A4CAB00416000 /* SDFFileTests.swift */, + BC5F1FD92A5A4EA900416000 /* XYZFileTests.swift */, + ); + path = MoleculesTests; sourceTree = ""; }; - BCC246AB112E3DB500710932 /* Resources-iPad */ = { + BC58071E2A56395900313289 /* MoleculesUITests */ = { isa = PBXGroup; children = ( - BCC246AC112E3DB700710932 /* SLSMoleculeDownloadView.xib */, + BC58071F2A56395900313289 /* MoleculesUITests.swift */, + BC5807212A56395900313289 /* MoleculesUITestsLaunchTests.swift */, ); - name = "Resources-iPad"; + path = MoleculesUITests; sourceTree = ""; }; - BCC709540E19C6580054D0EB /* Downloaded Molecules Table */ = { + BC5F1F5B2A578A3900416000 /* BuiltInMolecules */ = { isa = PBXGroup; children = ( - BC157288136CC58A00EEFBEC /* SLSMoleculeLibraryTableCell.h */, - BC157289136CC58A00EEFBEC /* SLSMoleculeLibraryTableCell.m */, - BCC286CF0E1D905400978ECA /* SLSMoleculeTableViewController.h */, - BCC286D00E1D905400978ECA /* SLSMoleculeTableViewController.m */, + BC5F1FDD2A5B097E00416000 /* 3QE5.pdb.gz */, + BC5F1FDC2A5B097E00416000 /* Acetylcholinesterase.pdb.gz */, + BC5F1FDE2A5B097E00416000 /* TheoreticalXenonPump.pdb.gz */, + BC5F1FAA2A5A0D4300416000 /* Buckminsterfullerene.sdf */, + BC5F1FAB2A5A0D4300416000 /* Caffeine.pdb */, + BC5F1FA82A5A0D4300416000 /* DNA.pdb */, + BC5F1FA92A5A0D4300416000 /* Insulin.pdb */, + BC5F1FAC2A5A0D4300416000 /* TransferRNA.pdb */, + BC5F1F862A59DAC000416000 /* Heme.sdf */, + BC5F1F892A59DAC000416000 /* Nanotube.pdb */, + BC5F1F822A59DABF00416000 /* TheoreticalBearing.pdb */, ); - name = "Downloaded Molecules Table"; + name = BuiltInMolecules; sourceTree = ""; }; - BCC7617B0E0EF7F10050DD95 /* 3D Molecule Display */ = { + BC5F1F722A579BCE00416000 /* FileProcessing */ = { isa = PBXGroup; children = ( - BCC286C30E1D8FB100978ECA /* SLSMoleculeGLView.h */, - BCC286C40E1D8FB100978ECA /* SLSMoleculeGLView.m */, - BCC286C60E1D8FB100978ECA /* SLSMoleculeGLViewController.h */, - BCC286C70E1D8FB100978ECA /* SLSMoleculeGLViewController.m */, - BCB3A67813553F9D000A286B /* SLSOpenGLESRenderer.h */, - BCB3A67913553F9D000A286B /* SLSOpenGLESRenderer.m */, - BCB3A67B13553FC0000A286B /* SLSOpenGLES11Renderer.h */, - BCB3A67C13553FC1000A286B /* SLSOpenGLES11Renderer.m */, - BCB3A67E13553FD2000A286B /* SLSOpenGLES20Renderer.h */, - BCB3A67F13553FD2000A286B /* SLSOpenGLES20Renderer.m */, - BCD40720151D0CFA00E622EA /* SLSAtomColorKeyController.h */, - BCD40721151D0CFA00E622EA /* SLSAtomColorKeyController.m */, - BCA2E7D9135A1B7B00D7206C /* Shaders */, - BC57EBF91358DE9000FA1060 /* GLProgram.h */, - BC57EBFA1358DE9000FA1060 /* GLProgram.m */, - ); - name = "3D Molecule Display"; + BC5F1FA32A59E96E00416000 /* MoleculeDocument.swift */, + BC5F1F762A58D40500416000 /* MolecularStructure.swift */, + BC5F1F732A579BEE00416000 /* PDBFile.swift */, + BC5F1FD12A5A4B5300416000 /* SDFFile.swift */, + BC5F1FD42A5A4C5800416000 /* XYZFile.swift */, + BC5F1F792A58EB5500416000 /* Atom.swift */, + BC5F1F7C2A58EB6300416000 /* Bond.swift */, + BC5F1F7F2A58EC5F00416000 /* Coordinate.swift */, + ); + path = FileProcessing; sourceTree = ""; }; - BCC7617C0E0EF8180050DD95 /* Application Control */ = { + BC5F1FA62A59FB1F00416000 /* Rendering */ = { isa = PBXGroup; children = ( - BCC286BD0E1D8F9300978ECA /* SLSMoleculeAppDelegate.h */, - BCC286BE0E1D8F9300978ECA /* SLSMoleculeAppDelegate.m */, - BCC286BF0E1D8F9300978ECA /* SLSMoleculeRootViewController.h */, - BCC286C00E1D8F9300978ECA /* SLSMoleculeRootViewController.m */, - BCD12CF711308E1000737166 /* SLSMoleculeiPadRootViewController.h */, - BCD12CF811308E1000737166 /* SLSMoleculeiPadRootViewController.m */, - ); - name = "Application Control"; + BC5807062A56395400313289 /* MoleculeDisplayView.swift */, + BC5F1FCB2A5A29D400416000 /* MetalRenderView.swift */, + BC5F1FCE2A5A34FD00416000 /* MoleculeMetadataView.swift */, + BC5F1FF02A5B63B800416000 /* MetalRenderingDevice.swift */, + BC5F1FED2A5B421600416000 /* Shaders */, + ); + path = Rendering; sourceTree = ""; }; - BCC7617D0E0EF8260050DD95 /* Molecules */ = { + BC5F1FDB2A5B088D00416000 /* TestMolecules */ = { isa = PBXGroup; children = ( - BC37E7C80E14779E00E11F4F /* SLSMolecule.h */, - BC37E7C90E14779E00E11F4F /* SLSMolecule.m */, - BC4FB6650E4E875900B316CD /* VCTitleCase.h */, - BC4FB6660E4E875900B316CD /* VCTitleCase.m */, - BCC281280E1B1FCD00978ECA /* NSData+Gzip.h */, - BCC281290E1B1FCD00978ECA /* NSData+Gzip.m */, - BC2CF8630E5784EC00653418 /* SLSMolecule+PDB.h */, - BC2CF8640E5784ED00653418 /* SLSMolecule+PDB.m */, - BC33740A1370C6830036428C /* SLSMolecule+SDF.h */, - BC33740B1370C6830036428C /* SLSMolecule+SDF.m */, + BC5F1FE72A5B099400416000 /* Caffeine.pdb.gz */, + BC5F1FE82A5B099400416000 /* DNA.pdb.gz */, + BC5F1FE62A5B099400416000 /* SampleMolecule.xyz */, + BC5F1FE52A5B099400416000 /* TransferRNA.pdb.gz */, ); - name = Molecules; + name = TestMolecules; sourceTree = ""; }; - BCC8FE7C0EC7E44D00853857 /* Searching and Retrieving Molecules */ = { + BC5F1FED2A5B421600416000 /* Shaders */ = { isa = PBXGroup; children = ( - BC1EF3810E36BD6100B09043 /* SLSMoleculeSearchViewController.h */, - BC1EF3820E36BD6100B09043 /* SLSMoleculeSearchViewController.m */, - BCC286CC0E1D905400978ECA /* SLSMoleculeDownloadController.h */, - BCC286CD0E1D905400978ECA /* SLSMoleculeDownloadController.m */, - BCC8FE7E0EC7E4CA00853857 /* SLSMoleculeDataSourceViewController.h */, - BCC8FE7F0EC7E4CA00853857 /* SLSMoleculeDataSourceViewController.m */, - BC146A990EC9173000CE5E72 /* SLSMoleculeCustomDownloadViewController.h */, - BC146A9A0EC9173000CE5E72 /* SLSMoleculeCustomDownloadViewController.m */, - BC15728B136CD96800EEFBEC /* SLSMoleculeWebDetailViewController.h */, - BC15728C136CD96800EEFBEC /* SLSMoleculeWebDetailViewController.m */, - ); - name = "Searching and Retrieving Molecules"; + BC5F1FEE2A5B51AF00416000 /* SphereRaytracing.metal */, + ); + path = Shaders; sourceTree = ""; }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ - 1D6058900D05DD3D006BFB54 /* Molecules */ = { + BC5807002A56395400313289 /* Molecules */ = { isa = PBXNativeTarget; - buildConfigurationList = 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "Molecules" */; + buildConfigurationList = BC5807252A56395900313289 /* Build configuration list for PBXNativeTarget "Molecules" */; buildPhases = ( - 1D60588D0D05DD3D006BFB54 /* Resources */, - 1D60588E0D05DD3D006BFB54 /* Sources */, - 1D60588F0D05DD3D006BFB54 /* Frameworks */, + BC5806FD2A56395400313289 /* Sources */, + BC5806FE2A56395400313289 /* Frameworks */, + BC5806FF2A56395400313289 /* Resources */, ); buildRules = ( ); dependencies = ( ); name = Molecules; - productName = PortaMol; - productReference = 1D6058910D05DD3D006BFB54 /* Molecules.app */; + packageProductDependencies = ( + BC5F1F702A579BB000416000 /* Gzip */, + ); + productName = Molecules; + productReference = BC5807012A56395400313289 /* Molecules.app */; productType = "com.apple.product-type.application"; }; + BC5807102A56395900313289 /* MoleculesTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = BC5807282A56395900313289 /* Build configuration list for PBXNativeTarget "MoleculesTests" */; + buildPhases = ( + BC58070D2A56395900313289 /* Sources */, + BC58070E2A56395900313289 /* Frameworks */, + BC58070F2A56395900313289 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + BC5807132A56395900313289 /* PBXTargetDependency */, + ); + name = MoleculesTests; + productName = MoleculesTests; + productReference = BC5807112A56395900313289 /* MoleculesTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + BC58071A2A56395900313289 /* MoleculesUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = BC58072B2A56395900313289 /* Build configuration list for PBXNativeTarget "MoleculesUITests" */; + buildPhases = ( + BC5807172A56395900313289 /* Sources */, + BC5807182A56395900313289 /* Frameworks */, + BC5807192A56395900313289 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + BC58071D2A56395900313289 /* PBXTargetDependency */, + ); + name = MoleculesUITests; + productName = MoleculesUITests; + productReference = BC58071B2A56395900313289 /* MoleculesUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ - 29B97313FDCFA39411CA2CEA /* Project object */ = { + BC5806F92A56395400313289 /* Project object */ = { isa = PBXProject; attributes = { - ORGANIZATIONNAME = "Sunset Lake Software LLC"; + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1410; + LastUpgradeCheck = 1410; + TargetAttributes = { + BC5807002A56395400313289 = { + CreatedOnToolsVersion = 14.1; + }; + BC5807102A56395900313289 = { + CreatedOnToolsVersion = 14.1; + TestTargetID = BC5807002A56395400313289; + }; + BC58071A2A56395900313289 = { + CreatedOnToolsVersion = 14.1; + TestTargetID = BC5807002A56395400313289; + }; + }; }; - buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Molecules" */; - compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; - hasScannedForEncodings = 1; + buildConfigurationList = BC5806FC2A56395400313289 /* Build configuration list for PBXProject "Molecules" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; knownRegions = ( - English, - Japanese, - French, - German, + en, + Base, + ); + mainGroup = BC5806F82A56395400313289; + packageReferences = ( + BC5F1F6F2A579BB000416000 /* XCRemoteSwiftPackageReference "GzipSwift" */, ); - mainGroup = 29B97314FDCFA39411CA2CEA /* CustomTemplate */; + productRefGroup = BC5807022A56395400313289 /* Products */; projectDirPath = ""; projectRoot = ""; targets = ( - 1D6058900D05DD3D006BFB54 /* Molecules */, + BC5807002A56395400313289 /* Molecules */, + BC5807102A56395900313289 /* MoleculesTests */, + BC58071A2A56395900313289 /* MoleculesUITests */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ - 1D60588D0D05DD3D006BFB54 /* Resources */ = { + BC5806FF2A56395400313289 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BC5F1FE32A5B097E00416000 /* TheoreticalXenonPump.pdb.gz in Resources */, + BC5F1FE12A5B097E00416000 /* 3QE5.pdb.gz in Resources */, + BC5F1FB92A5A0D4300416000 /* TransferRNA.pdb in Resources */, + BC5F1FB32A5A0D4300416000 /* Insulin.pdb in Resources */, + BC5F1FDF2A5B097E00416000 /* Acetylcholinesterase.pdb.gz in Resources */, + BC5F1FB52A5A0D4300416000 /* Buckminsterfullerene.sdf in Resources */, + BC5F1FB72A5A0D4300416000 /* Caffeine.pdb in Resources */, + BC5F1F952A59DAC000416000 /* Heme.sdf in Resources */, + BC58070C2A56395900313289 /* Preview Assets.xcassets in Resources */, + BC5F1F9B2A59DAC000416000 /* Nanotube.pdb in Resources */, + BC5807092A56395900313289 /* Assets.xcassets in Resources */, + BC5F1FB12A5A0D4300416000 /* DNA.pdb in Resources */, + BC5F1F8D2A59DAC000416000 /* TheoreticalBearing.pdb in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BC58070F2A56395900313289 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BC5F1FEC2A5B099400416000 /* DNA.pdb.gz in Resources */, + BC5F1F8E2A59DAC000416000 /* TheoreticalBearing.pdb in Resources */, + BC5F1FE02A5B097E00416000 /* Acetylcholinesterase.pdb.gz in Resources */, + BC5F1FE42A5B097E00416000 /* TheoreticalXenonPump.pdb.gz in Resources */, + BC5F1FE92A5B099400416000 /* TransferRNA.pdb.gz in Resources */, + BC5F1FB82A5A0D4300416000 /* Caffeine.pdb in Resources */, + BC5F1FEB2A5B099400416000 /* Caffeine.pdb.gz in Resources */, + BC5F1FEA2A5B099400416000 /* SampleMolecule.xyz in Resources */, + BC5F1FBA2A5A0D4300416000 /* TransferRNA.pdb in Resources */, + BC5F1FB62A5A0D4300416000 /* Buckminsterfullerene.sdf in Resources */, + BC5F1FB42A5A0D4300416000 /* Insulin.pdb in Resources */, + BC5F1F9C2A59DAC000416000 /* Nanotube.pdb in Resources */, + BC5F1FB22A5A0D4300416000 /* DNA.pdb in Resources */, + BC5F1FE22A5B097E00416000 /* 3QE5.pdb.gz in Resources */, + BC5F1F962A59DAC000416000 /* Heme.sdf in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + BC5807192A56395900313289 /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - BC5BB79C0E200C3B0001DFD0 /* 1BNA.pdb.gz in Resources */, - BC5BB7DF0E2020B10001DFD0 /* 1EVE.pdb.gz in Resources */, - BC5BBBFA0E216A170001DFD0 /* 1TRZ.pdb.gz in Resources */, - BC5BBCD40E219AB90001DFD0 /* MoleculesIcon.png in Resources */, - BC61CAFC0E2317EE0074BE04 /* 4TRA.pdb.gz in Resources */, - BC4FB75D0E4F6BB000B316CD /* greenButton.png in Resources */, - BC4FB7BA0E4F80DB00B316CD /* redButton.png in Resources */, - BC4E97FA0E69F8E1006CD291 /* molecules.sql in Resources */, - BCDC9F9D0EB37B3400ADCFA5 /* Localized.strings in Resources */, - BCF495221040945400BE82D5 /* RotationIcon.png in Resources */, - BCF496CC1040FB9D00BE82D5 /* RotationIconSelected.png in Resources */, - BC540D101145E04600045DE1 /* MoleculesIcon72.png in Resources */, - BCDFF560115469C1005C984C /* RotationIconiPad.png in Resources */, - BCDFF57311546FB2005C984C /* RotationIconiPadCancel.png in Resources */, - BCDFF580115473AD005C984C /* VisualizationIcon.png in Resources */, - BCDFF5AE1154819A005C984C /* 57-download.png in Resources */, - BC2795BA1162500A006DE4C8 /* Default-Landscape.png in Resources */, - BC2795BB1162500A006DE4C8 /* Default-Portrait.png in Resources */, - BC2795BD11625065006DE4C8 /* Default.png in Resources */, - BC281A6B11727A89005B7946 /* Document-molecules-64.png in Resources */, - BC281A6C11727A89005B7946 /* Document-molecules-320.png in Resources */, - BC835184117545C600B478D5 /* 69-display.png in Resources */, - BC286E1011C4751E005D720C /* RotationIcon@2x.png in Resources */, - BC286E2611C47956005D720C /* RotationIconSelected@2x.png in Resources */, - BC6A9FCC11CAA8CB00E62D55 /* MoleculesIcon@2x.png in Resources */, - BCA2E7E2135A1B9B00D7206C /* CylinderDepth.fsh in Resources */, - BCA2E7E3135A1B9B00D7206C /* CylinderDepth.vsh in Resources */, - BCA2E7E4135A1B9B00D7206C /* CylinderRaytracing.fsh in Resources */, - BCA2E7E5135A1B9B00D7206C /* CylinderRaytracing.vsh in Resources */, - BCA2E7E6135A1B9B00D7206C /* SphereDepth.fsh in Resources */, - BCA2E7E7135A1B9B00D7206C /* SphereDepth.vsh in Resources */, - BCD301631391D0C000C26201 /* SphereDepthWrite.fsh in Resources */, - BCD301641391D0C000C26201 /* SphereDepthWrite.vsh in Resources */, - BCA2E7E8135A1B9B00D7206C /* SphereRaytracing.fsh in Resources */, - BCA2E7E9135A1B9B00D7206C /* SphereRaytracing.vsh in Resources */, - BCA2E814135BBA3000D7206C /* SphereAmbientOcclusion.fsh in Resources */, - BCA2E815135BBA3000D7206C /* SphereAmbientOcclusion.vsh in Resources */, - BCA2E81E135E54C500D7206C /* CylinderAmbientOcclusion.fsh in Resources */, - BCA2E81F135E54C500D7206C /* CylinderAmbientOcclusion.vsh in Resources */, - BCCB1EFB1361E6BC006F00A0 /* PlainDisplay.fsh in Resources */, - BCCB1EFC1361E6BC006F00A0 /* PlainDisplay.vsh in Resources */, - BCA2E811135BB90F00D7206C /* caffeine.pdb.gz in Resources */, - BC157287136CBE9200EEFBEC /* 3QE5.pdb.gz in Resources */, - BC67B8BC137B5DF10075A361 /* SphereAOLookup.fsh in Resources */, - BC67B8BD137B5DF10075A361 /* SphereAOLookup.vsh in Resources */, - BC67B8BF137C4B7A0075A361 /* Default@2x.png in Resources */, - BC54D56E151A3951003F4A41 /* VisualizationIcon@2x.png in Resources */, - BC54D571151A3C2E003F4A41 /* RotationIconiPad@2x.png in Resources */, - BC54D572151A3C2E003F4A41 /* RotationIconiPadCancel@2x.png in Resources */, - BC54D574151A4953003F4A41 /* MoleculesIcon72@2x.png in Resources */, - BC54D577151A4C91003F4A41 /* Default-Landscape@2x.png in Resources */, - BC54D578151A4C91003F4A41 /* Default-Portrait@2x.png in Resources */, - BCD4072F151D702600E622EA /* 98-palette.png in Resources */, - BC1175B220F1514C00CCEAC9 /* LaunchScreen.storyboard in Resources */, - BCD40730151D702600E622EA /* 98-palette@2x.png in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ - 1D60588E0D05DD3D006BFB54 /* Sources */ = { + BC5806FD2A56395400313289 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 1D60589B0D05DD56006BFB54 /* main.m in Sources */, - BC37E7CA0E14779E00E11F4F /* SLSMolecule.m in Sources */, - BCC2812A0E1B1FCD00978ECA /* NSData+Gzip.m in Sources */, - BCC286C10E1D8F9300978ECA /* SLSMoleculeAppDelegate.m in Sources */, - BCC286C20E1D8F9300978ECA /* SLSMoleculeRootViewController.m in Sources */, - BCC286C80E1D8FB100978ECA /* SLSMoleculeGLView.m in Sources */, - BCC286CA0E1D8FB100978ECA /* SLSMoleculeGLViewController.m in Sources */, - BCC286D20E1D905400978ECA /* SLSMoleculeDownloadController.m in Sources */, - BCC286D40E1D905400978ECA /* SLSMoleculeTableViewController.m in Sources */, - BC5BB8BD0E204DC20001DFD0 /* SLSMoleculeDetailViewController.m in Sources */, - BC61CC310E2463180074BE04 /* SLSCellTextView.m in Sources */, - BC61CC320E2463180074BE04 /* SLSTextViewController.m in Sources */, - BC1EF3830E36BD6100B09043 /* SLSMoleculeSearchViewController.m in Sources */, - BC4FB6670E4E875900B316CD /* VCTitleCase.m in Sources */, - BC2CF8650E5784ED00653418 /* SLSMolecule+PDB.m in Sources */, - BCC8FE800EC7E4CA00853857 /* SLSMoleculeDataSourceViewController.m in Sources */, - BC146A9B0EC9173000CE5E72 /* SLSMoleculeCustomDownloadViewController.m in Sources */, - BCD12CFA11308E1000737166 /* SLSMoleculeiPadRootViewController.m in Sources */, - BCB3A67A13553F9D000A286B /* SLSOpenGLESRenderer.m in Sources */, - BCB3A67D13553FC1000A286B /* SLSOpenGLES11Renderer.m in Sources */, - BCB3A68013553FD2000A286B /* SLSOpenGLES20Renderer.m in Sources */, - BC57EBFB1358DE9000FA1060 /* GLProgram.m in Sources */, - BC15728A136CC58A00EEFBEC /* SLSMoleculeLibraryTableCell.m in Sources */, - BC15728D136CD96800EEFBEC /* SLSMoleculeWebDetailViewController.m in Sources */, - BC33740C1370C6830036428C /* SLSMolecule+SDF.m in Sources */, - BCD40722151D0CFA00E622EA /* SLSAtomColorKeyController.m in Sources */, + BC5F1F802A58EC5F00416000 /* Coordinate.swift in Sources */, + BC5F1FCF2A5A34FD00416000 /* MoleculeMetadataView.swift in Sources */, + BC5F1F772A58D40500416000 /* MolecularStructure.swift in Sources */, + BC5F1FD52A5A4C5800416000 /* XYZFile.swift in Sources */, + BC5F1FD22A5A4B5300416000 /* SDFFile.swift in Sources */, + BC5807072A56395400313289 /* MoleculeDisplayView.swift in Sources */, + BC5F1F7D2A58EB6300416000 /* Bond.swift in Sources */, + BC5F1F7A2A58EB5500416000 /* Atom.swift in Sources */, + BC5F1FF12A5B63B800416000 /* MetalRenderingDevice.swift in Sources */, + BC5F1F742A579BEE00416000 /* PDBFile.swift in Sources */, + BC5F1FA42A59E96E00416000 /* MoleculeDocument.swift in Sources */, + BC5F1FEF2A5B51AF00416000 /* SphereRaytracing.metal in Sources */, + BC5F1FCC2A5A29D400416000 /* MetalRenderView.swift in Sources */, + BC5807052A56395400313289 /* MoleculesApp.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXVariantGroup section */ - BCC246AC112E3DB700710932 /* SLSMoleculeDownloadView.xib */ = { - isa = PBXVariantGroup; - children = ( - BCC246AD112E3DB700710932 /* English */, - BCC246AE112E3DB700710932 /* French */, + BC58070D2A56395900313289 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BC5F1FD02A5A34FD00416000 /* MoleculeMetadataView.swift in Sources */, + BC5F1FD82A5A4CAB00416000 /* SDFFileTests.swift in Sources */, + BC5F1FDA2A5A4EA900416000 /* XYZFileTests.swift in Sources */, + BC5F1FCD2A5A29D400416000 /* MetalRenderView.swift in Sources */, + BC5807162A56395900313289 /* PDBFileTests.swift in Sources */, ); - name = SLSMoleculeDownloadView.xib; - sourceTree = ""; + runOnlyForDeploymentPostprocessing = 0; }; - BCDC9F860EB375FB00ADCFA5 /* Info.plist */ = { - isa = PBXVariantGroup; - children = ( - 8D1107310486CEB800E47090 /* English */, - BCDC9F870EB3760300ADCFA5 /* French */, + BC5807172A56395900313289 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + BC5807222A56395900313289 /* MoleculesUITestsLaunchTests.swift in Sources */, + BC5807202A56395900313289 /* MoleculesUITests.swift in Sources */, ); - name = Info.plist; - sourceTree = ""; + runOnlyForDeploymentPostprocessing = 0; }; - BCDC9F9C0EB37B3400ADCFA5 /* Localized.strings */ = { - isa = PBXVariantGroup; - children = ( - BCDC9F9A0EB37B1E00ADCFA5 /* English */, - BCDC9F9E0EB37B4100ADCFA5 /* French */, - ); - name = Localized.strings; - sourceTree = ""; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + BC5807132A56395900313289 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = BC5807002A56395400313289 /* Molecules */; + targetProxy = BC5807122A56395900313289 /* PBXContainerItemProxy */; }; -/* End PBXVariantGroup section */ + BC58071D2A56395900313289 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = BC5807002A56395400313289 /* Molecules */; + targetProxy = BC58071C2A56395900313289 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ - 1D6058940D05DD3E006BFB54 /* Debug */ = { + BC5807232A56395900313289 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = "$(ARCHS_STANDARD)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; COPY_PHASE_STRIP = NO; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = Molecules_Prefix.pch; - INFOPLIST_FILE = English.lproj/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - PRODUCT_NAME = Molecules; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.1; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; }; name = Debug; }; - 1D6058950D05DD3E006BFB54 /* Release */ = { + BC5807242A56395900313289 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = "$(ARCHS_STANDARD)"; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; - CODE_SIGN_IDENTITY = "iPhone Developer"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - COPY_PHASE_STRIP = YES; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = Molecules_Prefix.pch; - INFOPLIST_FILE = English.lproj/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - PRODUCT_NAME = Molecules; - PROVISIONING_PROFILE = ""; - "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.1; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; }; name = Release; }; - BC78D1AC0E1FDC0600BD9B0B /* Distribution */ = { + BC5807262A56395900313289 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - CODE_SIGN_IDENTITY = "iPhone Distribution"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - GCC_C_LANGUAGE_STANDARD = c99; - GCC_ENABLE_CPP_EXCEPTIONS = NO; - GCC_ENABLE_CPP_RTTI = NO; - GCC_TREAT_WARNINGS_AS_ERRORS = YES; - GCC_VERSION = com.apple.compilers.llvm.clang.1_0; - GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - OTHER_CFLAGS = "-fstrict-aliasing"; - "OTHER_CFLAGS[arch=armv6]" = ( - "-fstrict-aliasing", - "-mno-thumb", + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"Molecules/Preview Content\""; + DEVELOPMENT_TEAM = J2U2U9GBML; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = Molecules/Info.plist; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + INFOPLIST_KEY_UISupportsDocumentBrowser = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", ); - "OTHER_CFLAGS[arch=armv7]" = "-fstrict-aliasing"; - OTHER_LDFLAGS = "-lz"; - PROVISIONING_PROFILE = ""; - "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; - RUN_CLANG_STATIC_ANALYZER = YES; - SDKROOT = iphoneos; + MARKETING_VERSION = 3.0; + PRODUCT_BUNDLE_IDENTIFIER = com.sunsetlakesoftware.Molecules; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; - WARNING_CFLAGS = ""; }; - name = Distribution; + name = Debug; }; - BC78D1AD0E1FDC0600BD9B0B /* Distribution */ = { + BC5807272A56395900313289 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - ARCHS = "$(ARCHS_STANDARD)"; - CLANG_ENABLE_OBJC_ARC = YES; - CODE_SIGN_IDENTITY = "iPhone Distribution"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; - COPY_PHASE_STRIP = YES; - GCC_PRECOMPILE_PREFIX_HEADER = YES; - GCC_PREFIX_HEADER = Molecules_Prefix.pch; - INFOPLIST_FILE = English.lproj/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 8.0; - PRODUCT_NAME = Molecules; - PROVISIONING_PROFILE = ""; - "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"Molecules/Preview Content\""; + DEVELOPMENT_TEAM = J2U2U9GBML; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = Molecules/Info.plist; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; + INFOPLIST_KEY_UISupportsDocumentBrowser = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 3.0; + PRODUCT_BUNDLE_IDENTIFIER = com.sunsetlakesoftware.Molecules; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; }; - name = Distribution; + name = Release; }; - C01FCF4F08A954540054247B /* Debug */ = { + BC5807292A56395900313289 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - CODE_SIGN_IDENTITY = "iPhone Developer"; - "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; - GCC_C_LANGUAGE_STANDARD = c99; - GCC_ENABLE_CPP_EXCEPTIONS = NO; - GCC_ENABLE_CPP_RTTI = NO; - GCC_TREAT_WARNINGS_AS_ERRORS = YES; - GCC_VERSION = com.apple.compilers.llvm.clang.1_0; - GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - ONLY_ACTIVE_ARCH = YES; - OTHER_CFLAGS = "-fstrict-aliasing"; - "OTHER_CFLAGS[arch=armv6]" = ( - "-fstrict-aliasing", - "-mno-thumb", - ); - "OTHER_CFLAGS[arch=armv7]" = "-fstrict-aliasing"; - OTHER_LDFLAGS = "-lz"; - PROVISIONING_PROFILE = ""; - "PROVISIONING_PROFILE[sdk=iphoneos*]" = ""; - RUN_CLANG_STATIC_ANALYZER = YES; - SDKROOT = iphoneos; + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = J2U2U9GBML; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.sunsetlakesoftware.MoleculesTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; - WARNING_CFLAGS = ""; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Molecules.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Molecules"; }; name = Debug; }; - C01FCF5008A954540054247B /* Release */ = { + BC58072A2A56395900313289 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - CODE_SIGN_IDENTITY = "iPhone Developer"; - "CODE_SIGN_IDENTITY[sdk=*]" = "iPhone Developer"; - GCC_C_LANGUAGE_STANDARD = c99; - GCC_ENABLE_CPP_EXCEPTIONS = NO; - GCC_ENABLE_CPP_RTTI = NO; - GCC_TREAT_WARNINGS_AS_ERRORS = YES; - GCC_VERSION = com.apple.compilers.llvm.clang.1_0; - GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - OTHER_CFLAGS = "-fstrict-aliasing"; - "OTHER_CFLAGS[arch=armv6]" = ( - "-fstrict-aliasing", - "-mno-thumb", - ); - "OTHER_CFLAGS[arch=armv7]" = "-fstrict-aliasing"; - OTHER_LDFLAGS = "-lz"; - PROVISIONING_PROFILE = ""; - "PROVISIONING_PROFILE[sdk=*]" = ""; - RUN_CLANG_STATIC_ANALYZER = YES; - SDKROOT = iphoneos; + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = J2U2U9GBML; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.sunsetlakesoftware.MoleculesTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Molecules.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Molecules"; + }; + name = Release; + }; + BC58072C2A56395900313289 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = J2U2U9GBML; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.sunsetlakesoftware.MoleculesUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = Molecules; + }; + name = Debug; + }; + BC58072D2A56395900313289 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = J2U2U9GBML; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 16.0; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.sunsetlakesoftware.MoleculesUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; - WARNING_CFLAGS = ""; + TEST_TARGET_NAME = Molecules; }; name = Release; }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ - 1D6058960D05DD3E006BFB54 /* Build configuration list for PBXNativeTarget "Molecules" */ = { + BC5806FC2A56395400313289 /* Build configuration list for PBXProject "Molecules" */ = { isa = XCConfigurationList; buildConfigurations = ( - 1D6058940D05DD3E006BFB54 /* Debug */, - 1D6058950D05DD3E006BFB54 /* Release */, - BC78D1AD0E1FDC0600BD9B0B /* Distribution */, + BC5807232A56395900313289 /* Debug */, + BC5807242A56395900313289 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - C01FCF4E08A954540054247B /* Build configuration list for PBXProject "Molecules" */ = { + BC5807252A56395900313289 /* Build configuration list for PBXNativeTarget "Molecules" */ = { isa = XCConfigurationList; buildConfigurations = ( - C01FCF4F08A954540054247B /* Debug */, - C01FCF5008A954540054247B /* Release */, - BC78D1AC0E1FDC0600BD9B0B /* Distribution */, + BC5807262A56395900313289 /* Debug */, + BC5807272A56395900313289 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BC5807282A56395900313289 /* Build configuration list for PBXNativeTarget "MoleculesTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BC5807292A56395900313289 /* Debug */, + BC58072A2A56395900313289 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + BC58072B2A56395900313289 /* Build configuration list for PBXNativeTarget "MoleculesUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + BC58072C2A56395900313289 /* Debug */, + BC58072D2A56395900313289 /* Release */, ); defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + BC5F1F6F2A579BB000416000 /* XCRemoteSwiftPackageReference "GzipSwift" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/1024jp/GzipSwift"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 6.0.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + BC5F1F702A579BB000416000 /* Gzip */ = { + isa = XCSwiftPackageProductDependency; + package = BC5F1F6F2A579BB000416000 /* XCRemoteSwiftPackageReference "GzipSwift" */; + productName = Gzip; + }; +/* End XCSwiftPackageProductDependency section */ }; - rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; + rootObject = BC5806F92A56395400313289 /* Project object */; } diff --git a/Molecules/Assets.xcassets/AccentColor.colorset/Contents.json b/Molecules/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/Molecules/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Molecules/Assets.xcassets/AppIcon.appiconset/Contents.json b/Molecules/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..41a42b2 --- /dev/null +++ b/Molecules/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,14 @@ +{ + "images" : [ + { + "filename" : "MoleculesIcon-1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Molecules/Assets.xcassets/AppIcon.appiconset/MoleculesIcon-1024.png b/Molecules/Assets.xcassets/AppIcon.appiconset/MoleculesIcon-1024.png new file mode 100644 index 0000000000000000000000000000000000000000..4c5b677b21be7dbbddb25e0f6087e550c683c0c7 GIT binary patch literal 370912 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 z

    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 ~uE&KHH_s{n+{e_=?QWk;VO~Gn{A& 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 literal 0 HcmV?d00001 diff --git a/Molecules/Assets.xcassets/Contents.json b/Molecules/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Molecules/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Molecules/FileProcessing/Atom.swift b/Molecules/FileProcessing/Atom.swift new file mode 100644 index 0000000..7048963 --- /dev/null +++ b/Molecules/FileProcessing/Atom.swift @@ -0,0 +1,51 @@ +/// Individual atoms within a molecular structure, located in 3-D space. +struct Atom { + enum Element { + case carbon + case hydrogen + case oxygen + case nitrogen + case sulfur + case phosphorous + case iron + case silicon + case fluorine + case chlorine + case bromine + case iodine + case calcium + case zinc + case cadmium + case sodium + case magnesium + case unknown + } + + let element: Element + let location: Coordinate +} + +extension Atom.Element { + init(code: String) { + switch code.uppercased() { + case "C": self = .carbon + case "H": self = .hydrogen + case "O": self = .oxygen + case "N": self = .nitrogen + case "S": self = .sulfur + case "P": self = .phosphorous + case "FE": self = .iron + case "SI": self = .silicon + case "F": self = .fluorine + case "CL": self = .chlorine + case "BR": self = .bromine + case "I": self = .iodine + case "CA": self = .calcium + case "ZN": self = .zinc + case "CD": self = .cadmium + case "NA": self = .sodium + case "MG": self = .magnesium + default: self = .unknown + } + } +} diff --git a/Molecules/FileProcessing/Bond.swift b/Molecules/FileProcessing/Bond.swift new file mode 100644 index 0000000..fa651ef --- /dev/null +++ b/Molecules/FileProcessing/Bond.swift @@ -0,0 +1,12 @@ +/// Bonds between atoms in a molecular structure. +struct Bond { + enum Strength { + case single + case double + case triple + } + + let strength: Strength + let start: Coordinate + let end: Coordinate +} diff --git a/Molecules/FileProcessing/Coordinate.swift b/Molecules/FileProcessing/Coordinate.swift new file mode 100644 index 0000000..d2c2bad --- /dev/null +++ b/Molecules/FileProcessing/Coordinate.swift @@ -0,0 +1,6 @@ +/// A basic representation of 3-D spatial coordinates for atoms and bonds. +struct Coordinate { + let x: Float + let y: Float + let z: Float +} diff --git a/Molecules/FileProcessing/MolecularStructure.swift b/Molecules/FileProcessing/MolecularStructure.swift new file mode 100644 index 0000000..297ee09 --- /dev/null +++ b/Molecules/FileProcessing/MolecularStructure.swift @@ -0,0 +1,67 @@ +import Foundation + +/// All molecular file types need to conform to this protocol in order to be rendered. +protocol MolecularStructure { + var atoms: [Atom] { get } + var bonds: [Bond] { get } + var centerOfMass: Coordinate { get } + var minimumLimits: Coordinate { get } + var maximumLimits: Coordinate { get } + var suggestedScaleFactor: Coordinate { get } + + init(data: Data) throws +} + +/// To aid in scaling and other rendering tasks, statistics of a molecule are captured during +/// parsing. This helps aggregate commonly-used operations in a shared data structure. +struct MoleculeStatistics { + var minimumXPosition: Float = 1000.0 + var maximumXPosition: Float = 0.0 + var minimumYPosition: Float = 1000.0 + var maximumYPosition: Float = 0.0 + var minimumZPosition: Float = 1000.0 + var maximumZPosition: Float = 0.0 + var tallyForCenterOfMassInX: Float = 0.0 + var tallyForCenterOfMassInY: Float = 0.0 + var tallyForCenterOfMassInZ: Float = 0.0 + + var minimumLimits: Coordinate { + Coordinate(x: minimumXPosition, y: minimumYPosition, z: minimumZPosition) + } + + var maximumLimits: Coordinate { + Coordinate(x: maximumXPosition, y: maximumYPosition, z: maximumZPosition) + } + + func calculatedCenterOfMass(atomCount: Int) -> Coordinate { + return Coordinate( + x: tallyForCenterOfMassInX / Float(atomCount), + y: tallyForCenterOfMassInY / Float(atomCount), + z: tallyForCenterOfMassInZ / Float(atomCount) + ) + } + + mutating func update(using newCoordinate: Coordinate) { + tallyForCenterOfMassInX += newCoordinate.x + tallyForCenterOfMassInY += newCoordinate.y + tallyForCenterOfMassInZ += newCoordinate.z + minimumXPosition = min(minimumXPosition, newCoordinate.x) + minimumYPosition = min(minimumYPosition, newCoordinate.y) + minimumZPosition = min(minimumZPosition, newCoordinate.z) + maximumXPosition = max(maximumXPosition, newCoordinate.x) + maximumYPosition = max(maximumYPosition, newCoordinate.y) + maximumZPosition = max(maximumZPosition, newCoordinate.z) + } + + func calculatedScaleFactor() -> Coordinate { + guard maximumXPosition > minimumXPosition, + maximumYPosition > minimumYPosition, + maximumZPosition > minimumZPosition else { + return Coordinate(x: 1.0, y: 1.0, z: 1.0) + } + return Coordinate(x: 1.5 / (maximumXPosition - minimumXPosition), + y: 1.5 / (maximumYPosition - minimumYPosition), + z: (1.5 * 1.25) / (maximumZPosition - minimumZPosition) + ) + } +} diff --git a/Molecules/FileProcessing/MoleculeDocument.swift b/Molecules/FileProcessing/MoleculeDocument.swift new file mode 100644 index 0000000..3015022 --- /dev/null +++ b/Molecules/FileProcessing/MoleculeDocument.swift @@ -0,0 +1,84 @@ +import Gzip +import SwiftUI +import UniformTypeIdentifiers + +/// The central viewer document that takes in three file formats: PDB, SDF, and XYZ. It also can +/// process gzip-compressed .pdb.gz files, although that type registration is currently via fairly +/// broad .gz associations. +struct MoleculeDocument: FileDocument { + static var readableContentTypes: [UTType] { [.pdb, .sdf, .xyz, .gzip] } + + let molecule: MolecularStructure + + /// The initializer as used by SwiftUI. + init(configuration: ReadConfiguration) throws { + guard let data = configuration.file.regularFileContents else { + throw CocoaError(.fileReadCorruptFile) + } + try self.init(data: data, contentType: configuration.contentType, filename: configuration.file.filename ?? "") + } + + /// A more general initializer that can be used in construction of previews. + /// - Parameters: + /// - data: The binary data for the molecule file, as loaded from disk. + /// - contentType: One of the supported UTTypes for molecules. + /// - filename: The string filename, used for later processing. + init(data: Data, contentType: UTType, filename: String) throws { + switch contentType { + case .pdb: + molecule = try PDBFile(data: data) + case .sdf: + molecule = try SDFFile(data: data) + case .xyz: + molecule = try XYZFile(data: data) + case .gzip: + guard filename.hasSuffix("pdb.gz") else { + throw CocoaError(.fileReadUnsupportedScheme) + } + let uncompressedData = try data.gunzipped() + molecule = try PDBFile(data: uncompressedData) + default: + throw CocoaError(.fileReadUnsupportedScheme) + } + } + + func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper { + fatalError() + } +} + +extension UTType { + static var pdb: UTType { + UTType(exportedAs: "com.sunsetlakesoftware.molecules.pdb") + } + + static var sdf: UTType { + UTType(exportedAs: "com.sunsetlakesoftware.molecules.sdf") + } + + static var xyz: UTType { + UTType(exportedAs: "com.sunsetlakesoftware.molecules.xyz") + } +} + + +extension Bundle { + /// A helper function that lets us load molecule files that have been embedded in the + /// application or test bundles. + /// - Parameters: + /// - forResource: The base filename of the molecule file to load. + /// - withExtension: The extension of the file to load. + /// - compressed: Whether the file is gzip-compressed. + func loadData(forResource: String, withExtension: String, compressed: Bool = false) throws -> Data { + guard let url = self.url(forResource: forResource, withExtension: withExtension) else { + throw PDBFileError.missingResource + } + + let loadedData = try Data(contentsOf: url) + if compressed { + return try loadedData.gunzipped() + } else { + return loadedData + } + } +} diff --git a/Molecules/FileProcessing/PDBFile.swift b/Molecules/FileProcessing/PDBFile.swift new file mode 100644 index 0000000..027bf2e --- /dev/null +++ b/Molecules/FileProcessing/PDBFile.swift @@ -0,0 +1,139 @@ +import Foundation + +enum PDBFileError: Error { + case emptyFile + case missingResource +} + +/// Parses a .pdb file in the format of the Protein Data Bank: +/// https://www.cgl.ucsf.edu/chimera/docs/UsersGuide/tutorials/pdbintro.html +struct PDBFile: MolecularStructure { + let atoms: [Atom] + let bonds: [Bond] + let centerOfMass: Coordinate + let minimumLimits: Coordinate + let maximumLimits: Coordinate + let suggestedScaleFactor: Coordinate + + init(data: Data) throws { + guard let fileContents = String(data: data, encoding: .utf8) else { + throw PDBFileError.emptyFile + } + + var parsedAtoms: [Atom] = [] + var parsedBonds: [Bond] = [] + var globalAtomLookup: [Int: Atom] = [:] + var statistics = MoleculeStatistics() + + let lines = fileContents.components(separatedBy: "\n") + for line in lines { + // Verify that we at least have a line identifier present. + guard line.count >= 6 else { continue } + + let lineIdentifier = line.prefix(6).trimmingCharacters(in: .whitespacesAndNewlines) + print("Identifier: |\(lineIdentifier)|") + switch lineIdentifier { + case "ATOM", "HETATM": + // TODO: If ATOM, insert bonds for previous residue when switching residues. + // Grab the X, Y, Z coordinates of the atom. + guard line.count >= 54 else { continue } + guard let xCoordinate = Float(line.whitespaceTrimmedString(from: 30, to: 38)), + let yCoordinate = Float(line.whitespaceTrimmedString(from: 38, to: 46)), + let zCoordinate = Float(line.whitespaceTrimmedString(from: 46, to: 54)) else { + continue + } + let atomCoordinate = Coordinate(x: xCoordinate, y: yCoordinate, z: zCoordinate) + statistics.update(using: atomCoordinate) + + // Pull the atom's element, serial number, and identifiers within a residue. + let atomSerialNumber = Int(line.whitespaceTrimmedString(from: 6, to: 12)) ?? -1 + + let elementIdentifier: String + if line.count < 78 { + elementIdentifier = line.whitespaceTrimmedString(from: 12, to: 14) + + } else { + elementIdentifier = line.whitespaceTrimmedString(from: 76, to: 78) + } + let element = Atom.Element(code: elementIdentifier) + let newAtom = Atom(element: element, location: atomCoordinate) + parsedAtoms.append(newAtom) + globalAtomLookup[atomSerialNumber] = newAtom + + print("Atom detected: \(newAtom)") + case "TER": break + case "CONECT": + // TODO: Properly handle bidirectional bonds. + guard line.count >= 11 else { continue } + guard let firstAtomSerial = Int(line.whitespaceTrimmedString(from: 6, to: 11)), + firstAtomSerial > 0, + let firstAtom = globalAtomLookup[firstAtomSerial] else { + continue + } + guard line.count >= 16 else { continue } + guard let secondAtomSerial = Int(line.whitespaceTrimmedString(from: 11, to: 16)), + secondAtomSerial > 0, + let secondAtom = globalAtomLookup[secondAtomSerial] else { + continue + } + let firstBond = Bond(strength: .single, start: firstAtom.location, end: secondAtom.location) + parsedBonds.append(firstBond) + print("Bond detected: \(firstBond)") + guard line.count >= 21 else { continue } + guard let thirdAtomSerial = Int(line.whitespaceTrimmedString(from: 16, to: 21)), + thirdAtomSerial > 0, + let thirdAtom = globalAtomLookup[thirdAtomSerial] else { + continue + } + let secondBond = Bond(strength: .single, start: firstAtom.location, end: thirdAtom.location) + parsedBonds.append(secondBond) + print("Bond detected: \(secondBond)") + guard line.count >= 26 else { continue } + guard let fourthAtomSerial = Int(line.whitespaceTrimmedString(from: 21, to: 26)), + fourthAtomSerial > 0, + let fourthAtom = globalAtomLookup[fourthAtomSerial] else { + continue + } + let thirdBond = Bond(strength: .single, start: firstAtom.location, end: fourthAtom.location) + parsedBonds.append(thirdBond) + print("Bond detected: \(thirdBond)") + guard line.count >= 31 else { continue } + guard let fifthAtomSerial = Int(line.whitespaceTrimmedString(from: 26, to: 31)), + fifthAtomSerial > 0, + let fifthAtom = globalAtomLookup[fifthAtomSerial] else { + continue + } + let fourthBond = Bond(strength: .single, start: firstAtom.location, end: fifthAtom.location) + parsedBonds.append(fourthBond) + print("Bond detected: \(fourthBond)") + + case "MODEL": break + case "ENDMDL": break + case "TITLE": break + case "COMPND": break + case "SOURCE": break + case "AUTHOR": break + case "JRNL": break + case "SEQRES": break + default: break + } + } + guard parsedAtoms.count > 0 else { + throw PDBFileError.emptyFile + } + self.atoms = parsedAtoms + self.bonds = parsedBonds + self.centerOfMass = statistics.calculatedCenterOfMass(atomCount: parsedAtoms.count) + self.minimumLimits = statistics.minimumLimits + self.maximumLimits = statistics.maximumLimits + self.suggestedScaleFactor = statistics.calculatedScaleFactor() + } +} + +extension String { + func whitespaceTrimmedString(from start: Int, to end: Int) -> String { + let startIndex = self.index(self.startIndex, offsetBy: start) + let endIndex = self.index(self.startIndex, offsetBy: end) + return self[startIndex.. 67 { + guard stillCountingAtomsInFirstStructure else { continue } + if hasReachedBonds { + stillCountingAtomsInFirstStructure = false + continue + } + hasReachedAtoms = true + guard let xCoordinate = Float(line.whitespaceTrimmedString(from: 0, to: 10)), + let yCoordinate = Float(line.whitespaceTrimmedString(from: 10, to: 20)), + let zCoordinate = Float(line.whitespaceTrimmedString(from: 20, to: 30)) else { + continue + } + let atomCoordinate = Coordinate(x: xCoordinate, y: yCoordinate, z: zCoordinate) + statistics.update(using: atomCoordinate) + let elementIdentifier = line.whitespaceTrimmedString(from: 31, to: 34) + let element = Atom.Element(code: elementIdentifier) + let newAtom = Atom(element: element, location: atomCoordinate) + parsedAtoms.append(newAtom) + } else if (line.count > 20) && hasReachedAtoms { + guard stillCountingAtomsInFirstStructure else { continue } + hasReachedBonds = true + guard let firstAtomIndex = Int(line.whitespaceTrimmedString(from: 0, to: 3)), + let secondAtomIndex = Int(line.whitespaceTrimmedString(from: 3, to: 6)), + firstAtomIndex > 0, + secondAtomIndex > 0, + firstAtomIndex <= parsedAtoms.count, + secondAtomIndex <= parsedAtoms.count else { + continue + } + // TODO: Parse single, double bonds. + let newBond = Bond( + strength: .single, + start: parsedAtoms[firstAtomIndex - 1].location, + end: parsedAtoms[secondAtomIndex - 1].location) + parsedBonds.append(newBond) + } + } + + guard parsedAtoms.count > 0 else { + throw PDBFileError.emptyFile + } + self.atoms = parsedAtoms + self.bonds = parsedBonds + self.centerOfMass = statistics.calculatedCenterOfMass(atomCount: parsedAtoms.count) + self.minimumLimits = statistics.minimumLimits + self.maximumLimits = statistics.maximumLimits + self.suggestedScaleFactor = statistics.calculatedScaleFactor() + } +} diff --git a/Molecules/FileProcessing/XYZFile.swift b/Molecules/FileProcessing/XYZFile.swift new file mode 100644 index 0000000..2730568 --- /dev/null +++ b/Molecules/FileProcessing/XYZFile.swift @@ -0,0 +1,78 @@ +import Foundation + +/// Parses an .xyz atom coordinate file. +struct XYZFile: MolecularStructure { + let atoms: [Atom] + let bonds: [Bond] = [] + let centerOfMass: Coordinate + let minimumLimits: Coordinate + let maximumLimits: Coordinate + let suggestedScaleFactor: Coordinate + + init(data: Data) throws { + guard let fileContents = String(data: data, encoding: .utf8) else { + throw PDBFileError.emptyFile + } + + var parsedAtoms: [Atom] = [] + var statistics = MoleculeStatistics() + + let lines = fileContents.components(separatedBy: "\n") + for line in lines { + let lineComponents = line.components(separatedBy: .whitespacesAndNewlines).filter { !$0.isEmpty } + guard lineComponents.count >= 4 else { continue } + + let element: Atom.Element + if let xyzCode = Int(lineComponents[0]) { + element = Atom.Element(xyzCode: xyzCode) + } else { + element = Atom.Element(code: lineComponents[0]) + } + guard element != .unknown else { continue } + + guard let xCoordinate = Float(lineComponents[1]), + let yCoordinate = Float(lineComponents[2]), + let zCoordinate = Float(lineComponents[3]) else { + continue + } + let atomCoordinate = Coordinate(x: xCoordinate, y: yCoordinate, z: zCoordinate) + statistics.update(using: atomCoordinate) + let newAtom = Atom(element: element, location: atomCoordinate) + parsedAtoms.append(newAtom) + } + guard parsedAtoms.count > 0 else { + throw PDBFileError.emptyFile + } + self.atoms = parsedAtoms + self.bonds = parsedBonds + self.centerOfMass = statistics.calculatedCenterOfMass(atomCount: parsedAtoms.count) + self.minimumLimits = statistics.minimumLimits + self.maximumLimits = statistics.maximumLimits + self.suggestedScaleFactor = statistics.calculatedScaleFactor() + } +} + +extension Atom.Element { + init(xyzCode: Int) { + switch xyzCode { + case 1: self = .hydrogen + case 6: self = .carbon + case 7: self = .nitrogen + case 8: self = .oxygen + case 9: self = .fluorine + case 11: self = .sodium + case 12: self = .magnesium + case 14: self = .silicon + case 15: self = .phosphorous + case 16: self = .sulfur + case 17: self = .chlorine + case 20: self = .calcium + case 26: self = .iron + case 30: self = .zinc + case 35: self = .bromine + case 48: self = .cadmium + case 53: self = .iodine + default: self = .unknown + } + } +} diff --git a/Molecules/Info.plist b/Molecules/Info.plist new file mode 100644 index 0000000..2b63427 --- /dev/null +++ b/Molecules/Info.plist @@ -0,0 +1,112 @@ + + + + + CFBundleDocumentTypes + + + CFBundleTypeName + PDB + CFBundleTypeRole + Viewer + LSItemContentTypes + + com.sunsetlakesoftware.molecules.pdb + + NSUbiquitousDocumentUserActivityType + $(PRODUCT_BUNDLE_IDENTIFIER).molecule-document + + + CFBundleTypeName + SDF + CFBundleTypeRole + Viewer + LSItemContentTypes + + com.sunsetlakesoftware.molecules.sdf + + NSUbiquitousDocumentUserActivityType + $(PRODUCT_BUNDLE_IDENTIFIER).molecule-document + + + CFBundleTypeName + XYZ + CFBundleTypeRole + Viewer + LSItemContentTypes + + com.sunsetlakesoftware.molecules.xyz + + NSUbiquitousDocumentUserActivityType + $(PRODUCT_BUNDLE_IDENTIFIER).molecule-document + + + UIFileSharingEnabled + + UIUserInterfaceStyle + Dark + UTExportedTypeDeclarations + + + UTTypeConformsTo + + public.plain-text + public.text + + UTTypeDescription + Protein Data Bank file + UTTypeIconFiles + + UTTypeIdentifier + com.sunsetlakesoftware.molecules.pdb + UTTypeTagSpecification + + public.filename-extension + pdb + public.mime-type + chemical/x-pdb + + + + UTTypeConformsTo + + public.plain-text + public.text + + UTTypeDescription + SDF file + UTTypeIconFiles + + UTTypeIdentifier + com.sunsetlakesoftware.molecules.sdf + UTTypeTagSpecification + + public.filename-extension + sdf + public.mime-type + chemical/x-mdl-sdfile + + + + UTTypeConformsTo + + public.plain-text + public.text + + UTTypeDescription + XYZ file + UTTypeIconFiles + + UTTypeIdentifier + com.sunsetlakesoftware.molecules.xyz + UTTypeTagSpecification + + public.filename-extension + xyz + public.mime-type + chemical/x-xyz + + + + + diff --git a/Molecules/MoleculesApp.swift b/Molecules/MoleculesApp.swift new file mode 100644 index 0000000..62133fc --- /dev/null +++ b/Molecules/MoleculesApp.swift @@ -0,0 +1,50 @@ +import SwiftUI + +@main +struct MoleculesApp: App { + init() { + copyBuiltInMoleculesIfNeeded() + // TODO: Determine previously loaded molecule, set that to display on startup + // TODO: If no previous structure, load transfer RNA + } + + var body: some Scene { + DocumentGroup(viewing: MoleculeDocument.self) { file in + MoleculeDisplayView(document: file.$document) + .toolbarRole(.automatic) + .navigationBarTitleDisplayMode(.inline) + } + } +} + +func copyBuiltInMoleculesIfNeeded() { + guard !UserDefaults.standard.bool(forKey: "copiedInitialFiles") else { return } + + do { + let documents = try FileManager.default.url( + for: .documentDirectory, + in: .userDomainMask, + appropriateFor: nil, + create: false + ) + + guard let gzippedPDBs = Bundle.main.urls(forResourcesWithExtension: ".pdb.gz", subdirectory: nil) else { + return + } + guard let unzippedPDBs = Bundle.main.urls(forResourcesWithExtension: ".pdb", subdirectory: nil) else { + return + } + guard let unzippedSDFs = Bundle.main.urls(forResourcesWithExtension: ".sdf", subdirectory: nil) else { + return + } + let builtInMolecules = gzippedPDBs + unzippedPDBs + unzippedSDFs + for builtInMolecule in builtInMolecules { + let filename = builtInMolecule.lastPathComponent + let destinationInDocuments = documents.appendingPathComponent(filename) + try FileManager.default.copyItem(at: builtInMolecule, to: destinationInDocuments) + } + } catch { + print("Error copying built-in molecules: \(error)") + } + UserDefaults.standard.setValue(true, forKey: "copiedInitialFiles") +} diff --git a/Molecules/Preview Content/Preview Assets.xcassets/Contents.json b/Molecules/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Molecules/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Molecules/Rendering/MetalRenderView.swift b/Molecules/Rendering/MetalRenderView.swift new file mode 100644 index 0000000..a067868 --- /dev/null +++ b/Molecules/Rendering/MetalRenderView.swift @@ -0,0 +1,103 @@ +import Foundation +import MetalKit +import SwiftUI + +public class MetalRenderView: MTKView { + + var molecule: MolecularStructure! + var moleculeVertexBuffer: MTLBuffer! + + public override init(frame frameRect: CGRect, device: MTLDevice?) { + super.init(frame: frameRect, device: sharedMetalRenderingDevice.device) + + commonInit() + } + + public required init(coder: NSCoder) { + super.init(coder: coder) + + commonInit() + } + + private func commonInit() { + framebufferOnly = false + autoResizeDrawable = true + + self.device = sharedMetalRenderingDevice.device + + enableSetNeedsDisplay = true + isPaused = true + } + + public override func draw(_ rect:CGRect) { + print("Draw") + guard let drawable = self.currentDrawable else { + print("No drawable") + return + } + let commandBuffer = sharedMetalRenderingDevice.commandQueue.makeCommandBuffer() + let rpd = self.currentRenderPassDescriptor + rpd?.colorAttachments[0].clearColor = MTLClearColorMake(0.0, 1.0, 0.0, 1.0) + rpd?.colorAttachments[0].loadAction = .clear + rpd?.colorAttachments[0].storeAction = .store + let re = commandBuffer?.makeRenderCommandEncoder(descriptor: rpd!) + re?.endEncoding() + commandBuffer?.present(drawable) + commandBuffer?.commit() + } +} + +struct MetalView: UIViewRepresentable { + @Binding var molecule: MolecularStructure + + init(molecule: Binding) { + self._molecule = molecule + } + + func makeUIView(context: Context) -> MetalRenderView { + let view = MetalRenderView(frame: .zero, device: nil) + view.molecule = molecule + view.initializeMoleculeBuffers() + return view + } + + func updateUIView(_ uiView: MetalRenderView, context: Context) { + print("updating view") + } + + func dismantleUIView(_ uiView: MetalRenderView, coordinator: Coordinator) { + print("dismantling view") + // TODO: Handle removing the view + } +} + +extension MetalRenderView { + func initializeMoleculeBuffers() { + let moleculeVertices: [Float] = [ + 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0 + ] + + // const device packed_float3 *position [[buffer(0)]], + let vertexBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: moleculeVertices, + length: moleculeVertices.count * MemoryLayout.size, + options: [])! + + // const device packed_float2 *inputImpostorSpaceCoordinate [[buffer(1)]], + let moleculeVertices: [Float] = [ + 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0 + ] + +// constant SphereRaytracingVertexUniform& uniform [[buffer(3)]], + print("Number of atoms to initialize: \(molecule.atoms.count)") + } + + func renderMoleculeFrame() { + + } +} diff --git a/Molecules/Rendering/MetalRenderingDevice.swift b/Molecules/Rendering/MetalRenderingDevice.swift new file mode 100644 index 0000000..9737a70 --- /dev/null +++ b/Molecules/Rendering/MetalRenderingDevice.swift @@ -0,0 +1,26 @@ +import MetalKit + +public let sharedMetalRenderingDevice = MetalRenderingDevice() + +public class MetalRenderingDevice { + public let device: MTLDevice + public let commandQueue: MTLCommandQueue + public let shaderLibrary: MTLLibrary + + init() { + guard let device = MTLCreateSystemDefaultDevice() else {fatalError("Could not create Metal Device")} + self.device = device + + guard let queue = self.device.makeCommandQueue() else {fatalError("Could not create command queue")} + self.commandQueue = queue + + do { + let frameworkBundle = Bundle(for: MetalRenderingDevice.self) + let metalLibraryPath = frameworkBundle.url(forResource: "default", withExtension: "metallib")! + + self.shaderLibrary = try device.makeLibrary(URL: metalLibraryPath) + } catch { + fatalError("Could not load library") + } + } +} diff --git a/Molecules/Rendering/MoleculeDisplayView.swift b/Molecules/Rendering/MoleculeDisplayView.swift new file mode 100644 index 0000000..49f1887 --- /dev/null +++ b/Molecules/Rendering/MoleculeDisplayView.swift @@ -0,0 +1,62 @@ +import SwiftUI + +struct MoleculeDisplayView: View { + @Binding var document: MoleculeDocument + @State private var autorotate = true + + var body: some View { + ZStack { + MetalView(molecule: .constant(document.molecule)) + VStack(alignment: .trailing) { + Spacer() + HStack { + Button { + autorotate.toggle() + } label: { + Image(systemName: autorotate ? "arrow.uturn.backward.circle.fill" : "arrow.uturn.backward.circle") + .imageScale(.large) + .foregroundColor(.accentColor) + } + + Spacer() + } + } + .padding() + } + .toolbar { + ToolbarItem(placement: .navigationBarTrailing) { + Button { + // TODO: Rendering options + print("Show rendering options") + } label: { + Image(systemName: "rotate.3d") + .imageScale(.large) + .foregroundColor(.accentColor) + } + } + ToolbarItem(placement: .navigationBarTrailing) { + NavigationLink { + MoleculeMetadataView() + } label: { + // TODO: Popover on iPad? + Image(systemName: "info.circle") + .imageScale(.large) + .foregroundColor(.accentColor) + } + } + } + } +} + +struct MoleculeDisplayView_Previews: PreviewProvider { + static var previews: some View { + let data = try! Bundle.main.loadData(forResource: "Caffeine", withExtension: "pdb") + let document = try! MoleculeDocument(data: data, contentType: .pdb, filename: "Caffeine.pdb") + NavigationStack { + MoleculeDisplayView(document: .constant(document)) + .navigationTitle("Caffeine") + .toolbarRole(.automatic) + .navigationBarTitleDisplayMode(.inline) + } + } +} diff --git a/Molecules/Rendering/MoleculeMetadataView.swift b/Molecules/Rendering/MoleculeMetadataView.swift new file mode 100644 index 0000000..4967d6d --- /dev/null +++ b/Molecules/Rendering/MoleculeMetadataView.swift @@ -0,0 +1,17 @@ +import SwiftUI + +struct MoleculeMetadataView: View { + + var body: some View { + VStack { + Text("Hello world") + } + .padding() + } +} + +struct MoleculeMetadataView_Previews: PreviewProvider { + static var previews: some View { + return MoleculeMetadataView() + } +} diff --git a/Molecules/Rendering/Shaders/SphereRaytracing.metal b/Molecules/Rendering/Shaders/SphereRaytracing.metal new file mode 100644 index 0000000..e2d68e1 --- /dev/null +++ b/Molecules/Rendering/Shaders/SphereRaytracing.metal @@ -0,0 +1,94 @@ +#include +using namespace metal; + +typedef struct +{ + float3x3 modelViewProjMatrix; + float4x4 orthographicMatrix; + float sphereRadius; + float3 translation; +} SphereRaytracingVertexUniform; + +typedef struct +{ + float3 sphereColor; + float3x3 inverseModelViewProjMatrix; + // TODO: Ambient occlusion texture and parameters. +} SphereRaytracingFragmentUniform; + +struct SphereRaytracingVertexIO +{ + float4 position [[position]]; + float2 impostorSpaceCoordinate [[user(impostorSpaceCoordinate)]]; + float3 normalizedViewCoordinate [[user(normalizedViewCoordinate)]]; + float adjustedSphereRadius; +}; + +vertex SphereRaytracingVertexIO sphereRaytracingVertex(const device packed_float3 *position [[buffer(0)]], + const device packed_float2 *inputImpostorSpaceCoordinate [[buffer(1)]], + //const device packed_float2 *ambientOcclusionTextureOffset [[buffer(2)]], + constant SphereRaytracingVertexUniform& uniform [[buffer(3)]], + uint vid [[vertex_id]]) +{ + SphereRaytracingVertexIO outputVertices; + +// ambientOcclusionTextureBase = ambientOcclusionTextureOffset; + + float3 transformedPosition = uniform.modelViewProjMatrix * (float3(position[vid]) + uniform.translation); + outputVertices.impostorSpaceCoordinate = inputImpostorSpaceCoordinate[vid]; + + transformedPosition.xy = transformedPosition.xy + inputImpostorSpaceCoordinate[vid].xy * float2(uniform.sphereRadius); + float4 transformedPosition2 = float4(transformedPosition, 1.0) * uniform.orthographicMatrix; + + float4 depthAdjustmentPoint = float4(0.0, 0.0, 0.5, 1.0) * uniform.orthographicMatrix; + float depthAdjustmentForOrthographicProjection = depthAdjustmentPoint.z / depthAdjustmentPoint.w; + outputVertices.adjustedSphereRadius = uniform.sphereRadius * depthAdjustmentForOrthographicProjection; + + outputVertices.normalizedViewCoordinate = (transformedPosition2.xyz / 2.0) + 0.5; + outputVertices.position = transformedPosition2; + return outputVertices; +} + +struct FragmentColorDepth { + half4 color [[color(0)]]; + float depth [[depth(any)]]; +}; + +constant half3 lightPosition = half3(0.312757, 0.248372, 0.916785); + +fragment FragmentColorDepth sphereRaytracingFragment(SphereRaytracingVertexIO fragmentInput [[stage_in]], +// texture2d ambientOcclusionTexture [[texture(0)]], +// texture2d precalculatedAOLookupTexture [[texture(1)]], + constant SphereRaytracingFragmentUniform& uniform [[ buffer(1) ]]) +{ + half distanceFromCenter = length(fragmentInput.impostorSpaceCoordinate); + distanceFromCenter = min(distanceFromCenter, 1.0h); + half normalizedDepth = sqrt(1.0 - distanceFromCenter * distanceFromCenter); + half alphaComponent = step(distanceFromCenter, 0.99h); + + half currentDepthValue = fragmentInput.normalizedViewCoordinate.z - fragmentInput.adjustedSphereRadius * normalizedDepth; + +// half2 lookupTextureCoordinate = ambientOcclusionLookupCoordinate(distanceFromCenter); +// +// lookupTextureCoordinate = (lookupTextureCoordinate * 2.0h) - 1.0h; +// +// half2 textureCoordinateForAOLookup = ambientOcclusionTextureBase + ambientOcclusionTexturePatchWidth * lookupTextureCoordinate; +// half ambientOcclusionIntensity = texture2D(ambientOcclusionTexture, textureCoordinateForAOLookup).r; + half ambientOcclusionIntensity = 1.0h; + + // Ambient lighting + half3 normal = half3(fragmentInput.impostorSpaceCoordinate.x, fragmentInput.impostorSpaceCoordinate.y, normalizedDepth); + half ambientLightingIntensityFactor = clamp(dot(lightPosition, normal), 0.0h, 1.0h); + + half lightingIntensity = 0.1 + ambientLightingIntensityFactor * ambientOcclusionIntensity; + half3 finalSphereColor = half3(uniform.sphereColor) * lightingIntensity; + + // Specular lighting + half specularLightingIntensityFactor = pow(ambientLightingIntensityFactor, 60.0h) * 0.6h; + finalSphereColor = finalSphereColor + ((specularLightingIntensityFactor * ambientOcclusionIntensity) * (half3(1.0h) - finalSphereColor)); + + FragmentColorDepth colorDepth; + colorDepth.color = half4(finalSphereColor * alphaComponent, 1.0); + colorDepth.depth = float(currentDepthValue + (1.0 - alphaComponent)); + return colorDepth; +} diff --git a/MoleculesIcon.png b/MoleculesIcon.png deleted file mode 100644 index 11c8cbb491bf03e395f45926a301d9775f13f427..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3840 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
  1. u{JBKBud#MWc6k{HJ|YedDvYy=6U8sff$x)973h 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 d_SKI6{{ucm{}MWrO -#import -#endif diff --git a/NSData+Gzip.h b/NSData+Gzip.h deleted file mode 100644 index faf6da2..0000000 --- a/NSData+Gzip.h +++ /dev/null @@ -1,18 +0,0 @@ -// -// NSData+Gzip.h -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 7/1/2008. -// -// This extension is adapted from the examples present at the CocoaDevWiki at http://www.cocoadev.com/index.pl?NSDataCategory - -#import - - -@interface NSData (Gzip) -- (id)initWithGzippedData: (NSData *)gzippedData; -- (NSData *) gzipDeflate; - -@end \ No newline at end of file diff --git a/NSData+Gzip.m b/NSData+Gzip.m deleted file mode 100644 index 2598353..0000000 --- a/NSData+Gzip.m +++ /dev/null @@ -1,104 +0,0 @@ -// -// NSData+Gzip.m -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 7/1/2008. -// -// This extension is adapted from the examples present at the CocoaDevWiki at http://www.cocoadev.com/index.pl?NSDataCategory - -#import "NSData+Gzip.h" -#include - -@implementation NSData (Gzip) - -- (id)initWithGzippedData: (NSData *)gzippedData; -{ - if ([gzippedData length] == 0) return nil; - - unsigned long full_length = [gzippedData length]; - unsigned long half_length = [gzippedData length] / 2; - - NSMutableData *decompressed = [[NSMutableData alloc] initWithLength:(full_length + half_length)]; - BOOL done = NO; - int status; - - z_stream strm; - strm.next_in = (Bytef *)[gzippedData bytes]; - strm.avail_in = (unsigned int)[gzippedData length]; - strm.total_out = 0; - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - - if (inflateInit2(&strm, (15+32)) != Z_OK) - { - return nil; - } - while (!done) - { - // Make sure we have enough room and reset the lengths. - if (strm.total_out >= [decompressed length]) - [decompressed increaseLengthBy: half_length]; - strm.next_out = [decompressed mutableBytes] + strm.total_out; - strm.avail_out = (unsigned int)[decompressed length] - (unsigned int)strm.total_out; - - // Inflate another chunk. - status = inflate (&strm, Z_SYNC_FLUSH); - if (status == Z_STREAM_END) done = YES; - else if (status != Z_OK) break; - } - if (inflateEnd (&strm) != Z_OK) - { - return nil; - } - - // Set real length. - [decompressed setLength: strm.total_out]; - id newObject = [self initWithBytes:[decompressed bytes] length:[decompressed length]]; - return newObject; -} - -- (NSData *)gzipDeflate -{ - if ([self length] == 0) return self; - - z_stream strm; - - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - strm.total_out = 0; - strm.next_in=(Bytef *)[self bytes]; - strm.avail_in = (unsigned int)[self length]; - - // Compresssion Levels: - // Z_NO_COMPRESSION - // Z_BEST_SPEED - // Z_BEST_COMPRESSION - // Z_DEFAULT_COMPRESSION - - if (deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (15+16), 8, Z_DEFAULT_STRATEGY) != Z_OK) return nil; - - NSMutableData *compressed = [NSMutableData dataWithLength:16384]; // 16K chunks for expansion - - do { - - if (strm.total_out >= [compressed length]) - [compressed increaseLengthBy: 16384]; - - strm.next_out = [compressed mutableBytes] + strm.total_out; - strm.avail_out = (unsigned int)[compressed length] - (unsigned int)strm.total_out; - - deflate(&strm, Z_FINISH); - - } while (strm.avail_out == 0); - - deflateEnd(&strm); - - [compressed setLength: strm.total_out]; - return [NSData dataWithData:compressed]; -} - - -@end diff --git a/README.md b/README.md new file mode 100755 index 0000000..44de3eb --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# Molecules # + +
    + +Brad Larson + +http://www.sunsetlakesoftware.com + +[@bradlarson](http://twitter.com/bradlarson) + +contact@sunsetlakesoftware.com + +## Overview ## + +Molecules is a molecular visualizer for iOS. It allows you to view three-dimensional renderings of molecules and manipulate them via touch. This application is a counterpart to [the Mac Molecules application](https://github.com/BradLarson/MoleculesMac), which uses the same rendering engine to present 3-D molecules on macOS. + +The application currently supports molecular structures in the PDB, SDF, and XYZ file formats. These files can be downloaded from a number of sources, including the [RCSB Protein Data Bank](http://www.rcsb.org/pdb) and [NCBI's PubChem](http://pubchem.ncbi.nlm.nih.gov). + +Three visualization modes are supported within the application: ball-and-stick, cylindrical, and spacefilling. These can be switched at any time. Additionally, molecules can be set to autorotate. + +Molecules is free and its source code is available under the BSD license. I feel that this can be a useful scientific and educational tool, and welcome any feedback you can provide to make it an even better program. + +## License ## + +BSD-style, with the full license available with the framework in License.txt. + +## Technical requirements ## + +- iOS 16 diff --git a/Resources-iPad/English.lproj/SLSMoleculeDownloadView.xib b/Resources-iPad/English.lproj/SLSMoleculeDownloadView.xib deleted file mode 100644 index d37599d..0000000 --- a/Resources-iPad/English.lproj/SLSMoleculeDownloadView.xib +++ /dev/null @@ -1,800 +0,0 @@ - - - - 512 - 10C540 - 760 - 1038.25 - 458.00 - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - 82 - - - YES - - - YES - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - YES - - YES - - - YES - - - - YES - - IBFilesOwner - IBIPadFramework - - - IBFirstResponder - IBIPadFramework - - - - 292 - {768, 1024} - - 3 - MQA - - 2 - - - NO - IBIPadFramework - - - - 292 - - YES - - - 292 - {{20, 0}, {280, 127}} - - NO - YES - NO - IBIPadFramework - Titleas afs;ijas;fij;weifj;woeijfwoiejf;oiwjefn iwe;foijwef;ijwe;fij;iw w;neoifnw;eifn;weoinfw;i - - 1 - MCAwIDAAA - - - 1 - 10 - 5 - 0 - - - - 292 - {{36, 155}, {247, 37}} - - NO - NO - IBIPadFramework - 0 - 0 - - Helvetica-Bold - 15 - 16 - - 1 - Read more about this molecule - Read more about this molecule - Read more about this molecule - Read more about this molecule - - 1 - MSAxIDEAA - - - 1 - MC4xOTYwNzg0MyAwLjMwOTgwMzkzIDAuNTIxNTY4NjYAA - - - 3 - MAA - - - - - -2147483356 - {{84, 341}, {150, 9}} - - NO - YES - NO - IBIPadFramework - - - - 292 - {{148, 313}, {20, 20}} - - NO - NO - NO - IBIPadFramework - YES - 2 - - - - -2147483356 - {{78, 284}, {162, 21}} - - NO - YES - NO - IBIPadFramework - Checking status... - - - 1 - 10 - 1 - - - {320, 480} - - 0.99295777082443237 - IBIPadFramework - - - - 292 - - YES - - - 274 - {320, 416} - - - 3 - MQA - - YES - YES - IBIPadFramework - YES - 1 - YES - - - - 292 - {{151, 197}, {20, 20}} - - NO - NO - NO - IBIPadFramework - YES - 2 - - - - -2147483356 - {{102, 168}, {118, 21}} - - NO - YES - NO - IBIPadFramework - Loading page... - - - 1 - 10 - - - {320, 460} - - 3 - MQA - - - NO - IBIPadFramework - - - - - YES - - - view - - - - 4 - - - - pdbCodeSearchWebView - - - - 22 - - - - downloadStatusBar - - - - 32 - - - - delegate - - - - 34 - - - - indefiniteDownloadIndicator - - - - 36 - - - - downloadStatusText - - - - 38 - - - - moleculeTitleText - - - - 46 - - - - pdbDownloadDisplayView - - - - 48 - - - - pdbInformationDisplayButton - - - - 49 - - - - pdbInformationWebView - - - - 50 - - - - showWebPageForMolecule - - - 1 - - 54 - - - - webLoadingIndicator - - - - 58 - - - - webLoadingLabel - - - - 59 - - - - - YES - - 0 - - - - - - -1 - - - File's Owner - - - -2 - - - - - 3 - - - Main download view - - - 5 - - - YES - - - - - - - - Molecule Download View - - - 6 - - - - - 13 - - - YES - - - - - - PDB Web Page - - - 16 - - - - - 30 - - - - - 35 - - - - - 37 - - - - - 8 - - - - - 56 - - - - - 57 - - - - - - - YES - - YES - -1.CustomClassName - -2.CustomClassName - 13.IBEditorWindowLastContentRect - 13.IBPluginDependency - 16.IBPluginDependency - 3.IBEditorWindowLastContentRect - 3.IBPluginDependency - 30.IBPluginDependency - 35.IBPluginDependency - 37.IBPluginDependency - 5.IBEditorWindowLastContentRect - 5.IBPluginDependency - 56.IBPluginDependency - 57.IBPluginDependency - 6.IBPluginDependency - 8.IBPluginDependency - - - YES - SLSMoleculeDownloadViewController - UIResponder - {{444, 92}, {320, 460}} - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - {{0, 274}, {320, 460}} - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - {{444, 170}, {320, 480}} - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - - YES - - - YES - - - - - YES - - - YES - - - - 59 - - - - YES - - SLSMoleculeDownloadViewController - UIViewController - - YES - - YES - cancelDownload - downloadNewProtein - returnToDetailView - showWebPageForMolecule - - - YES - id - id - id - id - - - - YES - - YES - delegate - downloadStatusBar - downloadStatusText - indefiniteDownloadIndicator - moleculeTitleText - pdbCodeSearchWebView - pdbDownloadButton - pdbDownloadDisplayView - pdbInformationDisplayButton - pdbInformationWebView - webLoadingIndicator - webLoadingLabel - - - YES - id - UIProgressView - UILabel - UIActivityIndicatorView - UILabel - UIWebView - UIButton - UIView - UIButton - UIView - UIActivityIndicatorView - UILabel - - - - IBProjectSource - SLSMoleculeDownloadViewController.h - - - - - YES - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSError.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSFileManager.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSKeyValueCoding.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSKeyValueObserving.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSKeyedArchiver.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSNetServices.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSObject.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSPort.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSRunLoop.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSStream.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSThread.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSURL.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSURLConnection.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSXMLParser.h - - - - NSObject - - IBFrameworkSource - QuartzCore.framework/Headers/CAAnimation.h - - - - NSObject - - IBFrameworkSource - QuartzCore.framework/Headers/CALayer.h - - - - NSObject - - IBFrameworkSource - UIKit.framework/Headers/UIAccessibility.h - - - - NSObject - - IBFrameworkSource - UIKit.framework/Headers/UINibLoading.h - - - - NSObject - - IBFrameworkSource - UIKit.framework/Headers/UIResponder.h - - - - UIActivityIndicatorView - UIView - - IBFrameworkSource - UIKit.framework/Headers/UIActivityIndicatorView.h - - - - UIButton - UIControl - - IBFrameworkSource - UIKit.framework/Headers/UIButton.h - - - - UIControl - UIView - - IBFrameworkSource - UIKit.framework/Headers/UIControl.h - - - - UILabel - UIView - - IBFrameworkSource - UIKit.framework/Headers/UILabel.h - - - - UIProgressView - UIView - - IBFrameworkSource - UIKit.framework/Headers/UIProgressView.h - - - - UIResponder - NSObject - - - - UISearchBar - UIView - - IBFrameworkSource - UIKit.framework/Headers/UISearchBar.h - - - - UISearchDisplayController - NSObject - - IBFrameworkSource - UIKit.framework/Headers/UISearchDisplayController.h - - - - UIView - - IBFrameworkSource - UIKit.framework/Headers/UITextField.h - - - - UIView - UIResponder - - IBFrameworkSource - UIKit.framework/Headers/UIView.h - - - - UIViewController - - IBFrameworkSource - UIKit.framework/Headers/UINavigationController.h - - - - UIViewController - - IBFrameworkSource - UIKit.framework/Headers/UITabBarController.h - - - - UIViewController - UIResponder - - IBFrameworkSource - UIKit.framework/Headers/UIViewController.h - - - - UIWebView - UIView - - IBFrameworkSource - UIKit.framework/Headers/UIWebView.h - - - - - 0 - IBIPadFramework - - com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS - - - - com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS - - - - com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 - - - YES - Molecules.xcodeproj - 3 - 82 - - diff --git a/Resources-iPad/French.lproj/SLSMoleculeDownloadView.xib b/Resources-iPad/French.lproj/SLSMoleculeDownloadView.xib deleted file mode 100644 index 2f7827a..0000000 --- a/Resources-iPad/French.lproj/SLSMoleculeDownloadView.xib +++ /dev/null @@ -1,800 +0,0 @@ - - - - 512 - 10C540 - 760 - 1038.25 - 458.00 - - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - 82 - - - YES - - - YES - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - YES - - YES - - - YES - - - - YES - - IBFilesOwner - IBIPadFramework - - - IBFirstResponder - IBIPadFramework - - - - 292 - {768, 1024} - - 3 - MQA - - 2 - - - NO - IBIPadFramework - - - - 292 - - YES - - - 292 - {{20, 0}, {280, 127}} - - NO - YES - NO - IBIPadFramework - Titleas afs;ijas;fij;weifj;woeijfwoiejf;oiwjefn iwe;foijwef;ijwe;fij;iw w;neoifnw;eifn;weoinfw;i - - 1 - MCAwIDAAA - - - 1 - 10 - 5 - 0 - - - - 292 - {{16, 156}, {288, 35}} - - NO - NO - IBIPadFramework - 0 - 0 - - Helvetica-Bold - 15 - 16 - - 1 - En savoir plus à propos de la molécule - En savoir plus à propos de la molécule - En savoir plus à propos de la molécule - En savoir plus à propos de la molécule - - 1 - MSAxIDEAA - - - 1 - MC4xOTYwNzg0MyAwLjMwOTgwMzkzIDAuNTIxNTY4NjYAA - - - 3 - MAA - - - - - -2147483356 - {{84, 341}, {150, 9}} - - NO - YES - NO - IBIPadFramework - - - - 292 - {{148, 313}, {20, 20}} - - NO - NO - NO - IBIPadFramework - YES - 2 - - - - -2147483356 - {{78, 284}, {162, 21}} - - NO - YES - NO - IBIPadFramework - Vérification du statut - - - 1 - 10 - 1 - - - {320, 480} - - 0.99295777082443237 - IBIPadFramework - - - - 292 - - YES - - - 274 - {320, 416} - - - 3 - MQA - - YES - YES - IBIPadFramework - YES - 1 - YES - - - - 292 - {{151, 197}, {20, 20}} - - NO - NO - NO - IBIPadFramework - YES - 2 - - - - -2147483356 - {{102, 168}, {118, 21}} - - NO - YES - NO - IBIPadFramework - Chargement de la page - - - 1 - 10 - - - {320, 460} - - 3 - MQA - - - NO - IBIPadFramework - - - - - YES - - - view - - - - 4 - - - - pdbCodeSearchWebView - - - - 22 - - - - downloadStatusBar - - - - 32 - - - - delegate - - - - 34 - - - - indefiniteDownloadIndicator - - - - 36 - - - - downloadStatusText - - - - 38 - - - - moleculeTitleText - - - - 46 - - - - pdbDownloadDisplayView - - - - 48 - - - - pdbInformationDisplayButton - - - - 49 - - - - pdbInformationWebView - - - - 50 - - - - showWebPageForMolecule - - - 1 - - 54 - - - - webLoadingIndicator - - - - 58 - - - - webLoadingLabel - - - - 59 - - - - - YES - - 0 - - - - - - -1 - - - File's Owner - - - -2 - - - - - 3 - - - Main download view - - - 5 - - - YES - - - - - - - - Molecule Download View - - - 6 - - - - - 13 - - - YES - - - - - - PDB Web Page - - - 16 - - - - - 30 - - - - - 35 - - - - - 37 - - - - - 8 - - - - - 56 - - - - - 57 - - - - - - - YES - - YES - -1.CustomClassName - -2.CustomClassName - 13.IBEditorWindowLastContentRect - 13.IBPluginDependency - 16.IBPluginDependency - 3.IBEditorWindowLastContentRect - 3.IBPluginDependency - 30.IBPluginDependency - 35.IBPluginDependency - 37.IBPluginDependency - 5.IBEditorWindowLastContentRect - 5.IBPluginDependency - 56.IBPluginDependency - 57.IBPluginDependency - 6.IBPluginDependency - 8.IBPluginDependency - - - YES - SLSMoleculeDownloadViewController - UIResponder - {{444, 92}, {320, 460}} - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - {{0, 274}, {320, 460}} - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - {{444, 170}, {320, 480}} - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - - YES - - - YES - - - - - YES - - - YES - - - - 59 - - - - YES - - SLSMoleculeDownloadViewController - UIViewController - - YES - - YES - cancelDownload - downloadNewProtein - returnToDetailView - showWebPageForMolecule - - - YES - id - id - id - id - - - - YES - - YES - delegate - downloadStatusBar - downloadStatusText - indefiniteDownloadIndicator - moleculeTitleText - pdbCodeSearchWebView - pdbDownloadButton - pdbDownloadDisplayView - pdbInformationDisplayButton - pdbInformationWebView - webLoadingIndicator - webLoadingLabel - - - YES - id - UIProgressView - UILabel - UIActivityIndicatorView - UILabel - UIWebView - UIButton - UIView - UIButton - UIView - UIActivityIndicatorView - UILabel - - - - IBProjectSource - SLSMoleculeDownloadViewController.h - - - - - YES - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSError.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSFileManager.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSKeyValueCoding.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSKeyValueObserving.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSKeyedArchiver.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSNetServices.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSObject.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSPort.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSRunLoop.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSStream.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSThread.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSURL.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSURLConnection.h - - - - NSObject - - IBFrameworkSource - Foundation.framework/Headers/NSXMLParser.h - - - - NSObject - - IBFrameworkSource - QuartzCore.framework/Headers/CAAnimation.h - - - - NSObject - - IBFrameworkSource - QuartzCore.framework/Headers/CALayer.h - - - - NSObject - - IBFrameworkSource - UIKit.framework/Headers/UIAccessibility.h - - - - NSObject - - IBFrameworkSource - UIKit.framework/Headers/UINibLoading.h - - - - NSObject - - IBFrameworkSource - UIKit.framework/Headers/UIResponder.h - - - - UIActivityIndicatorView - UIView - - IBFrameworkSource - UIKit.framework/Headers/UIActivityIndicatorView.h - - - - UIButton - UIControl - - IBFrameworkSource - UIKit.framework/Headers/UIButton.h - - - - UIControl - UIView - - IBFrameworkSource - UIKit.framework/Headers/UIControl.h - - - - UILabel - UIView - - IBFrameworkSource - UIKit.framework/Headers/UILabel.h - - - - UIProgressView - UIView - - IBFrameworkSource - UIKit.framework/Headers/UIProgressView.h - - - - UIResponder - NSObject - - - - UISearchBar - UIView - - IBFrameworkSource - UIKit.framework/Headers/UISearchBar.h - - - - UISearchDisplayController - NSObject - - IBFrameworkSource - UIKit.framework/Headers/UISearchDisplayController.h - - - - UIView - - IBFrameworkSource - UIKit.framework/Headers/UITextField.h - - - - UIView - UIResponder - - IBFrameworkSource - UIKit.framework/Headers/UIView.h - - - - UIViewController - - IBFrameworkSource - UIKit.framework/Headers/UINavigationController.h - - - - UIViewController - - IBFrameworkSource - UIKit.framework/Headers/UITabBarController.h - - - - UIViewController - UIResponder - - IBFrameworkSource - UIKit.framework/Headers/UIViewController.h - - - - UIWebView - UIView - - IBFrameworkSource - UIKit.framework/Headers/UIWebView.h - - - - - 0 - IBIPadFramework - - com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS - - - - com.apple.InterfaceBuilder.CocoaTouchPlugin.iPhoneOS - - - - com.apple.InterfaceBuilder.CocoaTouchPlugin.InterfaceBuilder3 - - - YES - ../Molecules.xcodeproj - 3 - 82 - - diff --git a/RotationIcon.png b/RotationIcon.png deleted file mode 100644 index 7d46bf5cd6b29799868cf99e56b9f0adf3981fa8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1539 zcmV+e2K@PnP)4Tx0C)j~RL^S@K@|QrZmG~B2wH0nvUrdpNm;9CMbtL^5n^i$+aIn^?(HA4aZWV5ov6ELTdbo0FI&wK{O>*+w4vx20?>!`FrQsdJlnHR>OPy zcd~b_n$otK2Za4V;76L-DzNVtaSB-y0*E}{p()372;bw_^6ZZ}PI-92wGS&j#91PI zKs7DSe@(bk%_Y-7gGe}(^>I=@oY#w#*Bu9GZf3^F5WP>3rn}7Ut74&?PWBFvy`A)a zPP5)V!Xd&78LdA?xQ(9mjMYElVd13a#D+Z_7&Y|xU=_C-srWU*6kiZcC!$nw*)9$7 zn6CX+@=AhmkT}X@VSsa5NKe;HZuq)~1$`#h6R+ZTR#D-3j}vF!)ZOnz+5)dI4jl{{ z44Mr{P!L4~VVJN`K!!XTF*LGrKO?IK8z<8w`3e3jI8lUGNUta*C8 zn(P`s>{pjD=7Kek#B;Fw@hxAK%$F&Q6vg9J^Xf~4by_hu-=A!MJ3Znq&n~srbFGPs zH&&aMXZ>nO`|hf|ljc?VPhR!${AbO?W8x_>CU%PFA&Hm8F7cAsOREdwU~R_;ot1_u z(ruCYB-LPGn!NQdT|ZlRy+(fw^-+`=%+gee_kY4FWHg<*4sZI8+sFJD270UUORdLHO0nA4V) z%{fwsET5CQ>B?eK%uw4yQc~9?*JVo2}ze(;aRcp*ceL#HUJSllrgm5wQKR zQu+C;QrUh^8rFfA`ftFz{YAidi-`aL0?A#nNG#|eSa7H&&8NTf z%q#c3ywto@ibF4)oUeP%|DJR1eQ`Jr*8z3FX%}Z@W#y66>6~o0+jU}VJ3Bkq5{bkQ zyLmiz1K2+Tod;YC_4#~*)oRst*Cc+R)2hI#%orx<#0$o-g>B z=`v&S!?VycW$$!4oj#)I-OnkROfojldZW>Js&4Ehn`V5HeRrbKXdK`LB__aoH*~LS z#=&6lHn;M!5@ZnZ8Q!t(}3-78Jn>>04l}&|0p|`nQ?mj`!N!$`UNnXOn@gI|fl}hE8 z*ku9e4Fm#H_~uc^G~=J21>zfu?oi*LW?x!b8u9!68z}Nk))oNY5MGn8j9jLrWh!za z4`UxjK7o7*|5fzkT2SW)`yw-+Q*9!50gEx4&5n~SL!v9954b*azz2M(a#rz{Gn82s zi^T~7Za`0)cUfgyNpoR|!y7tU^0raxl2Bty-Fp2w`srdl5fx8m8TnAGzz=n$J_4+`-wl5dJ pA0hcuAEN7CW-Pb+aQ(j>_zktV{&^zoL%RR~002ovPDHLkV1h(j?9>1N diff --git a/RotationIcon@2x.png b/RotationIcon@2x.png deleted file mode 100644 index a0c12c8fca1b58c62fb74f9880afb80eabd1b9b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2354 zcmV-23C;G2P)4Tx0C)j~RL^S@K@|QrZmG~B2wH0nvUrdpNm;9CMbtL^5n^i$+aIn^?(HA4aZWV5ov6ELTdbo0FI&wK{O>*+w4vx20?>!`FrQsdJlnHR>OPy zcd~b_n$otK2Za4V;76L-DzNVtaSB-y0*E}{p()372;bw_^6ZZ}PI-92wGS&j#91PI zKs7DSe@(bk%_Y-7gGe}(^>I=@oY#w#*Bu9GZf3^F5WP>3rn}7Ut74&?PWBFvy`A)a zPP5)V!Xd&78LdA?xQ(9mjMYElVd13a#D+Z_7&Y|xU=_C-srWU*6kiZcC!$nw*)9$7 zn6CX+@=AhmkT}X@VSsa5NKe;HZuq)~1$`#h6R+ZTR#D-3j}vF!)ZOnz+5)dI4jl{{ z44Mr{P!L4~VVJN`K!!XTF*LGrKO?IK8z<8w`3e3jI8lUGNUta*C8 zn(P`s>{pjD=7Kek#B;Fw@hxAK%$F&Q6vg9J^Xf~4by_hu-=A!MJ3Znq&n~srbFGPs zH&&aMXZ>nO`|hf|ljc?VPhR!${AbO?W8x_>CU%PFA&Hm8F7cAsOREdwU~R_;ot1_u z(ruCYB-LPGn!NQdT|ZlRy+(fw^-+`=%+gee_kY4FWHg<*4sZI8+sFJD270UUORdLHO0nA4V) z%{fwsET5CQ>B?eK%uw4yQc~9?*JVo2}ze(;aRcp*ceL#HUJSllrgm5wQKR zQu+C;QrUh^8rFfA`ftFz{YAidi-`aL010qNS#tmY79{`x79{~mQY7#I00rtvL_t(& z1?`$mOkG73$9=$i3IzO!)uNGND&WGXwJ{pCF|9F?1xQ>_K2k!dp|Kmo&e{c17iwG( zyCK0qAj(HgV_j4eV~ne&HZ^G@Q4@&J7!_i{8u|YAcb?4SzWeUIbMJ$DY2IU!VP?+E zoOAwj&YYRKkI>rM+HL8SGSUgGVBGK5OzaH?B4%Wr_ay}f0bnVBb%v&f0CB&Fm~ zC=_ikEG)d$(9kgMw;qbW?|=ce3to3vFd(WpZ_O zb%HUIqoboYTUuIHtz_TQfN}RdC>bypVd=;*kUNoQmt=jLT213&734PnJXc>hAIc&56#ItEtc zvFI1=44P#D zYS~OvZJj(V;Po3%%#8v5oMP@*IW#nM0~c;Ma^%R2l?*^VZ$%dxeK+j-gsuf#QBm;( z<6=)U>L+pW9GUD_vU|&lyq=z(y)61MJK4DVCA&VMYXRfJ1x5x4E6vZ(KiAgQmTja5 zQODKyL?V$3=#Xn8?+RY=n@wMotOYDz5-b>$6%-TFMb&o0^(t%>yI`R903t zq39v{vPhZ0t~TbF-Y; zj>#2Zf>fBJ>1z}?=*Z2`33&O4_AB{MvikV;H}y66gz1-jJ9YV9RY<*%QY`8nNZ7_9;r0yI~x_m^r+tb$EFAL#L>2*26VmnF!br`%12Kr^(u< zn9T7Zt-$l_{QUeWeuTU3mA@yzin+Wyb8~Y~;D&Fo1`jB@pCz(Jko7rMZRhbalB}$x z04rgmhim>&o3y?6M=v9I^SyirU086uJ7}G(D##{ zLfImey_-_pUvezsLYJU6L*EQ_2{;nqex@$@C#iFf{+Dt~)0P6(X(m@vqJ7Tz`1p*Q zBSgn9nSS2^6XagTie>D*t0?#vi29F1yUg?NTr$chGspD@fMl4<9Kg!W zsu^%m7OI3EZ3Db9S_$aS|62Gc3_#9zSKjkJiceteJKzL~CIah`T7cQJ>1AO8lp|pF za@tu~u8M&r-}kUCXr-KmIffivnmwLcE}eE3PCLN5XvF8B)N*Ov9u7Ka`F@vPTwEOC zTk?BYI)Z!4|6D~_glM_mmTz8xCEs65fgy1z$oF&Dr5=F|+W{QdQg4%>9Sd*M!Urby YKP#0&ubatnK>z>%07*qoM6N<$g7Mjv?*IS* diff --git a/RotationIconSelected.png b/RotationIconSelected.png deleted file mode 100644 index 02f87597e15200839a6da22bc7205892855e862a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1686 zcmV;H25I?;P)4Tx0C)j~RL^S@K@|QrZmG~B2wH0nvUrdpNm;9CMbtL^5n^i$+aIn^?(HA4aZWV5ov6ELTdbo0FI&wK{O>*+w4vx20?>!`FrQsdJlnHR>OPy zcd~b_n$otK2Za4V;76L-DzNVtaSB-y0*E}{p()372;bw_^6ZZ}PI-92wGS&j#91PI zKs7DSe@(bk%_Y-7gGe}(^>I=@oY#w#*Bu9GZf3^F5WP>3rn}7Ut74&?PWBFvy`A)a zPP5)V!Xd&78LdA?xQ(9mjMYElVd13a#D+Z_7&Y|xU=_C-srWU*6kiZcC!$nw*)9$7 zn6CX+@=AhmkT}X@VSsa5NKe;HZuq)~1$`#h6R+ZTR#D-3j}vF!)ZOnz+5)dI4jl{{ z44Mr{P!L4~VVJN`K!!XTF*LGrKO?IK8z<8w`3e3jI8lUGNUta*C8 zn(P`s>{pjD=7Kek#B;Fw@hxAK%$F&Q6vg9J^Xf~4by_hu-=A!MJ3Znq&n~srbFGPs zH&&aMXZ>nO`|hf|ljc?VPhR!${AbO?W8x_>CU%PFA&Hm8F7cAsOREdwU~R_;ot1_u z(ruCYB-LPGn!NQdT|ZlRy+(fw^-+`=%+gee_kY4FWHg<*4sZI8+sFJD270UUORdLHO0nA4V) z%{fwsET5CQ>B?eK%uw4yQc~9?*JVo2}ze(;aRcp*ceL#HUJSllrgm5wQKR zQu+C;QrUh^8rFfA`ftFz{YAidi-`aL010qNS#tmY3ljhU3ljkVnw%H_00T}*L_t(Y z4eeH4NK`=-o-=druC7w~htxwBQjwM!gcJm+54{CquF*pjR0dLp1(o8%f)rVpAyHOO zLA|tLK@dHqQ1nC6uZ9F+f;}X=C8ef!cV;@1-tF%0-P~(oFCMse=KOr~&73(i_YyKM zbCdya%5(fu?-dGA9_BK{fEfM5p)GfO>qnf9qd5ggA#(_Wd<|KZ`YzZ zyNJ=!ap^Q_kx`0@ql}iTc7caB`+UAQzw+6M-(PLCPLtO7dOQHsXU1_Zho1X~dre)n zaVy3%7*GzzXvL4*s%IxIRyL#zcRs*L2xSk2$wL5HpixNULJSE_A6IrIHJ5wzg*>$a z^U;h+Sv6(F`~b9dcbA1tUA1vJJJkLnCtY{e2VZ?&m@9I-MUZSO14ba@TGLK~DmOZjLRys5RW z=!PBMmRJR<>fX2`cN-ycHwJCRDW=*D5M`5OR1kUJFvRVlNBy_dzDM~jNxUr-6G@hpw zM8X7;=YAjO^%ss5y|u<9(~qm_nLt=XOR-8T&FXOB_iiSPo!>DCD;pR5TZ}MXkh2iS z#CUe2h}{}XYENV5=9oBk0GFH3&5H3Vj9PI3e{5nWlZ@`TxBx`Il=LZHtuFlZ>7F!w gA{nCjnE#f6pFG_VLB%V6jQ{`u07*qoM6N<$f=oXmjQ{`u diff --git a/RotationIconSelected@2x.png b/RotationIconSelected@2x.png deleted file mode 100644 index 4bca3e97a0157f3a6fadd574309ea3923a5e35c3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2519 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 diff --git a/RotationIconiPad.png b/RotationIconiPad.png deleted file mode 100644 index 6386d5515250d58cf2d78576ca63a8a7e255c9fc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1384 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 diff --git a/SLSAtomColorKeyController.h b/SLSAtomColorKeyController.h deleted file mode 100644 index d61ed40..0000000 --- a/SLSAtomColorKeyController.h +++ /dev/null @@ -1,14 +0,0 @@ -// -// SLSAtomColorKeyController.h -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// - -#import -#import -#import "SLSMolecule.h" - -@interface SLSAtomColorKeyController : UITableViewController - -@end diff --git a/SLSAtomColorKeyController.m b/SLSAtomColorKeyController.m deleted file mode 100644 index 570a1e3..0000000 --- a/SLSAtomColorKeyController.m +++ /dev/null @@ -1,249 +0,0 @@ -// -// SLSAtomColorKeyController.m -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// - -#import "SLSAtomColorKeyController.h" -#import "SLSOpenGLESRenderer.h" -#import "SLSMoleculeTableViewController.h" -#import "SLSMoleculeLibraryTableCell.h" - -typedef enum { COLORKEY_HYDROGEN, COLORKEY_CARBON, COLORKEY_NITROGEN, COLORKEY_OXYGEN, COLORKEY_FLUORINE, COLORKEY_SODIUM, COLORKEY_MAGNESIUM, COLORKEY_SILICON, COLORKEY_PHOSPHOROUS, COLORKEY_SULFUR, COLORKEY_CHLORINE, COLORKEY_CALCIUM, COLORKEY_IRON, COLORKEY_ZINC, COLORKEY_BROMINE, COLORKEY_CADMIUM, COLORKEY_IODINE, COLORKEY_UNKNOWN, COLORKEY_NUM_ATOMTYPES } SLSAtomTypeForTable; - -@interface SLSAtomColorKeyController () - -@end - -@implementation SLSAtomColorKeyController - -- (id)initWithStyle:(UITableViewStyle)style -{ - self = [super initWithStyle:style]; - if (self) { - self.title = NSLocalizedStringFromTable(@"Color Key", @"Localized", nil); - - // Custom initialization - } - return self; -} - -- (void)viewDidLoad -{ - [super viewDidLoad]; - - if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) - { - self.preferredContentSize = CGSizeMake(250.0f, 864.0f); -// self.contentSizeForViewInPopover = CGSizeMake(200.0f, 864.0f); - } - - self.tableView.backgroundColor = [UIColor blackColor]; - self.tableView.separatorColor = [UIColor clearColor]; - self.tableView.rowHeight = 48.0; -} - -- (void)viewDidUnload -{ - [super viewDidUnload]; -} - -- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation -{ - return (interfaceOrientation == UIInterfaceOrientationPortrait); -} - -#pragma mark - Table view data source - -- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView -{ - return 1; -} - -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section -{ - return COLORKEY_NUM_ATOMTYPES; -} - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath -{ - UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"ColorTableKey"]; - NSInteger index = [indexPath row]; - - if (cell == nil) - { - cell = [[SLSMoleculeLibraryTableCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"ColorTableKey"]; - -// cell.textLabel.textColor = [UIColor blackColor]; - cell.textLabel.textColor = [UIColor colorWithWhite:0.1 alpha:1.0]; - cell.selectionStyle = UITableViewCellSelectionStyleNone; - cell.accessoryType = UITableViewCellAccessoryNone; - - // Specular highlight - CAGradientLayer *newGlow = [[CAGradientLayer alloc] init]; - CGRect newGlowFrame = CGRectMake(0.0, 7.0, self.view.frame.size.width, 14.0); - newGlow.frame = newGlowFrame; - - UIColor *topColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.0f]; - UIColor *middleColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.40f]; - UIColor *bottomColor = [UIColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:0.00f]; - - newGlow.colors = [NSArray arrayWithObjects:(id)[topColor CGColor], (id)[middleColor CGColor], (id)[bottomColor CGColor], nil]; - [cell.layer insertSublayer:newGlow atIndex:10]; - - // Bottom shadow - CAGradientLayer *newShadow = [[CAGradientLayer alloc] init]; - CGRect newShadowFrame = CGRectMake(0, 24.0, self.view.frame.size.width, 24.0); - newShadow.frame = newShadowFrame; - - UIColor *topShadowColor = [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.0f]; - UIColor *middleShadowColor = [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.5f]; - - newShadow.colors = [NSArray arrayWithObjects:(id)[topShadowColor CGColor], (id)[middleShadowColor CGColor], nil]; - [cell.layer insertSublayer:newShadow atIndex:10]; - - // Top shadow - CAGradientLayer *newShadow2 = [[CAGradientLayer alloc] init]; - CGRect newShadowFrame2 = CGRectMake(0, 0.0, self.view.frame.size.width, 10.0); - newShadow2.frame = newShadowFrame2; - - UIColor *topShadowColor2 = [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.2f]; - UIColor *middleShadowColor2 = [UIColor colorWithRed:0.0 green:0.0 blue:0.0 alpha:0.0f]; - - newShadow2.colors = [NSArray arrayWithObjects:(id)[topShadowColor2 CGColor], (id)[middleShadowColor2 CGColor], nil]; - [cell.layer insertSublayer:newShadow2 atIndex:9]; - } - -// SLSAtomType atomType = CARBON; - switch (index) - { - case COLORKEY_CARBON: - { -// atomType = CARBON; - cell.textLabel.text = NSLocalizedStringFromTable(@"Carbon", @"Localized", nil); - }; break; - case COLORKEY_HYDROGEN: - { -// atomType = HYDROGEN; - cell.textLabel.text = NSLocalizedStringFromTable(@"Hydrogen", @"Localized", nil); - }; break; - case COLORKEY_NITROGEN: - { -// atomType = NITROGEN; - cell.textLabel.text = NSLocalizedStringFromTable(@"Nitrogen", @"Localized", nil); - }; break; - case COLORKEY_OXYGEN: - { -// atomType = OXYGEN; - cell.textLabel.text = NSLocalizedStringFromTable(@"Oxygen", @"Localized", nil); - }; break; - case COLORKEY_FLUORINE: - { -// atomType = FLUORINE; - cell.textLabel.text = NSLocalizedStringFromTable(@"Fluorine", @"Localized", nil); - }; break; - case COLORKEY_SODIUM: - { -// atomType = SODIUM; - cell.textLabel.text = NSLocalizedStringFromTable(@"Sodium", @"Localized", nil); - }; break; - case COLORKEY_MAGNESIUM: - { -// atomType = MAGNESIUM; - cell.textLabel.text = NSLocalizedStringFromTable(@"Magnesium", @"Localized", nil); - }; break; - case COLORKEY_SILICON: - { -// atomType = SILICON; - cell.textLabel.text = NSLocalizedStringFromTable(@"Silicon", @"Localized", nil); - }; break; - case COLORKEY_PHOSPHOROUS: - { -// atomType = PHOSPHOROUS; - cell.textLabel.text = NSLocalizedStringFromTable(@"Phosphorous", @"Localized", nil); - }; break; - case COLORKEY_SULFUR: - { -// atomType = SULFUR; - cell.textLabel.text = NSLocalizedStringFromTable(@"Sulfur", @"Localized", nil); - }; break; - case COLORKEY_CHLORINE: - { -// atomType = CHLORINE; - cell.textLabel.text = NSLocalizedStringFromTable(@"Chlorine", @"Localized", nil); - }; break; - case COLORKEY_CALCIUM: - { -// atomType = CALCIUM; - cell.textLabel.text = NSLocalizedStringFromTable(@"Calcium", @"Localized", nil); - }; break; - case COLORKEY_IRON: - { -// atomType = IRON; - cell.textLabel.text = NSLocalizedStringFromTable(@"Iron", @"Localized", nil); - }; break; - case COLORKEY_ZINC: - { -// atomType = ZINC; - cell.textLabel.text = NSLocalizedStringFromTable(@"Zinc", @"Localized", nil); - }; break; - case COLORKEY_BROMINE: - { -// atomType = BROMINE; - cell.textLabel.text = NSLocalizedStringFromTable(@"Bromine", @"Localized", nil); - }; break; - case COLORKEY_CADMIUM: - { -// atomType = CADMIUM; - cell.textLabel.text = NSLocalizedStringFromTable(@"Cadmium", @"Localized", nil); - }; break; - case COLORKEY_IODINE: - { -// atomType = IODINE; - cell.textLabel.text = NSLocalizedStringFromTable(@"Iodine", @"Localized", nil); - }; break; - default: - { -// atomType = UNKNOWN; - cell.textLabel.text = NSLocalizedStringFromTable(@"Unknown", @"Localized", nil); - }; break; - } - - return cell; -} - -- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath -{ - cell.backgroundColor = [UIColor blackColor]; - - switch ([indexPath row]) - { - case COLORKEY_CARBON: cell.backgroundColor = [UIColor colorWithRed:((float)atomProperties[CARBON].redComponent / 255.0) green:((float)atomProperties[CARBON].greenComponent / 255.0) blue:((float)atomProperties[CARBON].blueComponent / 255.0) alpha:1.0]; break; - case COLORKEY_HYDROGEN: cell.backgroundColor = [UIColor colorWithRed:((float)atomProperties[HYDROGEN].redComponent / 255.0) green:((float)atomProperties[HYDROGEN].greenComponent / 255.0) blue:((float)atomProperties[HYDROGEN].blueComponent / 255.0) alpha:1.0]; break; - case COLORKEY_NITROGEN: cell.backgroundColor = [UIColor colorWithRed:((float)atomProperties[NITROGEN].redComponent / 255.0) green:((float)atomProperties[NITROGEN].greenComponent / 255.0) blue:((float)atomProperties[NITROGEN].blueComponent / 255.0) alpha:1.0]; break; - case COLORKEY_OXYGEN: cell.backgroundColor = [UIColor colorWithRed:((float)atomProperties[OXYGEN].redComponent / 255.0) green:((float)atomProperties[OXYGEN].greenComponent / 255.0) blue:((float)atomProperties[OXYGEN].blueComponent / 255.0) alpha:1.0]; break; - case COLORKEY_FLUORINE: cell.backgroundColor = [UIColor colorWithRed:((float)atomProperties[FLUORINE].redComponent / 255.0) green:((float)atomProperties[FLUORINE].greenComponent / 255.0) blue:((float)atomProperties[FLUORINE].blueComponent / 255.0) alpha:1.0]; break; - case COLORKEY_SODIUM: cell.backgroundColor = [UIColor colorWithRed:((float)atomProperties[SODIUM].redComponent / 255.0) green:((float)atomProperties[SODIUM].greenComponent / 255.0) blue:((float)atomProperties[SODIUM].blueComponent / 255.0) alpha:1.0]; break; - case COLORKEY_MAGNESIUM: cell.backgroundColor = [UIColor colorWithRed:((float)atomProperties[MAGNESIUM].redComponent / 255.0) green:((float)atomProperties[MAGNESIUM].greenComponent / 255.0) blue:((float)atomProperties[MAGNESIUM].blueComponent / 255.0) alpha:1.0]; break; - case COLORKEY_SILICON: cell.backgroundColor = [UIColor colorWithRed:((float)atomProperties[SILICON].redComponent / 255.0) green:((float)atomProperties[SILICON].greenComponent / 255.0) blue:((float)atomProperties[SILICON].blueComponent / 255.0) alpha:1.0]; break; - case COLORKEY_PHOSPHOROUS: cell.backgroundColor = [UIColor colorWithRed:((float)atomProperties[PHOSPHOROUS].redComponent / 255.0) green:((float)atomProperties[PHOSPHOROUS].greenComponent / 255.0) blue:((float)atomProperties[PHOSPHOROUS].blueComponent / 255.0) alpha:1.0]; break; - case COLORKEY_SULFUR: cell.backgroundColor = [UIColor colorWithRed:((float)atomProperties[SULFUR].redComponent / 255.0) green:((float)atomProperties[SULFUR].greenComponent / 255.0) blue:((float)atomProperties[SULFUR].blueComponent / 255.0) alpha:1.0]; break; - case COLORKEY_CHLORINE: cell.backgroundColor = [UIColor colorWithRed:((float)atomProperties[CHLORINE].redComponent / 255.0) green:((float)atomProperties[CHLORINE].greenComponent / 255.0) blue:((float)atomProperties[CHLORINE].blueComponent / 255.0) alpha:1.0]; break; - case COLORKEY_CALCIUM: cell.backgroundColor = [UIColor colorWithRed:((float)atomProperties[CALCIUM].redComponent / 255.0) green:((float)atomProperties[CALCIUM].greenComponent / 255.0) blue:((float)atomProperties[CALCIUM].blueComponent / 255.0) alpha:1.0]; break; - case COLORKEY_IRON: cell.backgroundColor = [UIColor colorWithRed:((float)atomProperties[IRON].redComponent / 255.0) green:((float)atomProperties[IRON].greenComponent / 255.0) blue:((float)atomProperties[IRON].blueComponent / 255.0) alpha:1.0]; break; - case COLORKEY_ZINC: cell.backgroundColor = [UIColor colorWithRed:((float)atomProperties[ZINC].redComponent / 255.0) green:((float)atomProperties[ZINC].greenComponent / 255.0) blue:((float)atomProperties[ZINC].blueComponent / 255.0) alpha:1.0]; break; - case COLORKEY_BROMINE: cell.backgroundColor = [UIColor colorWithRed:((float)atomProperties[BROMINE].redComponent / 255.0) green:((float)atomProperties[BROMINE].greenComponent / 255.0) blue:((float)atomProperties[BROMINE].blueComponent / 255.0) alpha:1.0]; break; - case COLORKEY_CADMIUM: cell.backgroundColor = [UIColor colorWithRed:((float)atomProperties[CADMIUM].redComponent / 255.0) green:((float)atomProperties[CADMIUM].greenComponent / 255.0) blue:((float)atomProperties[CADMIUM].blueComponent / 255.0) alpha:1.0]; break; - case COLORKEY_IODINE: cell.backgroundColor = [UIColor colorWithRed:((float)atomProperties[IODINE].redComponent / 255.0) green:((float)atomProperties[IODINE].greenComponent / 255.0) blue:((float)atomProperties[IODINE].blueComponent / 255.0) alpha:1.0]; break; - default: cell.backgroundColor = [UIColor colorWithRed:((float)atomProperties[UNKNOWN].redComponent / 255.0) green:((float)atomProperties[UNKNOWN].greenComponent / 255.0) blue:((float)atomProperties[UNKNOWN].blueComponent / 255.0) alpha:1.0]; break; - } -} - -// Override to support conditional editing of the table view. -- (BOOL)tableView:(UITableView *)tableView canEditRowAtIndexPath:(NSIndexPath *)indexPath -{ - return NO; -} - -@end diff --git a/SLSCellTextView.h b/SLSCellTextView.h deleted file mode 100755 index 9c8b2cc..0000000 --- a/SLSCellTextView.h +++ /dev/null @@ -1,23 +0,0 @@ -// -// SLSCellTextView.h -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 6/30/2008. -// -// This class is based on Apple's example from the Recipes sample application, with only minor modifications - -#import - -// cell identifier for this custom cell -extern NSString *kSLSCellTextView_ID; - -@interface SLSCellTextView : UITableViewCell -{ - UITextView *view; -} - -@property (nonatomic, strong) UITextView *view; - -@end diff --git a/SLSCellTextView.m b/SLSCellTextView.m deleted file mode 100755 index 6282ecc..0000000 --- a/SLSCellTextView.m +++ /dev/null @@ -1,53 +0,0 @@ -// -// SLSCellTextView.m -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 6/30/2008. -// -// This class is based on Apple's example from the Recipes sample application, with only minor modifications - -#import "SLSCellTextView.h" - - -// cell identifier for this custom cell -NSString* kSLSCellTextView_ID = @"SLSCellTextViewID"; - -@implementation SLSCellTextView - -@synthesize view; - -- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)identifier -{ - self = [super initWithStyle:style reuseIdentifier:identifier]; - if (self) - { - // turn off selection use - self.selectionStyle = UITableViewCellSelectionStyleNone; - } - return self; -} - -- (void)setView:(UITextView *)inView -{ - view = inView; - [self.contentView addSubview:inView]; - [self layoutSubviews]; -} - -- (void)layoutSubviews -{ - [super layoutSubviews]; - - CGRect contentRect = [self.contentView bounds]; - - // inset the text view within the cell - self.view.frame = CGRectMake( contentRect.origin.x + 8.0f, - contentRect.origin.y + 8.0f, - contentRect.size.width - 16.0f, - contentRect.size.height - 16.0f); -} - - -@end diff --git a/SLSMolecule+PDB.h b/SLSMolecule+PDB.h deleted file mode 100644 index fa97abf..0000000 --- a/SLSMolecule+PDB.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// SLSMolecule.h -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 6/26/2008. -// -// This is the model class for the molecule object. It parses a PDB file, generates a vertex buffer object, and renders that object to the screen - -#import -#import "SLSMolecule.h" - -@interface SLSMolecule (PDB) - -- (void)createBondsForPDBResidue:(NSString *)residueType withAtomDictionary:(NSDictionary *)atomDictionary structureNumber:(NSInteger)structureNumber; -- (BOOL)readFromPDBFileToDatabase:(NSError **)error; - -@end diff --git a/SLSMolecule+PDB.m b/SLSMolecule+PDB.m deleted file mode 100644 index 6f4d2bc..0000000 --- a/SLSMolecule+PDB.m +++ /dev/null @@ -1,901 +0,0 @@ -// -// SLSMolecule.m -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 6/26/2008. -// -// This is the model class for the molecule object. It parses a PDB file, generates a vertex buffer object, and renders that object to the screen - -#import "SLSMolecule+PDB.h" -#import "NSData+Gzip.h" -#import "VCTitleCase.h" - -static NSDictionary *pdbResidueLookupTable; - -//const unsigned int PRECISION = 16; -//inline GLfixed floatToFixed (GLfloat aValue) -//{ -// return (GLfixed) (aValue * 65536.0f); -//} - -//#define GL_UNSIGNED_INT 0x1405 - -@implementation SLSMolecule (PDB) - -#pragma mark - -#pragma mark Initialization and deallocation - -- (void)createBondsForPDBResidue:(NSString *)residueType withAtomDictionary:(NSDictionary *)atomDictionary structureNumber:(NSInteger)structureNumber; -{ - if (pdbResidueLookupTable == nil) - { - // Set up the residue lookup table once for all molecules - pdbResidueLookupTable = [[NSDictionary alloc] initWithObjectsAndKeys: - [NSNumber numberWithInteger:DEOXYADENINE], @"DA", - [NSNumber numberWithInteger:DEOXYCYTOSINE], @"DC", - [NSNumber numberWithInteger:DEOXYGUANINE], @"DG", - [NSNumber numberWithInteger:DEOXYTHYMINE], @"DT", - [NSNumber numberWithInteger:ADENINE], @"A", - [NSNumber numberWithInteger:CYTOSINE], @"C", - [NSNumber numberWithInteger:GUANINE], @"G", - [NSNumber numberWithInteger:URACIL], @"U", - [NSNumber numberWithInteger:GLYCINE], @"GLY", - [NSNumber numberWithInteger:ALANINE], @"ALA", - [NSNumber numberWithInteger:VALINE], @"VAL", - [NSNumber numberWithInteger:LEUCINE], @"LEU", - [NSNumber numberWithInteger:ISOLEUCINE], @"ILE", - [NSNumber numberWithInteger:SERINE], @"SER", - [NSNumber numberWithInteger:CYSTEINE], @"CYS", - [NSNumber numberWithInteger:THREONINE], @"THR", - [NSNumber numberWithInteger:METHIONINE], @"MET", - [NSNumber numberWithInteger:PROLINE], @"PRO", - [NSNumber numberWithInteger:PHENYLALANINE], @"PHE", - [NSNumber numberWithInteger:TYROSINE], @"TYR", - [NSNumber numberWithInteger:TRYPTOPHAN], @"TRP", - [NSNumber numberWithInteger:HISTIDINE], @"HIS", - [NSNumber numberWithInteger:LYSINE], @"LYS", - [NSNumber numberWithInteger:ARGININE], @"ARG", - [NSNumber numberWithInteger:ASPARTICACID], @"ASP", - [NSNumber numberWithInteger:GLUTAMICACID], @"GLU", - [NSNumber numberWithInteger:ASPARAGINE], @"ASN", - [NSNumber numberWithInteger:GLUTAMINE], @"GLN", - nil]; - - } - SLSResidueType residueIdentifier = [[pdbResidueLookupTable objectForKey:residueType] intValue]; - - // Do the common atoms for classes of residues - switch (residueIdentifier) - { - case ADENINE: // RNA nucleotides - case CYTOSINE: - case GUANINE: - case URACIL: - { - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C2'"] endPoint:[atomDictionary objectForKey:@"O2'"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - }; - case DEOXYADENINE: // DNA nucleotides - case DEOXYCYTOSINE: - case DEOXYGUANINE: - case DEOXYTHYMINE: - { - // P -> O3' (Starts from 3' end, so no P in first nucleotide) - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"P"] endPoint:[atomDictionary objectForKey:@"OP1"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"P"] endPoint:[atomDictionary objectForKey:@"OP2"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"P"] endPoint:[atomDictionary objectForKey:@"O5'"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"O5'"] endPoint:[atomDictionary objectForKey:@"C5'"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C5'"] endPoint:[atomDictionary objectForKey:@"C4'"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C4'"] endPoint:[atomDictionary objectForKey:@"O4'"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C4'"] endPoint:[atomDictionary objectForKey:@"C3'"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C3'"] endPoint:[atomDictionary objectForKey:@"O3'"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"O4'"] endPoint:[atomDictionary objectForKey:@"C1'"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C3'"] endPoint:[atomDictionary objectForKey:@"C2'"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C2'"] endPoint:[atomDictionary objectForKey:@"C1'"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - - // Link the nucleotides together - if (self.previousTerminalAtomValue != nil) - [self addBondToDatabaseWithStartPoint:self.previousTerminalAtomValue endPoint:[atomDictionary objectForKey:@"P"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - self.previousTerminalAtomValue = [atomDictionary objectForKey:@"O3'"]; - }; break; - case GLYCINE: // Amino acids - case ALANINE: - case VALINE: - case LEUCINE: - case ISOLEUCINE: - case SERINE: - case CYSTEINE: - case THREONINE: - case METHIONINE: - case PROLINE: - case PHENYLALANINE: - case TYROSINE: - case TRYPTOPHAN: - case HISTIDINE: - case LYSINE: - case ARGININE: - case ASPARTICACID: - case GLUTAMICACID: - case ASPARAGINE: - case GLUTAMINE: - { - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"N"] endPoint:[atomDictionary objectForKey:@"CA"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CA"] endPoint:[atomDictionary objectForKey:@"C"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C"] endPoint:[atomDictionary objectForKey:@"O"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - - // Peptide bond - if (self.previousTerminalAtomValue != nil) - { - [self addBondToDatabaseWithStartPoint:self.previousTerminalAtomValue endPoint:[atomDictionary objectForKey:@"N"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - } - self.previousTerminalAtomValue = [atomDictionary objectForKey:@"C"]; - - }; break; - case WATER: - case UNKNOWNRESIDUE: - default: - { - - }; break; - } - - // Now do the residue-specific atoms - switch (residueIdentifier) - { - case ADENINE: - case DEOXYADENINE: - { - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C1'"] endPoint:[atomDictionary objectForKey:@"N9"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"N9"] endPoint:[atomDictionary objectForKey:@"C4"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C4"] endPoint:[atomDictionary objectForKey:@"N3"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"N3"] endPoint:[atomDictionary objectForKey:@"C2"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C2"] endPoint:[atomDictionary objectForKey:@"N1"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"N1"] endPoint:[atomDictionary objectForKey:@"C6"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C6"] endPoint:[atomDictionary objectForKey:@"N6"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C6"] endPoint:[atomDictionary objectForKey:@"C5"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C5"] endPoint:[atomDictionary objectForKey:@"C4"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C5"] endPoint:[atomDictionary objectForKey:@"N7"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"N7"] endPoint:[atomDictionary objectForKey:@"C8"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C8"] endPoint:[atomDictionary objectForKey:@"N9"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - }; break; - case CYTOSINE: - case DEOXYCYTOSINE: - { - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C1'"] endPoint:[atomDictionary objectForKey:@"N1"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"N1"] endPoint:[atomDictionary objectForKey:@"C2"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C2"] endPoint:[atomDictionary objectForKey:@"O2"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C2"] endPoint:[atomDictionary objectForKey:@"N3"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"N3"] endPoint:[atomDictionary objectForKey:@"C4"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C4"] endPoint:[atomDictionary objectForKey:@"N4"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C4"] endPoint:[atomDictionary objectForKey:@"C5"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C5"] endPoint:[atomDictionary objectForKey:@"C6"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C6"] endPoint:[atomDictionary objectForKey:@"N1"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - }; break; - case GUANINE: - case DEOXYGUANINE: - { - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C1'"] endPoint:[atomDictionary objectForKey:@"N9"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"N9"] endPoint:[atomDictionary objectForKey:@"C4"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C4"] endPoint:[atomDictionary objectForKey:@"N3"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"N3"] endPoint:[atomDictionary objectForKey:@"C2"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C2"] endPoint:[atomDictionary objectForKey:@"N2"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C2"] endPoint:[atomDictionary objectForKey:@"N1"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"N1"] endPoint:[atomDictionary objectForKey:@"C6"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C6"] endPoint:[atomDictionary objectForKey:@"O6"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C6"] endPoint:[atomDictionary objectForKey:@"C5"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C5"] endPoint:[atomDictionary objectForKey:@"C4"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C5"] endPoint:[atomDictionary objectForKey:@"N7"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"N7"] endPoint:[atomDictionary objectForKey:@"C8"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C8"] endPoint:[atomDictionary objectForKey:@"N9"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - }; break; - case DEOXYTHYMINE: - { - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C5"] endPoint:[atomDictionary objectForKey:@"C7"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - }; - case URACIL: - { - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C1'"] endPoint:[atomDictionary objectForKey:@"N1"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"N1"] endPoint:[atomDictionary objectForKey:@"C2"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C2"] endPoint:[atomDictionary objectForKey:@"O2"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C2"] endPoint:[atomDictionary objectForKey:@"N3"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"N3"] endPoint:[atomDictionary objectForKey:@"C4"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C4"] endPoint:[atomDictionary objectForKey:@"O4"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C4"] endPoint:[atomDictionary objectForKey:@"C5"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C5"] endPoint:[atomDictionary objectForKey:@"C6"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"C6"] endPoint:[atomDictionary objectForKey:@"N1"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - }; break; - case ALANINE: - { - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CA"] endPoint:[atomDictionary objectForKey:@"CB"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - }; break; - case VALINE: - { - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CA"] endPoint:[atomDictionary objectForKey:@"CB"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CB"] endPoint:[atomDictionary objectForKey:@"CG1"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CB"] endPoint:[atomDictionary objectForKey:@"CG2"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - }; break; - case LEUCINE: - { - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CA"] endPoint:[atomDictionary objectForKey:@"CB"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CB"] endPoint:[atomDictionary objectForKey:@"CG"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CG"] endPoint:[atomDictionary objectForKey:@"CD1"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CG"] endPoint:[atomDictionary objectForKey:@"CD2"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - }; break; - case ISOLEUCINE: - { - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CA"] endPoint:[atomDictionary objectForKey:@"CB"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CB"] endPoint:[atomDictionary objectForKey:@"CG1"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CB"] endPoint:[atomDictionary objectForKey:@"CG2"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CG1"] endPoint:[atomDictionary objectForKey:@"CD1"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - }; break; - case SERINE: - { - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CA"] endPoint:[atomDictionary objectForKey:@"CB"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CB"] endPoint:[atomDictionary objectForKey:@"OB"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - }; break; - case CYSTEINE: - { - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CA"] endPoint:[atomDictionary objectForKey:@"CB"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CB"] endPoint:[atomDictionary objectForKey:@"SG"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - }; break; - case THREONINE: - { - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CA"] endPoint:[atomDictionary objectForKey:@"CB"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CB"] endPoint:[atomDictionary objectForKey:@"OG1"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CB"] endPoint:[atomDictionary objectForKey:@"CG2"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - }; break; - case METHIONINE: - { - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CA"] endPoint:[atomDictionary objectForKey:@"CB"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CB"] endPoint:[atomDictionary objectForKey:@"CG"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CG"] endPoint:[atomDictionary objectForKey:@"SD"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"SD"] endPoint:[atomDictionary objectForKey:@"CE"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - }; break; - case PROLINE: - { - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CA"] endPoint:[atomDictionary objectForKey:@"CB"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CB"] endPoint:[atomDictionary objectForKey:@"CG"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CG"] endPoint:[atomDictionary objectForKey:@"CD"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CD"] endPoint:[atomDictionary objectForKey:@"N"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - }; break; - case PHENYLALANINE: - { - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CA"] endPoint:[atomDictionary objectForKey:@"CB"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CB"] endPoint:[atomDictionary objectForKey:@"CG"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CG"] endPoint:[atomDictionary objectForKey:@"CD1"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CG"] endPoint:[atomDictionary objectForKey:@"CD2"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CD1"] endPoint:[atomDictionary objectForKey:@"CE1"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CD2"] endPoint:[atomDictionary objectForKey:@"CE2"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CE1"] endPoint:[atomDictionary objectForKey:@"CZ"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CE2"] endPoint:[atomDictionary objectForKey:@"CZ"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - }; break; - case TYROSINE: - { - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CA"] endPoint:[atomDictionary objectForKey:@"CB"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CB"] endPoint:[atomDictionary objectForKey:@"CG"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CG"] endPoint:[atomDictionary objectForKey:@"CD1"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CG"] endPoint:[atomDictionary objectForKey:@"CD2"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CD1"] endPoint:[atomDictionary objectForKey:@"CE1"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CD2"] endPoint:[atomDictionary objectForKey:@"CE2"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CE1"] endPoint:[atomDictionary objectForKey:@"CZ"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CE2"] endPoint:[atomDictionary objectForKey:@"CZ"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CZ"] endPoint:[atomDictionary objectForKey:@"OH"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - }; break; - case TRYPTOPHAN: - { - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CA"] endPoint:[atomDictionary objectForKey:@"CB"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CB"] endPoint:[atomDictionary objectForKey:@"CG"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CG"] endPoint:[atomDictionary objectForKey:@"CD1"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CG"] endPoint:[atomDictionary objectForKey:@"CD2"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CD1"] endPoint:[atomDictionary objectForKey:@"NE1"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CD2"] endPoint:[atomDictionary objectForKey:@"CE2"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"NE1"] endPoint:[atomDictionary objectForKey:@"CE2"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CE2"] endPoint:[atomDictionary objectForKey:@"CZ2"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CZ2"] endPoint:[atomDictionary objectForKey:@"CH2"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CH2"] endPoint:[atomDictionary objectForKey:@"CZ3"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CZ3"] endPoint:[atomDictionary objectForKey:@"CE3"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CE3"] endPoint:[atomDictionary objectForKey:@"CD2"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - }; break; - case HISTIDINE: - { - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CA"] endPoint:[atomDictionary objectForKey:@"CB"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CB"] endPoint:[atomDictionary objectForKey:@"CG"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CG"] endPoint:[atomDictionary objectForKey:@"ND1"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CG"] endPoint:[atomDictionary objectForKey:@"CD2"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"ND1"] endPoint:[atomDictionary objectForKey:@"CE1"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CD2"] endPoint:[atomDictionary objectForKey:@"NE2"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CE1"] endPoint:[atomDictionary objectForKey:@"NE2"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - }; break; - case LYSINE: - { - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CA"] endPoint:[atomDictionary objectForKey:@"CB"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CB"] endPoint:[atomDictionary objectForKey:@"CG"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CG"] endPoint:[atomDictionary objectForKey:@"CD"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CD"] endPoint:[atomDictionary objectForKey:@"CE"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CE"] endPoint:[atomDictionary objectForKey:@"NZ"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - }; break; - case ARGININE: - { - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CA"] endPoint:[atomDictionary objectForKey:@"CB"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CB"] endPoint:[atomDictionary objectForKey:@"CG"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CG"] endPoint:[atomDictionary objectForKey:@"CD"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CD"] endPoint:[atomDictionary objectForKey:@"NE"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"NE"] endPoint:[atomDictionary objectForKey:@"CZ"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CZ"] endPoint:[atomDictionary objectForKey:@"NH1"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CZ"] endPoint:[atomDictionary objectForKey:@"NH2"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - }; break; - case ASPARTICACID: - { - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CA"] endPoint:[atomDictionary objectForKey:@"CB"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CB"] endPoint:[atomDictionary objectForKey:@"CG"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CG"] endPoint:[atomDictionary objectForKey:@"OD1"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CG"] endPoint:[atomDictionary objectForKey:@"OD2"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - }; break; - case GLUTAMICACID: - { - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CA"] endPoint:[atomDictionary objectForKey:@"CB"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CB"] endPoint:[atomDictionary objectForKey:@"CG"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CG"] endPoint:[atomDictionary objectForKey:@"CD"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CD"] endPoint:[atomDictionary objectForKey:@"OE1"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CD"] endPoint:[atomDictionary objectForKey:@"OE2"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - }; break; - case ASPARAGINE: - { - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CA"] endPoint:[atomDictionary objectForKey:@"CB"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CB"] endPoint:[atomDictionary objectForKey:@"CG"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CG"] endPoint:[atomDictionary objectForKey:@"OD1"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CG"] endPoint:[atomDictionary objectForKey:@"ND2"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - }; break; - case GLUTAMINE: - { - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CA"] endPoint:[atomDictionary objectForKey:@"CB"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CB"] endPoint:[atomDictionary objectForKey:@"CG"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CG"] endPoint:[atomDictionary objectForKey:@"CD"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CD"] endPoint:[atomDictionary objectForKey:@"OE1"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - [self addBondToDatabaseWithStartPoint:[atomDictionary objectForKey:@"CD"] endPoint:[atomDictionary objectForKey:@"NE2"] bondType:SINGLEBOND structureNumber:structureNumber residueKey:residueIdentifier]; - }; break; - case GLYCINE: - { - - }; break; - case WATER: - { - - }; break; - case UNKNOWNRESIDUE: - default: - { - - }; break; - } -} - -- (BOOL)readFromPDBFileToDatabase:(NSError **)error; -{ - // TODO: Add structure number - unsigned int currentStructureNumber = 1; - - NSMutableDictionary *atomCoordinates = [[NSMutableDictionary alloc] init]; - NSMutableDictionary *residueAtoms = nil; - NSString *currentResidueType = nil; - int currentResidueNumber = -1; - - stillCountingAtomsInFirstStructure = YES; - numberOfAtoms = 0; - float tallyForCenterOfMassInX = 0.0f, tallyForCenterOfMassInY = 0.0f, tallyForCenterOfMassInZ = 0.0f; - minimumXPosition = 1000.0f; - maximumXPosition = 0.0f; - minimumYPosition = 1000.0f; - maximumYPosition = 0.0f; - minimumZPosition = 1000.0f; - maximumZPosition = 0.0f; - - // Find the file, Gunzip it - // TODO: Better error handling on file missing, etc. - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); - NSString *documentsDirectory = [paths objectAtIndex:0]; - - NSData *pdbData; - - if ([[[filename pathExtension] lowercaseString] isEqualToString:@"pdb"]) // Uncompressed PDB file - { - pdbData = [[NSData alloc] initWithContentsOfFile:[documentsDirectory stringByAppendingPathComponent:filename]]; - } - else // Deal with Gzipped files - { - NSData *gzippedPDBFile = [[NSData alloc] initWithContentsOfFile:[documentsDirectory stringByAppendingPathComponent:filename]]; - pdbData = [[NSData alloc] initWithGzippedData:gzippedPDBFile]; - } - - if (pdbData == nil) - { - return NO; - } - - // Wrap all SQLite write operations in a BEGIN, COMMIT block to make writing one operation - [SLSMolecule beginTransactionWithDatabase:database]; - - // Load the file into a string for processing - NSString *pdbFileContents = [[NSString alloc] initWithData:pdbData encoding:NSASCIIStringEncoding]; - NSUInteger length = [pdbFileContents length]; - NSUInteger lineStart = 0, lineEnd = 0, contentsEnd = 0; - NSRange currentRange; - - while (lineEnd < length) - { - @autoreleasepool { - [pdbFileContents getParagraphStart:&lineStart end:&lineEnd contentsEnd:&contentsEnd forRange:NSMakeRange(lineEnd, 0)]; - currentRange = NSMakeRange(lineStart, contentsEnd - lineStart); - NSString *currentLine = [pdbFileContents substringWithRange:currentRange]; - - if ([currentLine length] >= 6) // Make sure that we at least have a line identifier present, move on the the next line otherwise - { - NSString *lineIdentifier = [[currentLine substringWithRange:NSMakeRange(0, 6)] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - if (([lineIdentifier isEqualToString:@"ATOM"]) || ([lineIdentifier isEqualToString:@"HETATM"])) - { - // Process the bonds in the previous residue if starting a new residue - if (![lineIdentifier isEqualToString:@"HETATM"]) - { - int residueNumber = [[currentLine substringWithRange:NSMakeRange(22, 5)] intValue]; - if (residueNumber != currentResidueNumber) - { - if (residueAtoms != nil) - { - [self createBondsForPDBResidue:currentResidueType withAtomDictionary:residueAtoms structureNumber:currentStructureNumber]; - residueAtoms = nil; - currentResidueType = nil; - } - residueAtoms = [[NSMutableDictionary alloc] init]; - currentResidueNumber = residueNumber; - currentResidueType = [[currentLine substringWithRange:NSMakeRange(17, 3)] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - } - } - else // Bond (-0.231000,89.028999,38.627998) - { - if (residueAtoms != nil) - { - [self createBondsForPDBResidue:currentResidueType withAtomDictionary:residueAtoms structureNumber:currentStructureNumber]; - residueAtoms = nil; - currentResidueType = nil; - } - - self.previousTerminalAtomValue = nil; - } - - SLS3DPoint atomCoordinate; - - atomCoordinate.x = [[currentLine substringWithRange:NSMakeRange(30, 8)] floatValue]; - atomCoordinate.y = [[currentLine substringWithRange:NSMakeRange(38, 8)] floatValue]; - atomCoordinate.z = [[currentLine substringWithRange:NSMakeRange(46, 8)] floatValue]; - if (stillCountingAtomsInFirstStructure) - { - tallyForCenterOfMassInX += atomCoordinate.x; - if (minimumXPosition > atomCoordinate.x) - { - minimumXPosition = atomCoordinate.x; - } - if (maximumXPosition < atomCoordinate.x) - { - maximumXPosition = atomCoordinate.x; - } - - tallyForCenterOfMassInY += atomCoordinate.y; - if (minimumYPosition > atomCoordinate.y) - { - minimumYPosition = atomCoordinate.y; - } - if (maximumYPosition < atomCoordinate.y) - { - maximumYPosition = atomCoordinate.y; - } - - tallyForCenterOfMassInZ += atomCoordinate.z; - if (minimumZPosition > atomCoordinate.z) - { - minimumZPosition = atomCoordinate.z; - } - if (maximumZPosition < atomCoordinate.z) - { - maximumZPosition = atomCoordinate.z; - } - } - - unsigned int atomSerialNumber = [[currentLine substringWithRange:NSMakeRange(6, 5)] intValue]; - [atomCoordinates setObject:[NSValue valueWithBytes:&atomCoordinate objCType:@encode(SLS3DPoint)] forKey:[NSNumber numberWithInt:atomSerialNumber]]; - if (![lineIdentifier isEqualToString:@"HETATM"]) - { - NSString *atomResidueIdentifier = [[currentLine substringWithRange:NSMakeRange(12, 4)] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - [residueAtoms setObject:[NSValue valueWithBytes:&atomCoordinate objCType:@encode(SLS3DPoint)] forKey:atomResidueIdentifier]; - } - - NSString *atomElement; - if ([currentLine length] < 78) - { - atomElement = [currentLine substringWithRange:NSMakeRange(12, 2)]; - } - else - { - atomElement = [currentLine substringWithRange:NSMakeRange(76, 2)]; - } - - SLSAtomType processedAtomType; - if ([atomElement isEqualToString:@" C"]) - { - processedAtomType = CARBON; - } - else if ([atomElement isEqualToString:@" H"]) - { - processedAtomType = HYDROGEN; - } - else if ([atomElement isEqualToString:@" O"]) - { - processedAtomType = OXYGEN; - } - else if ([atomElement isEqualToString:@" N"]) - { - processedAtomType = NITROGEN; - } - else if ([atomElement isEqualToString:@" S"]) - { - processedAtomType = SULFUR; - } - else if ([atomElement isEqualToString:@" P"]) - { - processedAtomType = PHOSPHOROUS; - } - else if ([atomElement isEqualToString:@"FE"]) - { - processedAtomType = IRON; - } - else if ([atomElement isEqualToString:@"SI"]) - { - processedAtomType = SILICON; - } - else if ([atomElement isEqualToString:@" F"]) - { - processedAtomType = FLUORINE; - } - else if ([atomElement isEqualToString:@"CL"]) - { - processedAtomType = CHLORINE; - } - else if ([atomElement isEqualToString:@"BR"]) - { - processedAtomType = BROMINE; - } - else if ([atomElement isEqualToString:@" I"]) - { - processedAtomType = IODINE; - } - else if ([atomElement isEqualToString:@"CA"]) - { - processedAtomType = CALCIUM; - } - else if ([atomElement isEqualToString:@"ZN"]) - { - processedAtomType = ZINC; - } - else if ([atomElement isEqualToString:@"CD"]) - { - processedAtomType = CADMIUM; - } - else if ([atomElement isEqualToString:@"NA"]) - { - processedAtomType = SODIUM; - } - else if ([atomElement isEqualToString:@"MG"]) - { - processedAtomType = MAGNESIUM; - } - else - { - processedAtomType = UNKNOWN; - } - - - if ([lineIdentifier isEqualToString:@"HETATM"]) - { - NSString *atomResidueIdentifier = [[currentLine substringWithRange:NSMakeRange(16, 4)] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - if ([atomResidueIdentifier isEqualToString:@"HOH"]) - { - [self addAtomToDatabase:processedAtomType atPoint:atomCoordinate structureNumber:currentStructureNumber residueKey:WATER]; - } - else - { - [self addAtomToDatabase:processedAtomType atPoint:atomCoordinate structureNumber:currentStructureNumber residueKey:UNKNOWNRESIDUE]; - } - } - else - { - [self addAtomToDatabase:processedAtomType atPoint:atomCoordinate structureNumber:currentStructureNumber residueKey:SERINE]; - } - } - else if ([lineIdentifier isEqualToString:@"TER"]) - { - // Catch the last residue of the chain - if (residueAtoms != nil) - { - [self createBondsForPDBResidue:currentResidueType withAtomDictionary:residueAtoms structureNumber:currentStructureNumber]; - residueAtoms = nil; - currentResidueType = nil; - } - - self.previousTerminalAtomValue = nil; - } - else if ([lineIdentifier isEqualToString:@"CONECT"]) - { - NSValue *startValue = nil; - int indexForFirstAtom = [[currentLine substringWithRange:NSMakeRange(6, 5)] intValue]; - if ( (indexForFirstAtom <= [atomCoordinates count]) && (indexForFirstAtom > 0) ) - { - startValue = [atomCoordinates objectForKey:[NSNumber numberWithInt:indexForFirstAtom]]; - } - if (indexForFirstAtom > 0) - { - int indexForNextAtom; - if ([currentLine length] > 15) - { - indexForNextAtom = [[currentLine substringWithRange:NSMakeRange(11, 5)] intValue]; - if ( (indexForNextAtom > 0) && (indexForNextAtom <= [atomCoordinates count]) ) - { - [self addBondToDatabaseWithStartPoint:startValue endPoint:[atomCoordinates objectForKey:[NSNumber numberWithInt:indexForNextAtom]] bondType:SINGLEBOND structureNumber:currentStructureNumber residueKey:UNKNOWNRESIDUE]; - } - } - - if ([currentLine length] > 20) - { - indexForNextAtom = [[currentLine substringWithRange:NSMakeRange(16, 5)] intValue]; - if ( (indexForNextAtom > 0) && (indexForNextAtom <= [atomCoordinates count]) ) - { - [self addBondToDatabaseWithStartPoint:startValue endPoint:[atomCoordinates objectForKey:[NSNumber numberWithInt:indexForNextAtom]] bondType:SINGLEBOND structureNumber:currentStructureNumber residueKey:UNKNOWNRESIDUE]; - } - } - - if ([currentLine length] > 25) - { - indexForNextAtom = [[currentLine substringWithRange:NSMakeRange(21, 5)] intValue]; - if ( (indexForNextAtom > 0) && (indexForNextAtom <= [atomCoordinates count]) ) - { - [self addBondToDatabaseWithStartPoint:startValue endPoint:[atomCoordinates objectForKey:[NSNumber numberWithInt:indexForNextAtom]] bondType:SINGLEBOND structureNumber:currentStructureNumber residueKey:UNKNOWNRESIDUE]; - } - } - - if ([currentLine length] > 30) - { - indexForNextAtom = [[currentLine substringWithRange:NSMakeRange(26, 5)] intValue]; - if ( (indexForNextAtom > 0) && (indexForNextAtom <= [atomCoordinates count]) ) - { - [self addBondToDatabaseWithStartPoint:startValue endPoint:[atomCoordinates objectForKey:[NSNumber numberWithInt:indexForNextAtom]] bondType:SINGLEBOND structureNumber:currentStructureNumber residueKey:UNKNOWNRESIDUE]; - } - } - } - } - else if ([lineIdentifier isEqualToString:@"MODEL"]) - { - currentStructureNumber = [[currentLine substringWithRange:NSMakeRange(12, 4)] intValue]; - if (currentStructureNumber > numberOfStructures) - { - numberOfStructures = currentStructureNumber; - } - } - else if ([lineIdentifier isEqualToString:@"ENDMDL"]) - { - stillCountingAtomsInFirstStructure = NO; - } - else if ([lineIdentifier isEqualToString:@"TITLE"]) - { - if (title == nil) - { - title = [[currentLine substringFromIndex:10] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - } - else - { - title = [title stringByAppendingFormat:@" %@", [[currentLine substringFromIndex:10] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]]; - } - } - else if ([lineIdentifier isEqualToString:@"COMPND"]) - { - if ([currentLine length] > 20) - { - NSString *compoundIdentifier = [[currentLine substringWithRange:NSMakeRange(10, 10)] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - if ([compoundIdentifier isEqualToString:@"MOLECULE:"]) - { - if (compound == nil) - { - compound = [[currentLine substringFromIndex:20] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - } - } - } - } - else if ([lineIdentifier isEqualToString:@"SOURCE"]) - { - if (source == nil) - { - source = [[currentLine substringFromIndex:10] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - } - else - { - source = [source stringByAppendingFormat:@" %@", [[currentLine substringFromIndex:10] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]]; - } - } - else if ([lineIdentifier isEqualToString:@"AUTHOR"]) - { - if (author == nil) - { - author = [[currentLine substringFromIndex:10] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - } - else - { - author = [author stringByAppendingFormat:@" %@", [[currentLine substringFromIndex:10] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]]; - } - } - else if ([lineIdentifier isEqualToString:@"JRNL"]) - { - NSString *journalIdentifier = [[currentLine substringWithRange:NSMakeRange(12, 4)] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - if ([journalIdentifier isEqualToString:@"AUTH"]) - { - if (journalAuthor == nil) - { - journalAuthor = [[currentLine substringFromIndex:18] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - } - else - { - journalAuthor = [journalAuthor stringByAppendingFormat:@" %@", [[currentLine substringFromIndex:18] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]]; - } - } - else if ([journalIdentifier isEqualToString:@"TITL"]) - { - if (journalTitle == nil) - { - journalTitle = [[currentLine substringFromIndex:18] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - } - else - { - journalTitle = [journalTitle stringByAppendingFormat:@" %@", [[currentLine substringFromIndex:18] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]]; - } - } - else if ( ([journalIdentifier isEqualToString:@"REF"]) || ([journalIdentifier isEqualToString:@"REFN"]) ) - { - if (journalReference == nil) - { - journalReference = [[currentLine substringFromIndex:18] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - } - else - { - journalReference = [journalReference stringByAppendingFormat:@" %@", [[currentLine substringFromIndex:18] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]]; - } - } - } - else if ([lineIdentifier isEqualToString:@"SEQRES"]) - { -//#warning: Fix the sequence information here, which isn't processing right - - if (sequence == nil) - { - sequence = [[currentLine substringFromIndex:14] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - } - else - { - sequence = [sequence stringByAppendingFormat:@"\n%@", [[currentLine substringFromIndex:14] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]]; - } - } - - // NSString *pdbCode, *title, *keywords, *journalReference, *sequence, *compound; - } - - } - } - - // Create bonds for the very last residue in the list - if (residueAtoms != nil) - { - [self createBondsForPDBResidue:currentResidueType withAtomDictionary:residueAtoms structureNumber:currentStructureNumber]; - } - - residueAtoms = nil; - currentResidueType = nil; - - if (numberOfAtoms > 0) - { - centerOfMassInX = tallyForCenterOfMassInX / (float)numberOfAtoms; - centerOfMassInY = tallyForCenterOfMassInY / (float)numberOfAtoms; - centerOfMassInZ = tallyForCenterOfMassInZ / (float)numberOfAtoms; - scaleAdjustmentForX = 1.5 / (maximumXPosition - minimumXPosition); - scaleAdjustmentForY = 1.5 / (maximumYPosition - minimumYPosition); - scaleAdjustmentForZ = (1.5 * 1.25) / (maximumZPosition - minimumZPosition); - if (scaleAdjustmentForY < scaleAdjustmentForX) - { - scaleAdjustmentForX = scaleAdjustmentForY; - } - if (scaleAdjustmentForZ < scaleAdjustmentForX) - { - scaleAdjustmentForX = scaleAdjustmentForZ; - } - } - - // Convert the strings to title case and strip off the ;s at the end of lines - NSCharacterSet *semicolonSet = [NSCharacterSet characterSetWithCharactersInString:@";"]; - - if (title != nil) - { - title = [title lowercaseString]; - title = [title titlecaseString]; - title = [title stringByTrimmingCharactersInSet:semicolonSet]; - } - else - { - title = [filename copy]; - } - - if (compound != nil) - { - compound = [compound lowercaseString]; - compound = [compound titlecaseString]; - compound = [compound stringByTrimmingCharactersInSet:semicolonSet]; - } - else - { - NSRange rangeUntilFirstPeriod = [filename rangeOfString:@"."]; - if (rangeUntilFirstPeriod.location == NSNotFound) - { - compound = [filename copy]; - } - else - { - compound = [[filename substringToIndex:rangeUntilFirstPeriod.location] titlecaseString]; - } - } - - [self writeMoleculeDataToDatabase]; - - if (source != nil) - { - source = [source lowercaseString]; - source = [source titlecaseString]; - source = [source stringByTrimmingCharactersInSet:semicolonSet]; - [self addMetadataToDatabase:source type:MOLECULESOURCE]; - } - - if (author != nil) - { - author = [author capitalizedString]; - author = [author stringByTrimmingCharactersInSet:semicolonSet]; - [self addMetadataToDatabase:author type:MOLECULEAUTHOR]; - } - - if (journalAuthor != nil) - { - journalAuthor = [journalAuthor capitalizedString]; - journalAuthor = [journalAuthor stringByTrimmingCharactersInSet:semicolonSet]; - [self addMetadataToDatabase:journalAuthor type:JOURNALAUTHOR]; - } - - if (journalTitle != nil) - { - journalTitle = [journalTitle lowercaseString]; - journalTitle = [journalTitle titlecaseString]; - journalTitle = [journalTitle stringByTrimmingCharactersInSet:semicolonSet]; - [self addMetadataToDatabase:journalTitle type:JOURNALTITLE]; - } - - if (journalReference != nil) - { - journalReference = [journalReference capitalizedString]; - journalReference = [journalReference stringByTrimmingCharactersInSet:semicolonSet]; - [self addMetadataToDatabase:journalReference type:JOURNALREFERENCE]; - } - - if (sequence != nil) - { - [self addMetadataToDatabase:sequence type:MOLECULESEQUENCE]; - } - - - // End the SQLite BEGIN, COMMIT block and write it out to disk - [SLSMolecule endTransactionWithDatabase:database]; - - return YES; -} - -@end diff --git a/SLSMolecule+SDF.h b/SLSMolecule+SDF.h deleted file mode 100644 index 74ab3c8..0000000 --- a/SLSMolecule+SDF.h +++ /dev/null @@ -1,15 +0,0 @@ -// -// SLSMolecule+SDF.h -// Molecules -// -// Created by Brad Larson on 5/3/2011. -// Copyright 2011 Sunset Lake Software LLC. All rights reserved. -// - -#import "SLSMolecule.h" - -@interface SLSMolecule (SDF) - -- (BOOL)readFromSDFFileToDatabase:(NSError **)error; - -@end diff --git a/SLSMolecule+SDF.m b/SLSMolecule+SDF.m deleted file mode 100644 index fc70dff..0000000 --- a/SLSMolecule+SDF.m +++ /dev/null @@ -1,251 +0,0 @@ -// -// SLSMolecule+SDF.m -// Molecules -// -// Created by Brad Larson on 5/3/2011. -// Copyright 2011 Sunset Lake Software LLC. All rights reserved. -// - -#import "SLSMolecule+SDF.h" - - -@implementation SLSMolecule (SDF) - -- (BOOL)readFromSDFFileToDatabase:(NSError **)error; -{ - NSMutableDictionary *atomCoordinates = [[NSMutableDictionary alloc] init]; - stillCountingAtomsInFirstStructure = YES; - - numberOfAtoms = 0; - numberOfBonds = 0; - numberOfStructures = 1; - - float tallyForCenterOfMassInX = 0.0f, tallyForCenterOfMassInY = 0.0f, tallyForCenterOfMassInZ = 0.0f; - minimumXPosition = 1000.0f; - maximumXPosition = 0.0f; - minimumYPosition = 1000.0f; - maximumYPosition = 0.0f; - minimumZPosition = 1000.0f; - maximumZPosition = 0.0f; - - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); - NSString *documentsDirectory = [paths objectAtIndex:0]; - - NSData *sdfData; - -// if ([[[filename pathExtension] lowercaseString] isEqualToString:@"sdf"]) // Uncompressed PDB file -// { - sdfData = [[NSData alloc] initWithContentsOfFile:[documentsDirectory stringByAppendingPathComponent:filename]]; -// } - if (sdfData == nil) - { - return NO; - } - - // Wrap all SQLite write operations in a BEGIN, COMMIT block to make writing one operation - [SLSMolecule beginTransactionWithDatabase:database]; - - // Load the file into a string for processing - NSString *sdfFileContents = [[NSString alloc] initWithData:sdfData encoding:NSASCIIStringEncoding]; - - NSRange locationOfHTMLTag = [sdfFileContents rangeOfString:@" 67) - { - // Atoms - hasReachedAtoms = YES; - - SLS3DPoint atomCoordinate; - - atomCoordinate.x = [[currentLine substringWithRange:NSMakeRange(0, 10)] floatValue]; - atomCoordinate.y = [[currentLine substringWithRange:NSMakeRange(10, 10)] floatValue]; - atomCoordinate.z = [[currentLine substringWithRange:NSMakeRange(20, 10)] floatValue]; - if (stillCountingAtomsInFirstStructure) - { - tallyForCenterOfMassInX += atomCoordinate.x; - if (minimumXPosition > atomCoordinate.x) - { - minimumXPosition = atomCoordinate.x; - } - if (maximumXPosition < atomCoordinate.x) - { - maximumXPosition = atomCoordinate.x; - } - - tallyForCenterOfMassInY += atomCoordinate.y; - if (minimumYPosition > atomCoordinate.y) - { - minimumYPosition = atomCoordinate.y; - } - if (maximumYPosition < atomCoordinate.y) - { - maximumYPosition = atomCoordinate.y; - } - - tallyForCenterOfMassInZ += atomCoordinate.z; - if (minimumZPosition > atomCoordinate.z) - { - minimumZPosition = atomCoordinate.z; - } - if (maximumZPosition < atomCoordinate.z) - { - maximumZPosition = atomCoordinate.z; - } - } - - NSString *atomElement = [[currentLine substringWithRange:NSMakeRange(31, 3)] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; - - [atomCoordinates setObject:[NSValue valueWithBytes:&atomCoordinate objCType:@encode(SLS3DPoint)] forKey:[NSNumber numberWithInteger:atomSerialNumber]]; - atomSerialNumber++; - - SLSAtomType processedAtomType; - if ([atomElement isEqualToString:@"C"]) - { - processedAtomType = CARBON; - } - else if ([atomElement isEqualToString:@"H"]) - { - processedAtomType = HYDROGEN; - } - else if ([atomElement isEqualToString:@"O"]) - { - processedAtomType = OXYGEN; - } - else if ([atomElement isEqualToString:@"N"]) - { - processedAtomType = NITROGEN; - } - else if ([atomElement isEqualToString:@"S"]) - { - processedAtomType = SULFUR; - } - else if ([atomElement isEqualToString:@"P"]) - { - processedAtomType = PHOSPHOROUS; - } - else if ([[atomElement uppercaseString] isEqualToString:@"FE"]) - { - processedAtomType = IRON; - } - else if ([[atomElement uppercaseString] isEqualToString:@"SI"]) - { - processedAtomType = SILICON; - } - else if ([[atomElement uppercaseString] isEqualToString:@"F"]) - { - processedAtomType = FLUORINE; - } - else if ([[atomElement uppercaseString] isEqualToString:@"CL"]) - { - processedAtomType = CHLORINE; - } - else if ([[atomElement uppercaseString] isEqualToString:@"BR"]) - { - processedAtomType = BROMINE; - } - else if ([[atomElement uppercaseString] isEqualToString:@"I"]) - { - processedAtomType = IODINE; - } - else if ([[atomElement uppercaseString] isEqualToString:@"CA"]) - { - processedAtomType = CALCIUM; - } - else if ([[atomElement uppercaseString] isEqualToString:@"ZN"]) - { - processedAtomType = ZINC; - } - else if ([[atomElement uppercaseString] isEqualToString:@"CD"]) - { - processedAtomType = CADMIUM; - } - else if ([[atomElement uppercaseString] isEqualToString:@"NA"]) - { - processedAtomType = SODIUM; - } - else if ([[atomElement uppercaseString] isEqualToString:@"MG"]) - { - processedAtomType = MAGNESIUM; - } - else - { - processedAtomType = UNKNOWN; - } - - - [self addAtomToDatabase:processedAtomType atPoint:atomCoordinate structureNumber:1 residueKey:UNKNOWNRESIDUE]; - } - else if (([currentLine length] > 20) && (hasReachedAtoms)) - { - hasReachedBonds = YES; - // Bonds - - NSUInteger indexForFirstAtom = [[currentLine substringWithRange:NSMakeRange(0, 3)] intValue]; - NSUInteger indexForSecondAtom = [[currentLine substringWithRange:NSMakeRange(3, 3)] intValue]; - - NSValue *startValue = [atomCoordinates objectForKey:[NSNumber numberWithInteger:indexForFirstAtom]]; - NSValue *endValue = [atomCoordinates objectForKey:[NSNumber numberWithInteger:indexForSecondAtom]]; - - [self addBondToDatabaseWithStartPoint:startValue endPoint:endValue bondType:SINGLEBOND structureNumber:1 residueKey:UNKNOWNRESIDUE]; - } - else if (([currentLine length] < 15) && (hasReachedBonds)) - { - lineEnd = length + 1; - break; - } - } - - if (numberOfAtoms > 0) - { - centerOfMassInX = tallyForCenterOfMassInX / (float)numberOfAtoms; - centerOfMassInY = tallyForCenterOfMassInY / (float)numberOfAtoms; - centerOfMassInZ = tallyForCenterOfMassInZ / (float)numberOfAtoms; - scaleAdjustmentForX = 1.5 / (maximumXPosition - minimumXPosition); - scaleAdjustmentForY = 1.5 / (maximumYPosition - minimumYPosition); - scaleAdjustmentForZ = (1.5 * 1.25) / (maximumZPosition - minimumZPosition); - if (scaleAdjustmentForY < scaleAdjustmentForX) - { - scaleAdjustmentForX = scaleAdjustmentForY; - } - if (scaleAdjustmentForZ < scaleAdjustmentForX) - { - scaleAdjustmentForX = scaleAdjustmentForZ; - } - } - - - if (title == nil) - { - title = [filename copy]; - } - - compound = [title copy]; - - [self writeMoleculeDataToDatabase]; - - // End the SQLite BEGIN, COMMIT block and write it out to disk - [SLSMolecule endTransactionWithDatabase:database]; - - return YES; -} - -@end diff --git a/SLSMolecule.h b/SLSMolecule.h deleted file mode 100644 index 4eda2cb..0000000 --- a/SLSMolecule.h +++ /dev/null @@ -1,108 +0,0 @@ -// -// SLSMolecule.h -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 6/26/2008. -// -// This is the model class for the molecule object. It parses a PDB file, generates a vertex buffer object, and renders that object to the screen - -#import -#import -#import -#import -#import - -extern NSString *const kSLSMoleculeRenderingStartedNotification; -extern NSString *const kSLSMoleculeRenderingUpdateNotification; -extern NSString *const kSLSMoleculeRenderingEndedNotification; - -@class SLSOpenGLESRenderer; - -// TODO: Convert enum to elemental number -typedef enum { CARBON, HYDROGEN, OXYGEN, NITROGEN, SULFUR, PHOSPHOROUS, IRON, UNKNOWN, SILICON, FLUORINE, CHLORINE, BROMINE, IODINE, CALCIUM, ZINC, CADMIUM, SODIUM, MAGNESIUM, NUM_ATOMTYPES } SLSAtomType; -typedef enum { BALLANDSTICK, SPACEFILLING, CYLINDRICAL, } SLSVisualizationType; -typedef enum { UNKNOWNRESIDUE, DEOXYADENINE, DEOXYCYTOSINE, DEOXYGUANINE, DEOXYTHYMINE, ADENINE, CYTOSINE, GUANINE, URACIL, GLYCINE, ALANINE, VALINE, - LEUCINE, ISOLEUCINE, SERINE, CYSTEINE, THREONINE, METHIONINE, PROLINE, PHENYLALANINE, TYROSINE, TRYPTOPHAN, HISTIDINE, - LYSINE, ARGININE, ASPARTICACID, GLUTAMICACID, ASPARAGINE, GLUTAMINE, WATER, NUM_RESIDUETYPES } SLSResidueType; -typedef enum { MOLECULESOURCE, MOLECULEAUTHOR, JOURNALAUTHOR, JOURNALTITLE, JOURNALREFERENCE, MOLECULESEQUENCE } SLSMetadataType; -typedef enum { SINGLEBOND, DOUBLEBOND, TRIPLEBOND } SLSBondType; - -typedef struct { - GLfloat x; - GLfloat y; - GLfloat z; -} SLS3DPoint; - -@interface SLSMolecule : NSObject -{ - // Metadata from the Protein Data Bank - unsigned int numberOfAtoms, numberOfBonds, numberOfStructures; - NSString *filename, *filenameWithoutExtension, *title, *keywords, *journalAuthor, *journalTitle, *journalReference, *sequence, *compound, *source, *author; - - // Status of the molecule - BOOL isBeingDisplayed, isDoneRendering, isRenderingCancelled; - SLSVisualizationType currentVisualizationType; - unsigned int numberOfStructureBeingDisplayed; - unsigned int totalNumberOfFeaturesToRender, currentFeatureBeingRendered; - BOOL stillCountingAtomsInFirstStructure; - - // A holder for rendering connecting bonds - NSValue *previousTerminalAtomValue; - BOOL reverseChainDirection; - - // Database values - sqlite3 *database; - BOOL isPopulatedFromDatabase; - NSInteger databaseKey; - - // Molecule properties for scaling and translation - float centerOfMassInX, centerOfMassInY, centerOfMassInZ; - float minimumXPosition, maximumXPosition, minimumYPosition, maximumYPosition, minimumZPosition, maximumZPosition; - float scaleAdjustmentForX, scaleAdjustmentForY, scaleAdjustmentForZ; - - SLSOpenGLESRenderer *currentRenderer; -} - -@property (readonly) float centerOfMassInX, centerOfMassInY, centerOfMassInZ; -@property (readonly) NSString *filename, *filenameWithoutExtension, *title, *keywords, *journalAuthor, *journalTitle, *journalReference, *sequence, *compound, *source, *author; -@property (readwrite, nonatomic) BOOL isBeingDisplayed, isRenderingCancelled; -@property (readonly) BOOL isDoneRendering; -@property (readonly) unsigned int numberOfAtoms, numberOfStructures; -@property (readwrite, strong) NSValue *previousTerminalAtomValue; -@property (readwrite, nonatomic) SLSVisualizationType currentVisualizationType; -@property (readwrite) unsigned int numberOfStructureBeingDisplayed; - -- (id)initWithFilename:(NSString *)newFilename database:(sqlite3 *)newDatabase title:(NSString *)newTitle; -- (id)initWithSQLStatement:(sqlite3_stmt *)moleculeRetrievalStatement database:(sqlite3 *)newDatabase; -- (void)deleteMolecule; - -+ (BOOL)isFiletypeSupportedForFile:(NSString *)filePath; -+ (void)setBondColor:(GLubyte *)bondColor forResidueType:(SLSResidueType)residueType; - -// Database methods -+ (BOOL)beginTransactionWithDatabase:(sqlite3 *)database; -+ (BOOL)endTransactionWithDatabase:(sqlite3 *)database; -+ (void)finalizeStatements; -- (void)writeMoleculeDataToDatabase; -- (void)addMetadataToDatabase:(NSString *)metadata type:(SLSMetadataType)metadataType; -- (NSInteger)addAtomToDatabase:(SLSAtomType)atomType atPoint:(SLS3DPoint)newPoint structureNumber:(NSInteger)structureNumber residueKey:(SLSResidueType)residueKey; -- (void)addBondToDatabaseWithStartPoint:(NSValue *)startValue endPoint:(NSValue *)endValue bondType:(SLSBondType)bondType structureNumber:(NSInteger)structureNumber residueKey:(NSInteger)residueKey; -- (void)readMetadataFromDatabaseIfNecessary; -- (void)deleteMoleculeDataFromDatabase; -- (NSInteger)countAtomsForFirstStructure; -- (NSInteger)countBondsForFirstStructure; - -// Status notification methods -- (void)showStatusIndicator; -- (void)updateStatusIndicator; -- (void)hideStatusIndicator; - -// Rendering -- (void)switchToDefaultVisualizationMode; -- (BOOL)renderMolecule:(SLSOpenGLESRenderer *)openGLESRenderer; -- (void)readAndRenderAtoms:(SLSOpenGLESRenderer *)openGLESRenderer; -- (void)readAndRenderBonds:(SLSOpenGLESRenderer *)openGLESRenderer; - -@end diff --git a/SLSMolecule.m b/SLSMolecule.m deleted file mode 100644 index 030c512..0000000 --- a/SLSMolecule.m +++ /dev/null @@ -1,1058 +0,0 @@ -// -// SLSMolecule.m -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 6/26/2008. -// -// This is the model class for the molecule object. It parses a PDB file, generates a vertex buffer object, and renders that object to the screen - -#import "SLSMolecule.h" -// Filetypes -#import "SLSMolecule+PDB.h" -#import "SLSMolecule+SDF.h" - -#import "SLSOpenGLESRenderer.h" -#import "SLSOpenGLES20Renderer.h" - -NSString *const kSLSMoleculeRenderingStartedNotification = @"MoleculeRenderingStarted"; -NSString *const kSLSMoleculeRenderingUpdateNotification = @"MoleculeRenderingUpdate"; -NSString *const kSLSMoleculeRenderingEndedNotification = @"MoleculeRenderingEnded"; - -#define BOND_LENGTH_LIMIT 3.0f - -static sqlite3_stmt *insertMoleculeSQLStatement = nil; -static sqlite3_stmt *insertMetadataSQLStatement = nil; -static sqlite3_stmt *insertAtomSQLStatement = nil; -static sqlite3_stmt *insertBondSQLStatement = nil; - -static sqlite3_stmt *updateMoleculeSQLStatement = nil; - -static sqlite3_stmt *retrieveMoleculeSQLStatement = nil; -static sqlite3_stmt *retrieveMetadataSQLStatement = nil; -static sqlite3_stmt *retrieveAtomSQLStatement = nil; -static sqlite3_stmt *retrieveBondSQLStatement = nil; - -static sqlite3_stmt *deleteMoleculeSQLStatement = nil; -static sqlite3_stmt *deleteMetadataSQLStatement = nil; -static sqlite3_stmt *deleteAtomSQLStatement = nil; -static sqlite3_stmt *deleteBondSQLStatement = nil; - - -@implementation SLSMolecule - -#pragma mark - -#pragma mark Initialization and deallocation - -- (id)init; -{ - if (!(self = [super init])) - { - return nil; - } - - numberOfStructures = 1; - numberOfStructureBeingDisplayed = 1; - - filename = nil; - filenameWithoutExtension = nil; - title = nil; - keywords = nil; - sequence = nil; - compound = nil; - source = nil; - journalTitle = nil; - journalAuthor = nil; - journalReference = nil; - author = nil; - - isBeingDisplayed = NO; - isRenderingCancelled = NO; - - previousTerminalAtomValue = nil; - reverseChainDirection = NO; - currentVisualizationType = BALLANDSTICK; - - isPopulatedFromDatabase = NO; - databaseKey = 0; - isDoneRendering = NO; - - stillCountingAtomsInFirstStructure = YES; - return self; -} - -- (id)initWithFilename:(NSString *)newFilename database:(sqlite3 *)newDatabase title:(NSString *)newTitle; -{ - if (!(self = [self init])) - { - return nil; - } - - database = newDatabase; - filename = [newFilename copy]; - title = [newTitle copy]; - - NSRange rangeUntilFirstPeriod = [filename rangeOfString:@"."]; - if (rangeUntilFirstPeriod.location == NSNotFound) - { - filenameWithoutExtension = filename; - } - else - { - filenameWithoutExtension = [filename substringToIndex:rangeUntilFirstPeriod.location]; - } - - if (insertMoleculeSQLStatement == nil) - { - static char *sql = "INSERT INTO molecules (filename) VALUES(?)"; - if (sqlite3_prepare_v2(database, sql, -1, &insertMoleculeSQLStatement, NULL) != SQLITE_OK) - { - NSAssert1(0,NSLocalizedStringFromTable(@"Error: failed to prepare statement with message '%s'.", @"Localized", nil), sqlite3_errmsg(database)); - } - } - // Bind the query variables. - sqlite3_bind_text(insertMoleculeSQLStatement, 1, [filename UTF8String], -1, SQLITE_TRANSIENT); - int success = sqlite3_step(insertMoleculeSQLStatement); - // Because we want to reuse the statement, we "reset" it instead of "finalizing" it. - sqlite3_reset(insertMoleculeSQLStatement); - if (success != SQLITE_ERROR) - { - // SQLite provides a method which retrieves the value of the most recently auto-generated primary key sequence - // in the database. To access this functionality, the table should have a column declared of type - // "INTEGER PRIMARY KEY" - databaseKey = sqlite3_last_insert_rowid(database); - } - - NSError *error = nil; - - if ([[[filename pathExtension] lowercaseString] isEqualToString:@"sdf"]) - { - if (![self readFromSDFFileToDatabase:&error]) - { - return nil; - } - } - else - { - if (![self readFromPDBFileToDatabase:&error]) - { - return nil; - } - } - - return self; -} - -- (id)initWithSQLStatement:(sqlite3_stmt *)moleculeRetrievalStatement database:(sqlite3 *)newDatabase; -{ - if (!(self = [self init])) - { - return nil; - } - - database = newDatabase; - - // Retrieve molecule information from the line of the SELECT statement - //(id,filename,title,compound,format,atom_count,structure_count, centerofmass_x,centerofmass_y,centerofmass_z,minimumposition_x,minimumposition_y,minimumposition_z,maximumposition_x,maximumposition_y,maximumposition_z) - databaseKey = sqlite3_column_int(moleculeRetrievalStatement, 0); - char *stringResult = (char *)sqlite3_column_text(moleculeRetrievalStatement, 1); - NSString *sqlString = (stringResult) ? [NSString stringWithUTF8String:stringResult] : @""; - filename = [sqlString stringByReplacingOccurrencesOfString:@"''" withString:@"'"]; - - NSRange rangeUntilFirstPeriod = [filename rangeOfString:@"."]; - if (rangeUntilFirstPeriod.location == NSNotFound) - filenameWithoutExtension = filename; - else - filenameWithoutExtension = [filename substringToIndex:rangeUntilFirstPeriod.location]; - - stringResult = (char *)sqlite3_column_text(moleculeRetrievalStatement, 2); - sqlString = (stringResult) ? [NSString stringWithUTF8String:stringResult] : @""; - title = [sqlString stringByReplacingOccurrencesOfString:@"''" withString:@"'"]; - - stringResult = (char *)sqlite3_column_text(moleculeRetrievalStatement, 3); - sqlString = (stringResult) ? [NSString stringWithUTF8String:stringResult] : @""; - compound = [sqlString stringByReplacingOccurrencesOfString:@"''" withString:@"'"]; - - // Ignore the format for now - // stringResult = (char *)sqlite3_column_text(moleculeRetrievalStatement, 4); - // format = (stringResult) ? [[NSString alloc] initWithUTF8String:stringResult] : [[NSString alloc] initWithString:@""]; - numberOfAtoms = sqlite3_column_int(moleculeRetrievalStatement, 5); - numberOfBonds = sqlite3_column_int(moleculeRetrievalStatement, 6); - numberOfStructures = sqlite3_column_int(moleculeRetrievalStatement, 7); - centerOfMassInX = sqlite3_column_double(moleculeRetrievalStatement, 8); - centerOfMassInY = sqlite3_column_double(moleculeRetrievalStatement, 9); - centerOfMassInZ = sqlite3_column_double(moleculeRetrievalStatement, 10); - minimumXPosition = sqlite3_column_double(moleculeRetrievalStatement, 11); - minimumYPosition = sqlite3_column_double(moleculeRetrievalStatement, 12); - minimumZPosition = sqlite3_column_double(moleculeRetrievalStatement, 13); - maximumXPosition = sqlite3_column_double(moleculeRetrievalStatement, 14); - maximumYPosition = sqlite3_column_double(moleculeRetrievalStatement, 15); - maximumZPosition = sqlite3_column_double(moleculeRetrievalStatement, 16); - - scaleAdjustmentForX = 1.5 / (maximumXPosition - minimumXPosition); - scaleAdjustmentForY = 1.5 / (maximumYPosition - minimumYPosition); - scaleAdjustmentForZ = (1.5 * 1.25) / (maximumZPosition - minimumZPosition); - if (scaleAdjustmentForY < scaleAdjustmentForX) - scaleAdjustmentForX = scaleAdjustmentForY; - if (scaleAdjustmentForZ < scaleAdjustmentForX) - scaleAdjustmentForX = scaleAdjustmentForZ; - - return self; -} - -- (void)deleteMolecule; -{ - [self deleteMoleculeDataFromDatabase]; - - // Remove the file from disk - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); - NSString *documentsDirectory = [paths objectAtIndex:0]; - - NSError *error = nil; - if (![[NSFileManager defaultManager] removeItemAtPath:[documentsDirectory stringByAppendingPathComponent:filename] error:&error]) - { - UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedStringFromTable(@"Could not delete file", @"Localized", nil) message:[error localizedDescription] - delegate:self cancelButtonTitle:NSLocalizedStringFromTable(@"OK", @"Localized", nil) otherButtonTitles:nil, nil]; - [alert show]; - return; - } -} - - -+ (BOOL)isFiletypeSupportedForFile:(NSString *)filePath; -{ - // TODO: Make the categories perform a selector to determine whether this file is supported - if ([[[filePath pathExtension] lowercaseString] isEqualToString:@"pdb"]) // Uncompressed PDB file - { - return YES; - } - else if ([[[filePath pathExtension] lowercaseString] isEqualToString:@"gz"]) // Gzipped PDB file - { - return YES; - } - else - { - return NO; - } -} - -#pragma mark - -#pragma mark Molecule 3-D geometry generation -+ (void)setBondColor:(GLubyte *)bondColor forResidueType:(SLSResidueType)residueType; -{ - // Bonds are grey by default - bondColor[0] = 150; - bondColor[1] = 150; - bondColor[2] = 150; - bondColor[3] = 255; - - switch (residueType) - { - case ADENINE: - case DEOXYADENINE: - { - bondColor[0] = 160; - bondColor[1] = 160; - bondColor[2] = 255; - }; break; - case CYTOSINE: - case DEOXYCYTOSINE: - { - bondColor[0] = 255; - bondColor[1] = 140; - bondColor[2] = 75; - }; break; - case GUANINE: - case DEOXYGUANINE: - { - bondColor[0] = 255; - bondColor[1] = 112; - bondColor[2] = 112; - }; break; - case URACIL: - { - bondColor[0] = 255; - bondColor[1] = 128; - bondColor[2] = 128; - }; break; - case DEOXYTHYMINE: - { - bondColor[0] = 160; - bondColor[1] = 255; - bondColor[2] = 160; - }; break; - case GLYCINE: - { - bondColor[0] = 235; - bondColor[1] = 235; - bondColor[2] = 235; - }; break; - case ALANINE: - { - bondColor[0] = 200; - bondColor[1] = 200; - bondColor[2] = 200; - }; break; - case VALINE: - { - bondColor[0] = 15; - bondColor[1] = 130; - bondColor[2] = 15; - }; break; - case LEUCINE: - { - bondColor[0] = 15; - bondColor[1] = 130; - bondColor[2] = 15; - }; break; - case ISOLEUCINE: - { - bondColor[0] = 15; - bondColor[1] = 130; - bondColor[2] = 15; - }; break; - case SERINE: - { - bondColor[0] = 250; - bondColor[1] = 150; - bondColor[2] = 0; - }; break; - case CYSTEINE: - { - bondColor[0] = 230; - bondColor[1] = 230; - bondColor[2] = 0; - }; break; - case THREONINE: - { - bondColor[0] = 250; - bondColor[1] = 150; - bondColor[2] = 0; - }; break; - case METHIONINE: - { - bondColor[0] = 230; - bondColor[1] = 230; - bondColor[2] = 0; - }; break; - case PROLINE: - { - bondColor[0] = 220; - bondColor[1] = 150; - bondColor[2] = 130; - }; break; - case PHENYLALANINE: - { - bondColor[0] = 50; - bondColor[1] = 50; - bondColor[2] = 170; - }; break; - case TYROSINE: - { - bondColor[0] = 50; - bondColor[1] = 50; - bondColor[2] = 170; - }; break; - case TRYPTOPHAN: - { - bondColor[0] = 180; - bondColor[1] = 90; - bondColor[2] = 180; - }; break; - case HISTIDINE: - { - bondColor[0] = 130; - bondColor[1] = 130; - bondColor[2] = 210; - }; break; - case LYSINE: - { - bondColor[0] = 20; - bondColor[1] = 90; - bondColor[2] = 255; - }; break; - case ARGININE: - { - bondColor[0] = 20; - bondColor[1] = 90; - bondColor[2] = 255; - }; break; - case ASPARTICACID: - { - bondColor[0] = 230; - bondColor[1] = 10; - bondColor[2] = 10; - }; break; - case GLUTAMICACID: - { - bondColor[0] = 230; - bondColor[1] = 10; - bondColor[2] = 10; - }; break; - case ASPARAGINE: - { - bondColor[0] = 0; - bondColor[1] = 220; - bondColor[2] = 220; - }; break; - case GLUTAMINE: - { - bondColor[0] = 0; - bondColor[1] = 220; - bondColor[2] = 220; - }; break; - case WATER: - { - bondColor[0] = 0; - bondColor[1] = 0; - bondColor[2] = 255; - }; break; - case UNKNOWNRESIDUE: - default: - { - bondColor[0] = 255; - bondColor[1] = 255; - bondColor[2] = 255; - }; break; - } -} - -#pragma mark - -#pragma mark Database methods - -+ (BOOL)beginTransactionWithDatabase:(sqlite3 *)database; -{ - const char *sql1 = "BEGIN EXCLUSIVE TRANSACTION"; - sqlite3_stmt *begin_statement; - if (sqlite3_prepare_v2(database, sql1, -1, &begin_statement, NULL) != SQLITE_OK) - { - return NO; - } - if (sqlite3_step(begin_statement) != SQLITE_DONE) - { - return NO; - } - sqlite3_finalize(begin_statement); - return YES; -} - -+ (BOOL)endTransactionWithDatabase:(sqlite3 *)database; -{ - const char *sql2 = "COMMIT TRANSACTION"; - sqlite3_stmt *commit_statement; - if (sqlite3_prepare_v2(database, sql2, -1, &commit_statement, NULL) != SQLITE_OK) - { - return NO; - } - if (sqlite3_step(commit_statement) != SQLITE_DONE) - { - return NO; - } - sqlite3_finalize(commit_statement); - return YES; -} - -+ (void)finalizeStatements; -{ - if (insertMoleculeSQLStatement) sqlite3_finalize(insertMoleculeSQLStatement); - insertMoleculeSQLStatement = nil; - if (insertMetadataSQLStatement) sqlite3_finalize(insertMetadataSQLStatement); - insertMetadataSQLStatement = nil; - if (insertAtomSQLStatement) sqlite3_finalize(insertAtomSQLStatement); - insertAtomSQLStatement = nil; - if (insertBondSQLStatement) sqlite3_finalize(insertBondSQLStatement); - insertBondSQLStatement = nil; - if (updateMoleculeSQLStatement) sqlite3_finalize(updateMoleculeSQLStatement); - updateMoleculeSQLStatement = nil; - if (retrieveMoleculeSQLStatement) sqlite3_finalize(retrieveMoleculeSQLStatement); - retrieveMoleculeSQLStatement = nil; - if (retrieveMetadataSQLStatement) sqlite3_finalize(retrieveMetadataSQLStatement); - retrieveMetadataSQLStatement = nil; - if (retrieveAtomSQLStatement) sqlite3_finalize(retrieveAtomSQLStatement); - retrieveAtomSQLStatement = nil; - if (retrieveBondSQLStatement) sqlite3_finalize(retrieveBondSQLStatement); - retrieveBondSQLStatement = nil; - if (deleteMoleculeSQLStatement) sqlite3_finalize(deleteMoleculeSQLStatement); - deleteMoleculeSQLStatement = nil; - if (deleteMetadataSQLStatement) sqlite3_finalize(deleteMetadataSQLStatement); - deleteMetadataSQLStatement = nil; - if (deleteAtomSQLStatement) sqlite3_finalize(deleteAtomSQLStatement); - deleteAtomSQLStatement = nil; - if (deleteBondSQLStatement) sqlite3_finalize(deleteBondSQLStatement); - deleteBondSQLStatement = nil; -} - -// Write this after all parsing is complete -- (void)writeMoleculeDataToDatabase; -{ - if (updateMoleculeSQLStatement == nil) - { - const char *sql = "UPDATE molecules SET title=?, compound=?, format=?, atom_count=?, bond_count=?, structure_count=?, centerofmass_x=?, centerofmass_y=?, centerofmass_z=?, minimumposition_x=?, minimumposition_y=?, minimumposition_z=?, maximumposition_x=?, maximumposition_y=?, maximumposition_z=? WHERE id=?"; - if (sqlite3_prepare_v2(database, sql, -1, &updateMoleculeSQLStatement, NULL) != SQLITE_OK) - NSAssert1(0, NSLocalizedStringFromTable(@"Error: failed to prepare statement with message '%s'.", @"Localized", nil), sqlite3_errmsg(database)); - } - // Bind the query variables. - sqlite3_bind_text(updateMoleculeSQLStatement, 1, [[title stringByReplacingOccurrencesOfString:@"'" withString:@"''"] UTF8String], -1, SQLITE_TRANSIENT); - sqlite3_bind_text(updateMoleculeSQLStatement, 2, [[compound stringByReplacingOccurrencesOfString:@"'" withString:@"''"] UTF8String], -1, SQLITE_TRANSIENT); - sqlite3_bind_int(updateMoleculeSQLStatement, 3, 0); // Format enum is unused right now - sqlite3_bind_int(updateMoleculeSQLStatement, 4, numberOfAtoms); - sqlite3_bind_int(updateMoleculeSQLStatement, 5, numberOfBonds); - sqlite3_bind_int(updateMoleculeSQLStatement, 6, numberOfStructures); - sqlite3_bind_double(updateMoleculeSQLStatement, 7, centerOfMassInX); - sqlite3_bind_double(updateMoleculeSQLStatement, 8, centerOfMassInY); - sqlite3_bind_double(updateMoleculeSQLStatement, 9, centerOfMassInZ); - sqlite3_bind_double(updateMoleculeSQLStatement, 10, minimumXPosition); - sqlite3_bind_double(updateMoleculeSQLStatement, 11, minimumYPosition); - sqlite3_bind_double(updateMoleculeSQLStatement, 12, minimumZPosition); - sqlite3_bind_double(updateMoleculeSQLStatement, 13, maximumXPosition); - sqlite3_bind_double(updateMoleculeSQLStatement, 14, maximumYPosition); - sqlite3_bind_double(updateMoleculeSQLStatement, 15, maximumZPosition); - sqlite3_bind_int64(updateMoleculeSQLStatement, 16, databaseKey); - - // Execute the query. - int success = sqlite3_step(updateMoleculeSQLStatement); - // Reset the query for the next use. - sqlite3_reset(updateMoleculeSQLStatement); - // Handle errors. - if (success != SQLITE_DONE) - NSAssert1(0, NSLocalizedStringFromTable(@"Error: failed to dehydrate with message '%s'.", @"Localized", nil), sqlite3_errmsg(database)); -} - -- (void)addMetadataToDatabase:(NSString *)metadata type:(SLSMetadataType)metadataType; -{ - if (insertMetadataSQLStatement == nil) - { - static char *sql = "INSERT INTO metadata (molecule,type,value) VALUES(?,?,?)"; - if (sqlite3_prepare_v2(database, sql, -1, &insertMetadataSQLStatement, NULL) != SQLITE_OK) - { - NSAssert1(0,NSLocalizedStringFromTable(@"Error: failed to prepare statement with message '%s'.", @"Localized", nil), sqlite3_errmsg(database)); - } - } - // Bind the query variables. - sqlite3_bind_int64(insertMetadataSQLStatement, 1, databaseKey); - sqlite3_bind_int(insertMetadataSQLStatement, 2, metadataType); - sqlite3_bind_text(insertMetadataSQLStatement, 3, [[metadata stringByReplacingOccurrencesOfString:@"'" withString:@"''"] UTF8String], -1, SQLITE_TRANSIENT); - int success = sqlite3_step(insertMetadataSQLStatement); - // Because we want to reuse the statement, we "reset" it instead of "finalizing" it. - sqlite3_reset(insertMetadataSQLStatement); - if (success != SQLITE_DONE) - NSAssert1(0,NSLocalizedStringFromTable(@"Error: failed to insert metadata with message '%s'.", @"Localized", nil), sqlite3_errmsg(database)); -} - -- (NSInteger)addAtomToDatabase:(SLSAtomType)atomType atPoint:(SLS3DPoint)newPoint structureNumber:(NSInteger)structureNumber residueKey:(SLSResidueType)residueKey; -{ - if (insertAtomSQLStatement == nil) - { - static char *sql = "INSERT INTO atoms (molecule,residue,structure,element,x,y,z) VALUES(?,?,?,?,?,?,?)"; - if (sqlite3_prepare_v2(database, sql, -1, &insertAtomSQLStatement, NULL) != SQLITE_OK) - { - NSAssert1(0,NSLocalizedStringFromTable(@"Error: failed to prepare statement with message '%s'.", @"Localized", nil), sqlite3_errmsg(database)); - } - } - // Bind the query variables. - sqlite3_clear_bindings(insertAtomSQLStatement); - sqlite3_bind_int64(insertAtomSQLStatement, 1, databaseKey); - sqlite3_bind_int(insertAtomSQLStatement, 2, residueKey); - sqlite3_bind_int64(insertAtomSQLStatement, 3, structureNumber); - sqlite3_bind_int(insertAtomSQLStatement, 4, atomType); - sqlite3_bind_double(insertAtomSQLStatement, 5, (double)newPoint.x); - sqlite3_bind_double(insertAtomSQLStatement, 6, (double)newPoint.y); - sqlite3_bind_double(insertAtomSQLStatement, 7, (double)newPoint.z); - int success = sqlite3_step(insertAtomSQLStatement); - // Because we want to reuse the statement, we "reset" it instead of "finalizing" it. - sqlite3_reset(insertAtomSQLStatement); - if (success == SQLITE_ERROR) - { - return -1; - // SQLite provides a method which retrieves the value of the most recently auto-generated primary key sequence - // in the database. To access this functionality, the table should have a column declared of type - // "INTEGER PRIMARY KEY" - } - - if (stillCountingAtomsInFirstStructure) - numberOfAtoms++; - - return sqlite3_last_insert_rowid(database); -} - -// Evaluate using atom IDs here for greater rendering flexibility -- (void)addBondToDatabaseWithStartPoint:(NSValue *)startValue endPoint:(NSValue *)endValue bondType:(SLSBondType)bondType structureNumber:(NSInteger)structureNumber residueKey:(NSInteger)residueKey; -{ - SLS3DPoint startPoint, endPoint; - if ( (startValue == nil) || (endValue == nil) ) - return; - [startValue getValue:&startPoint]; - [endValue getValue:&endPoint]; - - float bondLength = sqrt((startPoint.x - endPoint.x) * (startPoint.x - endPoint.x) + (startPoint.y - endPoint.y) * (startPoint.y - endPoint.y) + (startPoint.z - endPoint.z) * (startPoint.z - endPoint.z)); - if (bondLength > BOND_LENGTH_LIMIT) - { - // Don't allow weird, wrong bonds to be displayed - return; - } - - if (insertBondSQLStatement == nil) - { - static char *sql = "INSERT INTO bonds (molecule,residue,structure,bond_type,start_x,start_y,start_z,end_x,end_y,end_z) VALUES(?,?,?,?,?,?,?,?,?,?)"; - if (sqlite3_prepare_v2(database, sql, -1, &insertBondSQLStatement, NULL) != SQLITE_OK) - { - NSAssert1(0, NSLocalizedStringFromTable(@"Error: failed to prepare statement with message '%s'.", @"Localized", nil), sqlite3_errmsg(database)); - } - } - // Bind the query variables. - sqlite3_clear_bindings(insertBondSQLStatement); - sqlite3_bind_int64(insertBondSQLStatement, 1, databaseKey); - sqlite3_bind_int64(insertBondSQLStatement, 2, residueKey); - sqlite3_bind_int64(insertBondSQLStatement, 3, structureNumber); - sqlite3_bind_int(insertBondSQLStatement, 4, bondType); - sqlite3_bind_double(insertBondSQLStatement, 5, (double)startPoint.x); - sqlite3_bind_double(insertBondSQLStatement, 6, (double)startPoint.y); - sqlite3_bind_double(insertBondSQLStatement, 7, (double)startPoint.z); - sqlite3_bind_double(insertBondSQLStatement, 8, (double)endPoint.x); - sqlite3_bind_double(insertBondSQLStatement, 9, (double)endPoint.y); - sqlite3_bind_double(insertBondSQLStatement, 10, (double)endPoint.z); - int success = sqlite3_step(insertBondSQLStatement); - // Because we want to reuse the statement, we "reset" it instead of "finalizing" it. - sqlite3_reset(insertBondSQLStatement); - if (success != SQLITE_DONE) - NSAssert1(0, NSLocalizedStringFromTable(@"Error: failed to insert bond with message '%s'.", @"Localized", nil), sqlite3_errmsg(database)); - - if (stillCountingAtomsInFirstStructure) - numberOfBonds++; -} - -- (void)readMetadataFromDatabaseIfNecessary; -{ - // Check to make sure metadata has not already been loaded - if (isPopulatedFromDatabase) - return; - - if (retrieveMetadataSQLStatement == nil) - { - const char *sql = "SELECT * FROM metadata WHERE molecule=?"; - if (sqlite3_prepare_v2(database, sql, -1, &retrieveMetadataSQLStatement, NULL) != SQLITE_OK) - { - NSAssert1(0,NSLocalizedStringFromTable(@"Error: failed to prepare statement with message '%s'.", @"Localized", nil), sqlite3_errmsg(database)); - } - } - - // Bind the query variables. - sqlite3_bind_int64(retrieveMetadataSQLStatement, 1, databaseKey); - - while (sqlite3_step(retrieveMetadataSQLStatement) == SQLITE_ROW) - { - //id, molecule,type,value - SLSMetadataType metadataType = sqlite3_column_int(retrieveMetadataSQLStatement, 2); - char *stringResult = (char *)sqlite3_column_text(retrieveMetadataSQLStatement, 3); - NSString *sqlString = (stringResult) ? [NSString stringWithUTF8String:stringResult] : @""; - - switch (metadataType) - { - case MOLECULESOURCE: - { - source = [sqlString stringByReplacingOccurrencesOfString:@"''" withString:@"'"]; - }; break; - case MOLECULEAUTHOR: - { - author = [sqlString stringByReplacingOccurrencesOfString:@"''" withString:@"'"]; - }; break; - case JOURNALAUTHOR: - { - journalAuthor = [sqlString stringByReplacingOccurrencesOfString:@"''" withString:@"'"]; - }; break; - case JOURNALTITLE: - { - journalTitle = [sqlString stringByReplacingOccurrencesOfString:@"''" withString:@"'"]; - }; break; - case JOURNALREFERENCE: - { - journalReference = [sqlString stringByReplacingOccurrencesOfString:@"''" withString:@"'"]; - }; break; - case MOLECULESEQUENCE: - { - sequence = [sqlString stringByReplacingOccurrencesOfString:@"''" withString:@"'"]; - }; break; - } - } - - // Because we want to reuse the statement, we "reset" it instead of "finalizing" it. - sqlite3_reset(retrieveMetadataSQLStatement); - isPopulatedFromDatabase = YES; -} - -- (void)deleteMoleculeDataFromDatabase; -{ - // Delete the molecule from the SQLite database - if (deleteMoleculeSQLStatement == nil) - { - const char *sql = "DELETE FROM molecules WHERE id=?"; - if (sqlite3_prepare_v2(database, sql, -1, &deleteMoleculeSQLStatement, NULL) != SQLITE_OK) - NSAssert1(0, NSLocalizedStringFromTable(@"Error: failed to prepare statement with message '%s'.", @"Localized", nil), sqlite3_errmsg(database)); - } - sqlite3_bind_int64(deleteMoleculeSQLStatement, 1, databaseKey); - int success = sqlite3_step(deleteMoleculeSQLStatement); - sqlite3_reset(deleteMoleculeSQLStatement); - if (success != SQLITE_DONE) - NSAssert1(0,NSLocalizedStringFromTable(@"Error: failed to dehydrate with message '%s'.", @"Localized", nil), sqlite3_errmsg(database)); - - // Delete the metadata associated with the molecule from the SQLite database - if (deleteMetadataSQLStatement == nil) - { - const char *sql = "DELETE FROM metadata WHERE molecule=?"; - if (sqlite3_prepare_v2(database, sql, -1, &deleteMetadataSQLStatement, NULL) != SQLITE_OK) - NSAssert1(0, NSLocalizedStringFromTable(@"Error: failed to prepare statement with message '%s'.", @"Localized", nil), sqlite3_errmsg(database)); - } - sqlite3_bind_int64(deleteMetadataSQLStatement, 1, databaseKey); - success = sqlite3_step(deleteMetadataSQLStatement); - sqlite3_reset(deleteMetadataSQLStatement); - if (success != SQLITE_DONE) - NSAssert1(0,NSLocalizedStringFromTable(@"Error: failed to dehydrate with message '%s'.", @"Localized", nil), sqlite3_errmsg(database)); - - // Delete the atoms associated with the molecule from the SQLite database - if (deleteAtomSQLStatement == nil) - { - const char *sql = "DELETE FROM atoms WHERE molecule=?"; - if (sqlite3_prepare_v2(database, sql, -1, &deleteAtomSQLStatement, NULL) != SQLITE_OK) - NSAssert1(0, NSLocalizedStringFromTable(@"Error: failed to prepare statement with message '%s'.", @"Localized", nil), sqlite3_errmsg(database)); - } - sqlite3_bind_int64(deleteAtomSQLStatement, 1, databaseKey); - success = sqlite3_step(deleteAtomSQLStatement); - sqlite3_reset(deleteAtomSQLStatement); - if (success != SQLITE_DONE) - NSAssert1(0,NSLocalizedStringFromTable(@"Error: failed to dehydrate with message '%s'.", @"Localized", nil), sqlite3_errmsg(database)); - - // Delete the bonds associated with the molecule from the SQLite database - if (deleteBondSQLStatement == nil) - { - const char *sql = "DELETE FROM bonds WHERE molecule=?"; - if (sqlite3_prepare_v2(database, sql, -1, &deleteBondSQLStatement, NULL) != SQLITE_OK) - NSAssert1(0, NSLocalizedStringFromTable(@"Error: failed to prepare statement with message '%s'.", @"Localized", nil), sqlite3_errmsg(database)); - } - sqlite3_bind_int64(deleteBondSQLStatement, 1, databaseKey); - success = sqlite3_step(deleteBondSQLStatement); - sqlite3_reset(deleteBondSQLStatement); - if (success != SQLITE_DONE) - NSAssert1(0, NSLocalizedStringFromTable(@"Error: failed to dehydrate with message '%s'.", @"Localized", nil), sqlite3_errmsg(database)); - -} - -- (NSInteger)countAtomsForFirstStructure; -{ - const char *sql = "SELECT COUNT(*) FROM atoms WHERE molecule=? AND structure=?"; - sqlite3_stmt *atomCountingStatement; - - unsigned int totalAtomCount = 0; - - if (sqlite3_prepare_v2(database, sql, -1, &atomCountingStatement, NULL) == SQLITE_OK) - { - sqlite3_bind_int64(atomCountingStatement, 1, databaseKey); - sqlite3_bind_int(atomCountingStatement, 2, numberOfStructureBeingDisplayed); - - if (sqlite3_step(atomCountingStatement) == SQLITE_ROW) - { - totalAtomCount = sqlite3_column_int(atomCountingStatement, 0); - } - else - { - } - } - sqlite3_finalize(atomCountingStatement); - - return totalAtomCount; -} - -- (NSInteger)countBondsForFirstStructure; -{ - const char *sql = "SELECT COUNT(*) FROM bonds WHERE molecule=? AND structure=?"; - sqlite3_stmt *bondCountingStatement; - - unsigned int totalBondCount = 0; - - if (sqlite3_prepare_v2(database, sql, -1, &bondCountingStatement, NULL) == SQLITE_OK) - { - sqlite3_bind_int64(bondCountingStatement, 1, databaseKey); - sqlite3_bind_int(bondCountingStatement, 2, numberOfStructureBeingDisplayed); - - if (sqlite3_step(bondCountingStatement) == SQLITE_ROW) - { - totalBondCount = sqlite3_column_int(bondCountingStatement, 0); - } - else - { - } - } - sqlite3_finalize(bondCountingStatement); - - return totalBondCount; -} - -#pragma mark - -#pragma mark Status notification methods - -- (void)showStatusIndicator; -{ - [[NSNotificationCenter defaultCenter] postNotificationName:kSLSMoleculeRenderingStartedNotification object:nil ]; -} - -- (void)updateStatusIndicator; -{ - [[NSNotificationCenter defaultCenter] postNotificationName:kSLSMoleculeRenderingUpdateNotification object:[NSNumber numberWithDouble:(double)currentFeatureBeingRendered/(double)totalNumberOfFeaturesToRender] ]; -} - -- (void)hideStatusIndicator; -{ - [[NSNotificationCenter defaultCenter] postNotificationName:kSLSMoleculeRenderingEndedNotification object:nil ]; -} - -#pragma mark - -#pragma mark Rendering - -- (void)switchToDefaultVisualizationMode; -{ - if ((numberOfAtoms < 600) && (numberOfBonds > 0)) - { -// self.currentVisualizationType = SPACEFILLING; - self.currentVisualizationType = BALLANDSTICK; - } - else - { - self.currentVisualizationType = SPACEFILLING; - } - - [[NSUserDefaults standardUserDefaults] setInteger:currentVisualizationType forKey:@"currentVisualizationMode"]; -} - -- (BOOL)renderMolecule:(SLSOpenGLESRenderer *)openGLESRenderer; -{ - currentRenderer = openGLESRenderer; - @autoreleasepool { - - isDoneRendering = NO; - [self performSelectorOnMainThread:@selector(showStatusIndicator) withObject:nil waitUntilDone:NO]; - - [openGLESRenderer initiateMoleculeRendering]; - - openGLESRenderer.overallMoleculeScaleFactor = scaleAdjustmentForX; - - currentFeatureBeingRendered = 0; - - switch(currentVisualizationType) - { - case BALLANDSTICK: - { - [openGLESRenderer configureBasedOnNumberOfAtoms:[self countAtomsForFirstStructure] numberOfBonds:[self countBondsForFirstStructure]]; - totalNumberOfFeaturesToRender = numberOfAtoms + numberOfBonds; - - openGLESRenderer.bondRadiusScaleFactor = 0.15; - openGLESRenderer.atomRadiusScaleFactor = 0.35; - - [self readAndRenderAtoms:openGLESRenderer]; - [self readAndRenderBonds:openGLESRenderer]; -// openGLESRenderer.atomRadiusScaleFactor = 0.27; - }; break; - case SPACEFILLING: - { - [openGLESRenderer configureBasedOnNumberOfAtoms:[self countAtomsForFirstStructure] numberOfBonds:0]; - totalNumberOfFeaturesToRender = numberOfAtoms; - - openGLESRenderer.atomRadiusScaleFactor = 1.0; - [self readAndRenderAtoms:openGLESRenderer]; - }; break; - case CYLINDRICAL: - { - [openGLESRenderer configureBasedOnNumberOfAtoms:0 numberOfBonds:[self countBondsForFirstStructure]]; - - totalNumberOfFeaturesToRender = numberOfBonds; - - openGLESRenderer.bondRadiusScaleFactor = 0.15; - [self readAndRenderBonds:openGLESRenderer]; - }; break; - } - - if (!isRenderingCancelled) - { - [openGLESRenderer bindVertexBuffersForMolecule]; -// } -// else -// { -// [openGLESRenderer performSelectorOnMainThread:@selector(bindVertexBuffersForMolecule) withObject:nil waitUntilDone:YES]; -// } - } - else - { - isBeingDisplayed = NO; - isRenderingCancelled = NO; - - [openGLESRenderer terminateMoleculeRendering]; - } - - - isDoneRendering = YES; - [self performSelectorOnMainThread:@selector(hideStatusIndicator) withObject:nil waitUntilDone:YES]; - - } - - currentRenderer = nil; - return YES; -} - -- (void)readAndRenderAtoms:(SLSOpenGLESRenderer *)openGLESRenderer; -{ - if (isRenderingCancelled) - { - return; - } - - if (retrieveAtomSQLStatement == nil) - { - const char *sql = "SELECT residue,structure,element,x,y,z FROM atoms WHERE molecule=? AND structure=?"; - // const char *sql = "SELECT * FROM atoms WHERE molecule=?"; - if (sqlite3_prepare_v2(database, sql, -1, &retrieveAtomSQLStatement, NULL) != SQLITE_OK) - { - NSAssert1(0, NSLocalizedStringFromTable(@"Error: failed to prepare statement with message '%s'.", @"Localized", nil), sqlite3_errmsg(database)); - } - } - - // Bind the query variables. - sqlite3_bind_int64(retrieveAtomSQLStatement, 1, databaseKey); - sqlite3_bind_int(retrieveAtomSQLStatement, 2, numberOfStructureBeingDisplayed); - - while ((sqlite3_step(retrieveAtomSQLStatement) == SQLITE_ROW) && !isRenderingCancelled) - { - //(id,molecule,residue,structure,element,x,y,z);" - if ( (currentFeatureBeingRendered % 100) == 0) - { - [self performSelectorOnMainThread:@selector(updateStatusIndicator) withObject:nil waitUntilDone:NO]; - } - currentFeatureBeingRendered++; - - SLSResidueType residueType = sqlite3_column_int(retrieveAtomSQLStatement, 0); - // TODO: Determine if rendering a particular structure, if not don't render atom - SLSAtomType atomType = sqlite3_column_int(retrieveAtomSQLStatement, 2); - SLS3DPoint atomCoordinate; - atomCoordinate.x = sqlite3_column_double(retrieveAtomSQLStatement, 3); - atomCoordinate.x -= centerOfMassInX; - atomCoordinate.x *= scaleAdjustmentForX; - atomCoordinate.y = sqlite3_column_double(retrieveAtomSQLStatement, 4); - atomCoordinate.y -= centerOfMassInY; - atomCoordinate.y *= scaleAdjustmentForX; - atomCoordinate.z = sqlite3_column_double(retrieveAtomSQLStatement, 5); - atomCoordinate.z -= centerOfMassInZ; - atomCoordinate.z *= scaleAdjustmentForX; - - if (residueType != WATER) - { -// [openGLESRenderer addAtomToVertexBuffers:OXYGEN atPoint:atomCoordinate]; - [openGLESRenderer addAtomToVertexBuffers:atomType atPoint:atomCoordinate]; - } - } - - // Because we want to reuse the statement, we "reset" it instead of "finalizing" it. - sqlite3_reset(retrieveAtomSQLStatement); -} - -- (void)readAndRenderBonds:(SLSOpenGLESRenderer *)openGLESRenderer; -{ - if (isRenderingCancelled) - { - return; - } - - if (retrieveBondSQLStatement == nil) - { - const char *sql = "SELECT * FROM bonds WHERE molecule=? AND structure=?"; - if (sqlite3_prepare_v2(database, sql, -1, &retrieveBondSQLStatement, NULL) != SQLITE_OK) - { - NSAssert1(0, NSLocalizedStringFromTable(@"Error: failed to prepare statement with message '%s'.", @"Localized", nil), sqlite3_errmsg(database)); - } - } - - // Bind the query variables. - sqlite3_bind_int64(retrieveBondSQLStatement, 1, databaseKey); - sqlite3_bind_int(retrieveBondSQLStatement, 2, numberOfStructureBeingDisplayed); - - while ((sqlite3_step(retrieveBondSQLStatement) == SQLITE_ROW) && !isRenderingCancelled) - { - //(id ,molecule ,residue ,structure ,bond_type ,start_x ,start_y ,start_z ,end_x ,end_y ,end_z ) - - // TODO: Determine if rendering a particular structure, if not don't render atom - if ( (currentFeatureBeingRendered % 100) == 0) - [self performSelectorOnMainThread:@selector(updateStatusIndicator) withObject:nil waitUntilDone:NO]; - currentFeatureBeingRendered++; - - SLSBondType bondType = sqlite3_column_int(retrieveBondSQLStatement, 4); - SLS3DPoint startingCoordinate, endingCoordinate; - startingCoordinate.x = sqlite3_column_double(retrieveBondSQLStatement, 5); - startingCoordinate.x -= centerOfMassInX; - startingCoordinate.x *= scaleAdjustmentForX; - startingCoordinate.y = sqlite3_column_double(retrieveBondSQLStatement, 6); - startingCoordinate.y -= centerOfMassInY; - startingCoordinate.y *= scaleAdjustmentForX; - startingCoordinate.z = sqlite3_column_double(retrieveBondSQLStatement, 7); - startingCoordinate.z -= centerOfMassInZ; - startingCoordinate.z *= scaleAdjustmentForX; - endingCoordinate.x = sqlite3_column_double(retrieveBondSQLStatement, 8); - endingCoordinate.x -= centerOfMassInX; - endingCoordinate.x *= scaleAdjustmentForX; - endingCoordinate.y = sqlite3_column_double(retrieveBondSQLStatement, 9); - endingCoordinate.y -= centerOfMassInY; - endingCoordinate.y *= scaleAdjustmentForX; - endingCoordinate.z = sqlite3_column_double(retrieveBondSQLStatement, 10); - endingCoordinate.z -= centerOfMassInZ; - endingCoordinate.z *= scaleAdjustmentForX; - SLSResidueType residueType = sqlite3_column_int(retrieveBondSQLStatement, 2); - GLubyte bondColor[4] = {200,200,200,255}; // Bonds are grey by default - - if (currentVisualizationType == CYLINDRICAL) - { - [SLSMolecule setBondColor:bondColor forResidueType:residueType]; - } - - if (residueType != WATER) - { - [openGLESRenderer addBondToVertexBuffersWithStartPoint:startingCoordinate endPoint:endingCoordinate bondColor:bondColor bondType:bondType]; - } - } - - // Because we want to reuse the statement, we "reset" it instead of "finalizing" it. - sqlite3_reset(retrieveBondSQLStatement); -} - - -#pragma mark - -#pragma mark Accessors - -@synthesize centerOfMassInX, centerOfMassInY, centerOfMassInZ; -@synthesize filename, filenameWithoutExtension, title, keywords, journalAuthor, journalTitle, journalReference, sequence, compound, source, author; -@synthesize isBeingDisplayed, isDoneRendering, isRenderingCancelled; -@synthesize numberOfAtoms, numberOfStructures; -@synthesize previousTerminalAtomValue; -@synthesize currentVisualizationType; -@synthesize numberOfStructureBeingDisplayed; - - -- (void)setIsBeingDisplayed:(BOOL)newValue; -{ - if (newValue == isBeingDisplayed) - { - return; - } - - isBeingDisplayed = newValue; - if (isBeingDisplayed) - { - isRenderingCancelled = NO; - } - else - { - if (!isDoneRendering) - { - self.isRenderingCancelled = YES; - [currentRenderer cancelMoleculeRendering]; - [NSThread sleepForTimeInterval:1.0]; - } - } -} - -@end diff --git a/SLSMoleculeAppDelegate.h b/SLSMoleculeAppDelegate.h deleted file mode 100644 index a6cabcb..0000000 --- a/SLSMoleculeAppDelegate.h +++ /dev/null @@ -1,64 +0,0 @@ -// -// SLSMoleculeAppDelegate.h -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 5/18/2008. -// -// This is the base application delegate, used for handling launch, termination, and memory-related delegate methods - -#import -#import -#import "SLSMoleculeCustomDownloadViewController.h" - -@class SLSMoleculeRootViewController; - - -@interface SLSMoleculeAppDelegate : NSObject -{ - UIWindow *window; - SLSMoleculeRootViewController *rootViewController; - UIViewController *splitViewController; - - NSURLConnection *downloadConnection; - NSMutableData *downloadedFileContents; - NSString *nameOfDownloadedMolecule; - BOOL downloadCancelled; - NSLock *initialDatabaseLoadLock; - BOOL isGzipCompressionUsedOnDownload, isHandlingCustomURLMoleculeDownload; - - // SQLite database of all molecules - sqlite3 *database; - NSMutableArray *molecules; -} - -@property (nonatomic, strong) UIWindow *window; -@property (nonatomic, strong) SLSMoleculeRootViewController *rootViewController; - -// Device-specific interface control -+ (BOOL)isRunningOniPad; - -// Database access -- (NSString *)cachesDirectory; -- (NSString *)applicationSupportDirectory; -- (BOOL)createEditableCopyOfDatabaseIfNeeded; -- (void)connectToDatabase; -- (void)disconnectFromDatabase; -- (void)loadAllMoleculesFromDatabase; -- (void)loadInitialMoleculesFromDisk; -- (void)loadMissingMoleculesIntoDatabase; - -// Status update methods -- (void)showStatusIndicator; -- (void)showDownloadIndicator; -- (void)updateStatusIndicator; -- (void)hideStatusIndicator; - -// Custom molecule download methods -- (BOOL)handleCustomURLScheme:(NSURL *)url; -- (void)downloadCompleted; -- (void)saveMoleculeWithData:(NSData *)moleculeData toFilename:(NSString *)filename; - -@end - diff --git a/SLSMoleculeAppDelegate.m b/SLSMoleculeAppDelegate.m deleted file mode 100644 index 5cc378b..0000000 --- a/SLSMoleculeAppDelegate.m +++ /dev/null @@ -1,687 +0,0 @@ -// -// SLSMoleculesAppDelegate.m -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 5/18/2008. -// -// This is the base application delegate, used for handling launch, termination, and memory-related delegate methods - -#import "SLSMoleculeAppDelegate.h" -#import "SLSMoleculeRootViewController.h" -#import "SLSMoleculeiPadRootViewController.h" -#import "SLSMoleculeGLViewController.h" -#import "SLSMoleculeTableViewController.h" -#import "SLSMolecule.h" -#import "NSData+Gzip.h" - -#import "VCTitleCase.h" - -#define MOLECULES_DATABASE_VERSION 1 - -@implementation SLSMoleculeAppDelegate - -@synthesize window; -@synthesize rootViewController; - -#pragma mark - -#pragma mark Initialization / teardown - -- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions -{ - //Initialize the application window - window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; - if (!window) - { - return NO; - } - window.backgroundColor = [UIColor blackColor]; - - if ([SLSMoleculeAppDelegate isRunningOniPad]) - { - UISplitViewController *newSplitViewController = [[UISplitViewController alloc] init]; - if ([newSplitViewController respondsToSelector:@selector(setPresentsWithGesture:)]) - { - [newSplitViewController setPresentsWithGesture:NO]; - } - - rootViewController = [[SLSMoleculeiPadRootViewController alloc] init]; - [rootViewController loadView]; - newSplitViewController.viewControllers = [NSArray arrayWithObjects:rootViewController.tableNavigationController, rootViewController, nil]; - newSplitViewController.delegate = (SLSMoleculeiPadRootViewController *)rootViewController; - splitViewController = newSplitViewController; - [window addSubview:splitViewController.view]; - self.window.rootViewController = splitViewController; - } - else - { - rootViewController = [[SLSMoleculeRootViewController alloc] init]; - [window addSubview:rootViewController.view]; - self.window.rootViewController = rootViewController; - } - - - [window makeKeyAndVisible]; - [window layoutSubviews]; - - // Start the initialization of the database, if necessary - isHandlingCustomURLMoleculeDownload = NO; - downloadedFileContents = nil; - initialDatabaseLoadLock = [[NSLock alloc] init]; - - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; - - NSURL *url = (NSURL *)[launchOptions valueForKey:UIApplicationLaunchOptionsURLKey]; - - if (url != nil) - { - isHandlingCustomURLMoleculeDownload = YES; - } - - [self performSelectorInBackground:@selector(loadInitialMoleculesFromDisk) withObject:nil]; - - return YES; -} - -- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url -{ - - if (url != nil) - { - isHandlingCustomURLMoleculeDownload = YES; - } - - // Deal with case where you are in the table view - if (![SLSMoleculeAppDelegate isRunningOniPad]) - { - if ([rootViewController.glViewController.view superview] == nil) - { - [[NSNotificationCenter defaultCenter] postNotificationName:@"ToggleView" object:nil]; - } - } - - // Handle the Molecules custom URL scheme - [self handleCustomURLScheme:url]; - - return YES; -} - - -#pragma mark - -#pragma mark Device-specific interface control - -/*+ (BOOL)isRunningOniPad; -{ - return (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad); -}*/ - -+ (BOOL)isRunningOniPad; -{ - static BOOL hasCheckediPadStatus = NO; - static BOOL isRunningOniPad = NO; - - if (!hasCheckediPadStatus) - { - if ([[UIDevice currentDevice] respondsToSelector:@selector(userInterfaceIdiom)]) - { - if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) - { - isRunningOniPad = YES; - hasCheckediPadStatus = YES; - return isRunningOniPad; - } - } - - hasCheckediPadStatus = YES; - } - - return isRunningOniPad; -} - -#pragma mark - -#pragma mark Database access - -- (NSString *)cachesDirectory; -{ - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); - NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil; - - NSFileManager *fileManager = [NSFileManager defaultManager]; - if ([fileManager fileExistsAtPath:basePath] == NO) - { - NSError *error = nil; - [fileManager createDirectoryAtPath:basePath withIntermediateDirectories:NO attributes:nil error:&error]; - } - - return basePath; -} - -- (NSString *)applicationSupportDirectory; -{ - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); - NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil; - - NSFileManager *fileManager = [NSFileManager defaultManager]; - if ([fileManager fileExistsAtPath:basePath] == NO) - { - NSError *error = nil; - [fileManager createDirectoryAtPath:basePath withIntermediateDirectories:NO attributes:nil error:&error]; - } - - return basePath; -} - -- (BOOL)createEditableCopyOfDatabaseIfNeeded; -{ - // First, see if the database exists in the /Documents directory. If so, move it to Application Support. - NSFileManager *fileManager = [NSFileManager defaultManager]; - NSError *error; - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); - NSString *documentsDirectory = [paths objectAtIndex:0]; - NSString *writableDBPath = [documentsDirectory stringByAppendingPathComponent:@"molecules.sql"]; - if ([fileManager fileExistsAtPath:writableDBPath]) - { - [fileManager moveItemAtPath:writableDBPath toPath:[[self cachesDirectory] stringByAppendingPathComponent:@"molecules.sql"] error:&error]; - } - - // Move the older database to the proper location for iCloud - if ([fileManager fileExistsAtPath:[[self applicationSupportDirectory] stringByAppendingPathComponent:@"molecules.sql"]]) - { - [fileManager moveItemAtPath:[[self applicationSupportDirectory] stringByAppendingPathComponent:@"molecules.sql"] toPath:[[self cachesDirectory] stringByAppendingPathComponent:@"molecules.sql"] error:&error]; - } - - writableDBPath = [[self cachesDirectory] stringByAppendingPathComponent:@"molecules.sql"]; - - if ([fileManager fileExistsAtPath:writableDBPath]) - return NO; - - // The database does not exist, so copy a blank starter database to the Documents directory - NSString *defaultDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:@"molecules.sql"]; - BOOL success = [fileManager copyItemAtPath:defaultDBPath toPath:writableDBPath error:&error]; - if (!success) { - NSAssert1(0,NSLocalizedStringFromTable(@"Failed to create writable database file with message '%@'.", @"Localized", nil), [error localizedDescription]); - } - return YES; -} - -- (void)connectToDatabase; -{ - molecules = [[NSMutableArray alloc] init]; - - // The database is stored in the application bundle. - NSString *path = [[self cachesDirectory] stringByAppendingPathComponent:@"molecules.sql"]; - // Open the database. The database was prepared outside the application. - if (sqlite3_open([path UTF8String], &database) == SQLITE_OK) - { - } - else - { - // Even though the open failed, call close to properly clean up resources. - sqlite3_close(database); - NSAssert1(0,NSLocalizedStringFromTable(@"Failed to open database with message '%s'.", @"Localized", nil), sqlite3_errmsg(database)); - // Additional error handling, as appropriate... - } - -} - -- (void)disconnectFromDatabase; -{ -// TODO: Maybe write out all database entries to disk - // [books makeObjectsPerformSelector:@selector(dehydrate)]; - [SLSMolecule finalizeStatements]; - // Close the database. - if (sqlite3_close(database) != SQLITE_OK) - { - //NSAssert1(0,NSLocalizedStringFromTable(@"Error: failed to close database with message '%s'.", @"Localized", nil), sqlite3_errmsg(database)); - } - - database = nil; -} - -- (void)loadInitialMoleculesFromDisk; -{ - [initialDatabaseLoadLock lock]; - - @autoreleasepool { - rootViewController.molecules = nil; - - if ([self createEditableCopyOfDatabaseIfNeeded]) - { - // The database needed to be recreated, so scan and copy over the default files - [self performSelectorOnMainThread:@selector(showStatusIndicator) withObject:nil waitUntilDone:NO]; - - [self connectToDatabase]; - // Before anything else, move included PDB files to /Documents if the program hasn't been run before - // User might have intentionally deleted files, so don't recopy the files in that case - NSError *error = nil; - // Grab the /Documents directory path - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); - NSString *documentsDirectory = [paths objectAtIndex:0]; - - NSFileManager *fileManager = [NSFileManager defaultManager]; - // Iterate through all files sitting in the application's Resources directory - // TODO: Can you fast enumerate this? - NSDirectoryEnumerator *direnum = [fileManager enumeratorAtPath:[[NSBundle mainBundle] resourcePath]]; - NSString *pname; - while ((pname = [direnum nextObject])) - { - if ([[pname pathExtension] isEqualToString:@"gz"]) - { - NSString *preloadedPDBPath = [[[NSBundle mainBundle] resourcePath] stringByAppendingPathComponent:pname]; - NSString *installedPDBPath = [documentsDirectory stringByAppendingPathComponent:pname]; - if (![fileManager fileExistsAtPath:installedPDBPath]) - { - // Move included PDB files to /Documents - [[NSFileManager defaultManager] copyItemAtPath:preloadedPDBPath toPath:installedPDBPath error:&error]; - if (error != nil) - { -// NSLog(@"Failed to copy over PDB files with error: '%@'.", [error localizedDescription]); - // TODO: Report the file copying problem to the user or do something about it - } - } - - } - } - - [self loadMissingMoleculesIntoDatabase]; - - [[NSUserDefaults standardUserDefaults] synchronize]; - [self performSelectorOnMainThread:@selector(hideStatusIndicator) withObject:nil waitUntilDone:YES]; - } - else - { - // The MySQL database has been created, so load molecules from the database - [self connectToDatabase]; - // TODO: Check to make sure that the proper version of the database is installed - [self loadAllMoleculesFromDatabase]; - [self loadMissingMoleculesIntoDatabase]; - } - - rootViewController.database = database; - rootViewController.molecules = molecules; - [initialDatabaseLoadLock unlock]; - - if ([SLSMoleculeAppDelegate isRunningOniPad]) - { - [[rootViewController.tableViewController tableView] performSelectorOnMainThread:@selector(reloadData) withObject:nil waitUntilDone:NO]; - } - - if (!isHandlingCustomURLMoleculeDownload) - [rootViewController loadInitialMolecule]; - } -} - -- (void)loadAllMoleculesFromDatabase; -{ - const char *sql = "SELECT * FROM molecules"; - sqlite3_stmt *moleculeLoadingStatement; - - if (sqlite3_prepare_v2(database, sql, -1, &moleculeLoadingStatement, NULL) == SQLITE_OK) - { - while (sqlite3_step(moleculeLoadingStatement) == SQLITE_ROW) - { - SLSMolecule *newMolecule = [[SLSMolecule alloc] initWithSQLStatement:moleculeLoadingStatement database:database]; - if (newMolecule != nil) - [molecules addObject:newMolecule]; - - } - } - // "Finalize" the statement - releases the resources associated with the statement. - sqlite3_finalize(moleculeLoadingStatement); -} - -- (void)loadMissingMoleculesIntoDatabase; -{ - // First, load all molecule names from the database - NSMutableDictionary *moleculeFilenameLookupTable = [[NSMutableDictionary alloc] init]; - - const char *sql = "SELECT * FROM molecules"; - sqlite3_stmt *moleculeLoadingStatement; - - if (sqlite3_prepare_v2(database, sql, -1, &moleculeLoadingStatement, NULL) == SQLITE_OK) - { - while (sqlite3_step(moleculeLoadingStatement) == SQLITE_ROW) - { - char *stringResult = (char *)sqlite3_column_text(moleculeLoadingStatement, 1); - NSString *sqlString = (stringResult) ? [NSString stringWithUTF8String:stringResult] : @""; - NSString *filename = [sqlString stringByReplacingOccurrencesOfString:@"''" withString:@"'"]; - [moleculeFilenameLookupTable setValue:[NSNumber numberWithBool:YES] forKey:filename]; - } - } - sqlite3_finalize(moleculeLoadingStatement); - - // Now, check all the files on disk to see if any are missing from the database - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); - NSString *documentsDirectory = [paths objectAtIndex:0]; - - NSDirectoryEnumerator *direnum = [[NSFileManager defaultManager] - enumeratorAtPath:documentsDirectory]; - NSString *pname; - while ((pname = [direnum nextObject])) - { - NSString *lastPathComponent = [pname lastPathComponent]; - if (![lastPathComponent isEqualToString:pname]) - { - NSError *error = nil; - // The file has been passed in using a subdirectory, so move it into the flattened /Documents directory - [[NSFileManager defaultManager] moveItemAtPath:[documentsDirectory stringByAppendingPathComponent:pname] toPath:[documentsDirectory stringByAppendingPathComponent:lastPathComponent] error:&error]; - [[NSFileManager defaultManager] removeItemAtPath:[documentsDirectory stringByAppendingPathComponent:[pname stringByDeletingLastPathComponent]] error:&error]; - pname = lastPathComponent; - } - - if ( ([moleculeFilenameLookupTable valueForKey:pname] == nil) && ([[[pname pathExtension] lowercaseString] isEqualToString:@"gz"] || [[[pname pathExtension] lowercaseString] isEqualToString:@"pdb"]) ) - { - // Parse the PDB file into the database - SLSMolecule *newMolecule = [[SLSMolecule alloc] initWithFilename:pname database:database title:nil]; - if (newMolecule != nil) - { - [molecules addObject:newMolecule]; - if (rootViewController.tableViewController != nil) - { - [rootViewController.tableViewController.tableView reloadData]; - } - } - } - } - -} - -#pragma mark - -#pragma mark Status update methods - -- (void)showStatusIndicator; -{ - [[NSNotificationCenter defaultCenter] postNotificationName:@"FileLoadingStarted" object:NSLocalizedStringFromTable(@"Initializing database...", @"Localized", nil)]; -} - -- (void)showDownloadIndicator; -{ - [[NSNotificationCenter defaultCenter] postNotificationName:@"FileLoadingStarted" object:NSLocalizedStringFromTable(@"Downloading molecule...", @"Localized", nil)]; -} - -- (void)updateStatusIndicator; -{ - -} - -- (void)hideStatusIndicator; -{ - [[NSNotificationCenter defaultCenter] postNotificationName:@"FileLoadingEnded" object:nil]; -} - -#pragma mark - -#pragma mark Flow control - -- (void)applicationWillResignActive:(UIApplication *)application -{ - [[NSUserDefaults standardUserDefaults] synchronize]; -} - -- (void)applicationDidBecomeActive:(UIApplication *)application -{ -} - -- (void)applicationWillTerminate:(UIApplication *)application -{ - if (database != nil) - { - [rootViewController cancelMoleculeLoading]; - [self disconnectFromDatabase]; - } -} - -- (void)applicationWillEnterForeground:(UIApplication *)application -{ -/* if (database == nil) - { - [self connectToDatabase]; - }*/ - - [self loadMissingMoleculesIntoDatabase]; -} - -- (void)applicationDidEnterBackground:(UIApplication *)application -{ - [rootViewController cancelMoleculeLoading]; -// [self disconnectFromDatabase]; -} - - -#pragma mark - -#pragma mark Custom molecule download methods - -- (BOOL)handleCustomURLScheme:(NSURL *)url; -{ - if (url != nil) - { - isHandlingCustomURLMoleculeDownload = YES; - [NSThread sleepForTimeInterval:0.5]; // Wait for database to load - - NSString *pathComponentForCustomURL = [[url host] stringByAppendingString:[url path]]; - NSString *locationOfRemotePDBFile = [NSString stringWithFormat:@"http://%@", pathComponentForCustomURL]; - nameOfDownloadedMolecule = [pathComponentForCustomURL lastPathComponent]; - - // Check to make sure that the file has not already been downloaded, if so, just switch to it - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); - NSString *documentsDirectory = [paths objectAtIndex:0]; - - [initialDatabaseLoadLock lock]; - - if ([[NSFileManager defaultManager] fileExistsAtPath:[documentsDirectory stringByAppendingPathComponent:nameOfDownloadedMolecule]]) - { - - NSInteger indexForMoleculeMatchingThisName = 0, currentIndex = 0; - for (SLSMolecule *currentMolecule in molecules) - { - if ([[currentMolecule filename] isEqualToString:nameOfDownloadedMolecule]) - { - indexForMoleculeMatchingThisName = currentIndex; - break; - } - currentIndex++; - } - - if (rootViewController.tableViewController == nil) - { - [rootViewController selectedMoleculeDidChange:indexForMoleculeMatchingThisName]; - } - else - { - if ([SLSMoleculeAppDelegate isRunningOniPad]) - { - [rootViewController.tableViewController tableView:rootViewController.tableViewController.tableView didSelectRowAtIndexPath:[NSIndexPath indexPathForRow:indexForMoleculeMatchingThisName inSection:0]]; - } - else - { - [rootViewController.tableViewController tableView:rootViewController.tableViewController.tableView didSelectRowAtIndexPath:[NSIndexPath indexPathForRow:(indexForMoleculeMatchingThisName + 1) inSection:0]]; - } - } - [rootViewController loadInitialMolecule]; - - nameOfDownloadedMolecule = nil; - [initialDatabaseLoadLock unlock]; - return YES; - } - [initialDatabaseLoadLock unlock]; - - - [rootViewController cancelMoleculeLoading]; - - [NSThread sleepForTimeInterval:0.1]; // Wait for cancel action to take place - - // Determine if this is a file being passed in, or something to download - if ([url isFileURL]) - { - - nameOfDownloadedMolecule = nil; - } - else - { - downloadCancelled = NO; - - // Start download of new molecule - [self showDownloadIndicator]; - - - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; - NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:locationOfRemotePDBFile] - cachePolicy:NSURLRequestUseProtocolCachePolicy - timeoutInterval:60.0f]; - downloadConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self]; - if (downloadConnection) - { - downloadedFileContents = [NSMutableData data]; - } - else - { - // inform the user that the download could not be made - return NO; - } - } - } - return YES; -} - -- (void)downloadCompleted; -{ - downloadConnection = nil; - - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; - - downloadedFileContents = nil; - [self hideStatusIndicator]; - nameOfDownloadedMolecule = nil; -} - -- (void)saveMoleculeWithData:(NSData *)moleculeData toFilename:(NSString *)filename; -{ - [initialDatabaseLoadLock lock]; - - if (moleculeData != nil) - { - // Add the new protein to the list by gunzipping the data and pulling out the title - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); - NSString *documentsDirectory = [paths objectAtIndex:0]; - - NSError *error = nil; - BOOL writeStatus; - if (isGzipCompressionUsedOnDownload) - { - writeStatus = [moleculeData writeToFile:[documentsDirectory stringByAppendingPathComponent:filename] options:NSAtomicWrite error:&error]; -// writeStatus = [[moleculeData gzipDeflate] writeToFile:[documentsDirectory stringByAppendingPathComponent:filename] options:NSAtomicWrite error:&error]; -// NSLog(@"Decompressing"); - } - else - writeStatus = [moleculeData writeToFile:[documentsDirectory stringByAppendingPathComponent:filename] options:NSAtomicWrite error:&error]; - - if (!writeStatus) - { - // TODO: Do some error handling here - return; - } - - SLSMolecule *newMolecule = [[SLSMolecule alloc] initWithFilename:filename database:database title:nil]; - if (newMolecule == nil) - { - UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedStringFromTable(@"Error in downloaded file", @"Localized", nil) message:NSLocalizedStringFromTable(@"The molecule file is either corrupted or not of a supported format", @"Localized", nil) - delegate:self cancelButtonTitle:NSLocalizedStringFromTable(@"OK", @"Localized", nil) otherButtonTitles:nil, nil]; - [alert show]; - - // Delete the corrupted or sunsupported file - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); - NSString *documentsDirectory = [paths objectAtIndex:0]; - - NSError *error = nil; - if (![[NSFileManager defaultManager] removeItemAtPath:[documentsDirectory stringByAppendingPathComponent:filename] error:&error]) - { - UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedStringFromTable(@"Could not delete file", @"Localized", nil) message:[error localizedDescription] - delegate:self cancelButtonTitle:NSLocalizedStringFromTable(@"OK", @"Localized", nil) otherButtonTitles:nil, nil]; - [alert show]; - return; - } - - } - else - { - [molecules addObject:newMolecule]; - - [rootViewController updateTableListOfMolecules]; - [rootViewController selectedMoleculeDidChange:([molecules count] - 1)]; - [rootViewController loadInitialMolecule]; - - } - } - [initialDatabaseLoadLock unlock]; - -} - -#pragma mark - -#pragma mark URL connection delegate methods - -- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error; -{ - UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedStringFromTable(@"Connection failed", @"Localized", nil) message:NSLocalizedStringFromTable(@"Could not connect to the Protein Data Bank", @"Localized", nil) - delegate:self cancelButtonTitle:NSLocalizedStringFromTable(@"OK", @"Localized", nil) otherButtonTitles: nil, nil]; - [alert show]; - - [self downloadCompleted]; -} - -- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data; -{ - // Concatenate the new data with the existing data to build up the downloaded file - // Update the status of the download - - if (downloadCancelled) - { - [connection cancel]; - [self downloadCompleted]; - downloadCancelled = NO; - return; - } - [downloadedFileContents appendData:data]; -} - -- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response; -{ -// downloadFileSize = [response expectedContentLength]; - NSString * contentEncoding = [[(NSHTTPURLResponse *)response allHeaderFields] valueForKey:@"Content-Encoding"]; -// NSDictionary *allHeaders = [(NSHTTPURLResponse *)response allHeaderFields]; - isGzipCompressionUsedOnDownload = [[contentEncoding lowercaseString] isEqualToString:@"gzip"]; - -// for (id key in allHeaders) -// { -// NSLog(@"key: %@, value: %@", key, [allHeaders objectForKey:key]); -// } -// -// if (isGzipCompressionUsedOnDownload) -// NSLog(@"gzipping"); - - // Stop the spinning wheel and start the status bar for download - if ([response textEncodingName] != nil) - { - UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedStringFromTable(@"Could not find file", @"Localized", nil) message:[NSString stringWithFormat:NSLocalizedStringFromTable(@"No such file exists on the server: %@", @"Localized", nil), nameOfDownloadedMolecule] - delegate:self cancelButtonTitle:NSLocalizedStringFromTable(@"OK", @"Localized", nil) otherButtonTitles: nil, nil]; - [alert show]; - [connection cancel]; - [self downloadCompleted]; - return; - } -} - -- (void)connectionDidFinishLoading:(NSURLConnection *)connection; -{ - -// UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Download completed" message:@"Download completed" -// delegate:self cancelButtonTitle:NSLocalizedStringFromTable(@"OK", @"Localized", nil) otherButtonTitles: nil]; -// [alert show]; -// [alert release]; - - // Close off the file and write it to disk - [self saveMoleculeWithData:downloadedFileContents toFilename:nameOfDownloadedMolecule]; - - [self downloadCompleted]; -} - -@end diff --git a/SLSMoleculeCustomDownloadViewController.h b/SLSMoleculeCustomDownloadViewController.h deleted file mode 100644 index 7d7890c..0000000 --- a/SLSMoleculeCustomDownloadViewController.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// SLSMoleculeCustomDownloadViewController.h -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 11/10/2008. - -#import - -@interface SLSMoleculeCustomDownloadViewController : UIViewController -{ - UITextField *urlInput; -// UIActivityIndicatorView *downloadActivityIndicator; -} - -@end \ No newline at end of file diff --git a/SLSMoleculeCustomDownloadViewController.m b/SLSMoleculeCustomDownloadViewController.m deleted file mode 100644 index 14e82c2..0000000 --- a/SLSMoleculeCustomDownloadViewController.m +++ /dev/null @@ -1,178 +0,0 @@ -// -// SLSMoleculeCustomDownloadView.m -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 11/10/2008. - -#import -#import "SLSMoleculeCustomDownloadViewController.h" - -#import "SLSMolecule.h" -#import "SLSMoleculeAppDelegate.h" - -@implementation SLSMoleculeCustomDownloadViewController - -#pragma mark - -#pragma mark Initialization and breakdown - -- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil -{ - if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) - { - self.title = NSLocalizedStringFromTable(@"Custom Location", @"Localized", @""); - self.navigationItem.rightBarButtonItem = nil; - } - return self; -} - - - -// Implement loadView to create a view hierarchy programmatically. -- (void)loadView -{ -// UIView *mainView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; - UIView *mainView = [[UIView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 240.0f)]; - mainView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - - if ([SLSMoleculeAppDelegate isRunningOniPad]) - { - mainView.backgroundColor = [UIColor whiteColor]; - self.preferredContentSize = CGSizeMake(320.0f, 100.0f); - } - else - { - mainView.backgroundColor = [UIColor groupTableViewBackgroundColor]; - - } - mainView.autoresizesSubviews = YES; - self.view = mainView; - - UILabel *descriptionLabel = [[UILabel alloc] initWithFrame:CGRectMake(20.0f, 20.0f, mainView.bounds.size.width - 40.0f, 60.0f)]; - descriptionLabel.text = NSLocalizedStringFromTable(@"Type or paste in the location of the custom molecule and press Go to begin the download.", @"Localized", @""); - descriptionLabel.numberOfLines = 3; - if ([SLSMoleculeAppDelegate isRunningOniPad]) - descriptionLabel.backgroundColor = [UIColor whiteColor]; - else - descriptionLabel.backgroundColor = [UIColor clearColor]; - [mainView addSubview:descriptionLabel]; - - urlInput = [[UITextField alloc] initWithFrame:CGRectMake(20.0f, 100.0f, mainView.bounds.size.width - 40.0f, 30.0f)]; - urlInput.placeholder = NSLocalizedStringFromTable(@"Molecule location", @"Localized", nil); - urlInput.delegate = self; - urlInput.adjustsFontSizeToFitWidth = YES; - // urlInput.font = [UIFont systemFontOfSize:14]; - urlInput.borderStyle = UITextBorderStyleRoundedRect; - urlInput.keyboardType = UIKeyboardTypeURL; - urlInput.autocorrectionType = UITextAutocorrectionTypeNo; - urlInput.autocapitalizationType = UITextAutocapitalizationTypeNone; - urlInput.returnKeyType = UIReturnKeyGo; - urlInput.enablesReturnKeyAutomatically = YES; - - [mainView addSubview:urlInput]; - [urlInput becomeFirstResponder]; - - - -} - -/* -// Implement viewDidLoad to do additional setup after loading the view. -- (void)viewDidLoad { - [super viewDidLoad]; -} -*/ - - -- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation -{ - return YES; - // Return YES for supported orientations -// return (interfaceOrientation == UIInterfaceOrientationPortrait); -} - - -- (void)didReceiveMemoryWarning -{ -} - - - -#pragma mark - -#pragma mark Webview delegate methods - -- (void)webViewDidStartLoad:(UIWebView *)webView -{ - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; -} - -- (void)webViewDidFinishLoad:(UIWebView *)thewebView -{ - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; -} - -- (void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error -{ - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; - - UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedStringFromTable(@"Error in loading custom location", @"Localized", nil) message:NSLocalizedStringFromTable(@"The address could not be reached", @"Localized", nil) - delegate:self cancelButtonTitle:NSLocalizedStringFromTable(@"OK", @"Localized", nil) otherButtonTitles: nil, nil]; - [alert show]; -} - -- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType -{ - NSURL *url = [request URL]; - if ([SLSMolecule isFiletypeSupportedForFile:[url path]]) - { -// [self.delegate customURLSelectedForMoleculeDownload:url]; - return NO; - } - return YES; -} - -#pragma mark - -#pragma mark UITextFieldDelegate - -- (BOOL)textFieldShouldReturn:(UITextField *)textField -{ - - // Either load the url in the web view or start the download process of the new molecule - - NSString *urlString = urlInput.text; - NSRange locationOfSubstring = [urlString rangeOfString:@"://"]; - if (locationOfSubstring.location == NSNotFound) - urlString = [NSString stringWithFormat:@"http://%@", urlString]; - - if ([SLSMolecule isFiletypeSupportedForFile:urlInput.text]) - { - [urlInput resignFirstResponder]; - [[NSNotificationCenter defaultCenter] postNotificationName:@"CustomURLForMoleculeSelected" object:[NSURL URLWithString:urlString]]; - - if ([SLSMoleculeAppDelegate isRunningOniPad]) - { - [self.navigationController popViewControllerAnimated:YES]; - } - } - else - { - UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedStringFromTable(@"Error in loading custom location", @"Localized", nil) message:NSLocalizedStringFromTable(@"The address does not contain a file of a supported molecule type.", @"Localized", nil) - delegate:self cancelButtonTitle:NSLocalizedStringFromTable(@"OK", @"Localized", nil) otherButtonTitles: nil, nil]; - [alert show]; - } - - return YES; -} - -#pragma mark - -#pragma mark UIViewController methods - -- (void)viewWillDisappear:(BOOL)animated -{ - urlInput.delegate = nil; - - [super viewWillDisappear:animated]; -} - -@end diff --git a/SLSMoleculeDataSourceViewController.h b/SLSMoleculeDataSourceViewController.h deleted file mode 100644 index 1f8e3ad..0000000 --- a/SLSMoleculeDataSourceViewController.h +++ /dev/null @@ -1,17 +0,0 @@ -// -// SLSMoleculeDataSourceViewController.h -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 11/9/2008. -// -// This handles the table view listing the different data sources Molecule supports - -#import - -@interface SLSMoleculeDataSourceViewController : UITableViewController -{ -} - -@end diff --git a/SLSMoleculeDataSourceViewController.m b/SLSMoleculeDataSourceViewController.m deleted file mode 100644 index 45c1822..0000000 --- a/SLSMoleculeDataSourceViewController.m +++ /dev/null @@ -1,119 +0,0 @@ -// -// SLSMoleculeDataSourceViewController.m -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 11/9/2008. -// -// This handles the table view listing the different data sources Molecule supports - -#import "SLSMoleculeDataSourceViewController.h" -#import "SLSMoleculeSearchViewController.h" -#import "SLSMoleculeAppDelegate.h" - -@implementation SLSMoleculeDataSourceViewController - -#pragma mark - -#pragma mark Initialization and teardown - -- (id)initWithStyle:(UITableViewStyle)style -{ - if ((self = [super initWithStyle:style])) - { - if ([SLSMoleculeAppDelegate isRunningOniPad]) - { - self.preferredContentSize = CGSizeMake(320.0, 600.0); - } - - self.navigationItem.title = NSLocalizedStringFromTable(@"Online Data Source", @"Localized", nil); - self.navigationItem.rightBarButtonItem = nil; - } - return self; -} - - -- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation -{ - return YES; -} - -#pragma mark - -#pragma mark Table view methods - -- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView -{ - return 1; -} - - -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section -{ - return 2; -} - - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath -{ - NSInteger index = [indexPath row]; - UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Download"]; - if (cell == nil) - { -// cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"Download"] autorelease]; - cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Download"]; - cell.textLabel.textColor = [UIColor blackColor]; - } - - cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; - cell.textLabel.textColor = [UIColor blackColor]; - - switch (index) - { - case 0: cell.textLabel.text = NSLocalizedStringFromTable(@"RCSB Protein Data Bank", @"Localized", nil); break; - case 1: cell.textLabel.text = NSLocalizedStringFromTable(@"Custom Location", @"Localized", nil); break; - } - - return cell; -} - - -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath -{ - NSInteger index = [indexPath row]; - switch (index) - { - case 0: - { - // Go to the PDB search view - SLSMoleculeSearchViewController *searchViewController = [[SLSMoleculeSearchViewController alloc] initWithStyle:UITableViewStylePlain]; - - [self.navigationController pushViewController:searchViewController animated:YES]; - }; break; - case 1: - { - // Go to the custom URL download view - SLSMoleculeCustomDownloadViewController *customURLViewController = [[SLSMoleculeCustomDownloadViewController alloc] initWithNibName:nil bundle:nil]; - - [self.navigationController pushViewController:customURLViewController animated:YES]; - }; break; - } - -} - -#pragma mark - -#pragma mark MoleculeDownloadDelegate protocol method - -/* -- (void)viewDidDisappear:(BOOL)animated { -} -*/ - -- (void)didReceiveMemoryWarning -{ -} - -#pragma mark - -#pragma mark Accessors - -@end - diff --git a/SLSMoleculeDetailViewController.h b/SLSMoleculeDetailViewController.h deleted file mode 100644 index a7720a6..0000000 --- a/SLSMoleculeDetailViewController.h +++ /dev/null @@ -1,28 +0,0 @@ -// -// SLSMoleculeDetailViewController.h -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 7/5/2008. -// -// This controller manages the detail view of the molecule's properties, such as author, publication, etc. - -#import - -@class SLSMolecule; - -@interface SLSMoleculeDetailViewController : UITableViewController -{ - SLSMolecule *molecule; - - UILabel *nameLabel; -} - -@property (nonatomic, strong) SLSMolecule *molecule; -- (id)initWithStyle:(UITableViewStyle)style andMolecule:(SLSMolecule *)newMolecule; - -- (UILabel *)createLabelForIndexPath:(NSIndexPath *)indexPath; -- (NSString *)textForIndexPath:(NSIndexPath *)indexPath; - -@end \ No newline at end of file diff --git a/SLSMoleculeDetailViewController.m b/SLSMoleculeDetailViewController.m deleted file mode 100644 index 112a5b0..0000000 --- a/SLSMoleculeDetailViewController.m +++ /dev/null @@ -1,349 +0,0 @@ -// -// SLSMoleculeDetailViewController.m -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 7/5/2008. -// -// This controller manages the detail view of the molecule's properties, such as author, publication, etc. - -#import "SLSMoleculeDetailViewController.h" -#import "SLSMolecule.h" -#import "SLSTextViewController.h" -#import "SLSMoleculeAppDelegate.h" - -#define DESCRIPTION_SECTION 0 -#define AUTHOR_SECTION 1 -#define STATISTICS_SECTION 2 -#define JOURNAL_SECTION 3 -#define SOURCE_SECTION 4 -#define SEQUENCE_SECTION 5 - -@implementation SLSMoleculeDetailViewController - - -- (id)initWithStyle:(UITableViewStyle)style andMolecule:(SLSMolecule *)newMolecule; -{ - if ((self = [super initWithStyle:style])) - { - self.view.frame = [[UIScreen mainScreen] bounds]; - self.view.autoresizesSubviews = YES; - self.molecule = newMolecule; - [newMolecule readMetadataFromDatabaseIfNecessary]; - self.title = molecule.compound; - -// UILabel *label= [[UILabel alloc] initWithFrame:CGRectMake(25.0f, 60.0f, 320.0f, 66.0f)]; - UILabel *label= [[UILabel alloc] initWithFrame:CGRectMake(45.0f, 60.0f, 320.0f, 66.0f)]; - label.textColor = [UIColor blackColor]; - label.font = [UIFont fontWithName:@"Helvetica" size:18.0]; - label.backgroundColor = [UIColor groupTableViewBackgroundColor]; - label.text = molecule.compound; - label.numberOfLines = 3; - label.lineBreakMode = NSLineBreakByWordWrapping; - label.textAlignment = NSTextAlignmentCenter; - // label.text = @"Text"; - - self.tableView.tableHeaderView = label; - - if ([SLSMoleculeAppDelegate isRunningOniPad]) - { - self.preferredContentSize = CGSizeMake(320.0, 600.0); - } - } - return self; -} - - -- (void)viewDidLoad -{ -// UILabel *label= [[UILabel alloc] initWithFrame:CGRectZero]; - [super viewDidLoad]; -} - - -- (void)viewWillAppear:(BOOL)animated -{ - [super viewWillAppear:animated]; -} - -- (void)viewDidAppear:(BOOL)animated -{ - [super viewDidAppear:animated]; -} - -- (void)viewWillDisappear:(BOOL)animated -{ -} - -- (void)viewDidDisappear:(BOOL)animated -{ -} - -- (void)didReceiveMemoryWarning { -} - -#pragma mark - -#pragma mark UITableView Delegate/Datasource - -- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView -{ - return 6; -} - - -- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section -{ - switch (section) - { - case DESCRIPTION_SECTION: - return NSLocalizedStringFromTable(@"Description", @"Localized", nil); - case STATISTICS_SECTION: - return NSLocalizedStringFromTable(@"Statistics", @"Localized", nil); - case JOURNAL_SECTION: - return NSLocalizedStringFromTable(@"Journal", @"Localized", nil); - case SOURCE_SECTION: - return NSLocalizedStringFromTable(@"Source", @"Localized", nil); - case AUTHOR_SECTION: - return NSLocalizedStringFromTable(@"Author(s)", @"Localized", nil); - case SEQUENCE_SECTION: - return NSLocalizedStringFromTable(@"Sequence", @"Localized", nil); - default: - break; - } - return nil; -} - -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section -{ - NSInteger rows = 0; - - switch (section) - { - case DESCRIPTION_SECTION: - case AUTHOR_SECTION: - case SOURCE_SECTION: - case SEQUENCE_SECTION: - rows = 1; - break; - case STATISTICS_SECTION: - rows = 4; - break; - case JOURNAL_SECTION: - rows = 3; - break; - default: - break; - } - return rows; -} - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { - - if (indexPath.section == STATISTICS_SECTION) - { - static NSString *StatisticsCellIdentifier = @"StatisticsCell"; - - UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:StatisticsCellIdentifier]; - if (cell == nil) - { - cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:StatisticsCellIdentifier]; - - cell.detailTextLabel.textColor = [UIColor colorWithRed:50.0f/255.0f green:79.0f/255.0f blue:133.0f/255.0f alpha:1.0f]; - cell.detailTextLabel.highlightedTextColor = [UIColor whiteColor]; - } - - switch (indexPath.row) - { - case 0: - { - cell.textLabel.text = NSLocalizedStringFromTable(@"File name", @"Localized", nil); - cell.detailTextLabel.text = molecule.filename; - }; break; - case 1: - { - cell.textLabel.text = NSLocalizedStringFromTable(@"Number of atoms", @"Localized", nil); - cell.detailTextLabel.text = [NSString stringWithFormat:@"%d", molecule.numberOfAtoms]; - }; break; - case 2: - { - cell.textLabel.text =NSLocalizedStringFromTable(@"Number of structures", @"Localized", nil); - cell.detailTextLabel.text = [NSString stringWithFormat:@"%d", molecule.numberOfStructures]; - }; break; - case 3: - { - cell.textLabel.text = NSLocalizedStringFromTable(@"Current structure", @"Localized", nil); - cell.detailTextLabel.text = [NSString stringWithFormat:@"%d", molecule.numberOfStructureBeingDisplayed]; - }; break; - } - return cell; - } - - static NSString *MyIdentifier = @"MyIdentifier"; - UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier]; - if (cell == nil) { -// cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:MyIdentifier] autorelease]; - cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:MyIdentifier]; - } - cell.textLabel.text = [self textForIndexPath:indexPath]; - - -// static NSString *DetailedTextCell = @"DetailedTextCell"; -// -// CellTextView *cell = (CellTextView *)[tableView dequeueReusableCellWithIdentifier:DetailedTextCell]; -// -// if (cell == nil) -// { -// cell = [[[CellTextView alloc] initWithFrame:CGRectZero reuseIdentifier:DetailedTextCell] autorelease]; -// } -// -// cell.view = [self createLabelForIndexPath:indexPath]; - return cell; -} - -- (UILabel *)createLabelForIndexPath:(NSIndexPath *)indexPath; -{ - NSString *text = nil; - switch (indexPath.section) - { - case DESCRIPTION_SECTION: // type -- should be selectable -> checkbox - text = molecule.title; - break; - case AUTHOR_SECTION: // instructions - text = molecule.author; - break; - case JOURNAL_SECTION: - { - switch (indexPath.row) - { - case 0: text = molecule.journalTitle; break; - case 1: text = molecule.journalAuthor; break; - case 2: text = molecule.journalReference; break; - } - }; break; - case SOURCE_SECTION: - text = molecule.source; - break; - case SEQUENCE_SECTION: - text = molecule.sequence; - break; - default: - break; - } - -// CGRect frame = CGRectMake(0.0, 0.0, 100.0, 100.0); - - UILabel *label= [[UILabel alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 100.0f, 100.0f)]; - label.textColor = [UIColor blackColor]; -// textView.font = [UIFont fontWithName:@"Helvetica" size:18.0]; -// textView.editable = NO; - label.backgroundColor = [UIColor whiteColor]; - - label.text = text; - - return label; -} - -//#define HEIGHTPERLINE 23.0 -// -//- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath -//{ -// CGFloat result; -// -// switch (indexPath.section) -// { -// case DESCRIPTION_SECTION: // type -- should be selectable -> checkbox -// result = (float)[molecule.title length] * HEIGHTPERLINE; -// break; -// case AUTHOR_SECTION: // instructions -// result = (float)[molecule.author length] * HEIGHTPERLINE; -// break; -// case JOURNAL_SECTION: -// { -// switch (indexPath.row) -// { -// case 0: result = (float)[molecule.journalTitle length] * HEIGHTPERLINE; break; -// case 1: result = (float)[molecule.journalAuthor length] * HEIGHTPERLINE; break; -// case 2: result = (float)[molecule.journalReference length] * HEIGHTPERLINE; break; -// } -// }; break; -// case SOURCE_SECTION: -// result = (float)[molecule.source length] * HEIGHTPERLINE; -// break; -// case SEQUENCE_SECTION: -// result = (float)[molecule.sequence length] * HEIGHTPERLINE; -// break; -// default: -// result = 43.0; -// break; -// } -// -// return result; -//} - -- (NSString *)textForIndexPath:(NSIndexPath *)indexPath; -{ - NSString *text; - switch (indexPath.section) - { - case DESCRIPTION_SECTION: - text = molecule.title; - break; - case AUTHOR_SECTION: - text = molecule.author; - break; - case JOURNAL_SECTION: - { - switch (indexPath.row) - { - case 0: text = molecule.journalTitle; break; - case 1: text = molecule.journalAuthor; break; - case 2: text = molecule.journalReference; break; - default: text = @""; break; - } - }; break; - case SOURCE_SECTION: - text = molecule.source; - break; - case SEQUENCE_SECTION: - text = molecule.sequence; - break; - default: - text = @""; - break; - } - - return [text stringByReplacingOccurrencesOfString:@"\n" withString:@" "]; - -} - -- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath; -{ - if (indexPath.section == STATISTICS_SECTION) - return nil; - - return indexPath; -} - -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath -{ - if (indexPath.section != STATISTICS_SECTION) - { - SLSTextViewController *nextViewController = [[SLSTextViewController alloc] initWithTitle:[self tableView:tableView titleForHeaderInSection:indexPath.section] andContent:[self textForIndexPath:indexPath]]; - [self.navigationController pushViewController:nextViewController animated:YES]; - } - -} - -- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { - // Overriden to allow any orientation. - return YES; -} - -#pragma mark - -#pragma mark Accessors - -@synthesize molecule; - -@end - diff --git a/SLSMoleculeDownloadController.h b/SLSMoleculeDownloadController.h deleted file mode 100644 index 24ecbcc..0000000 --- a/SLSMoleculeDownloadController.h +++ /dev/null @@ -1,34 +0,0 @@ -// -// SLSMoleculeDownloadViewController.h -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 7/2/2008. -// -// This controller manages the pop-up modal view for downloading new molecules from the Protein Data Bank - -#import - -typedef enum { PUBCHEMSEARCH, PROTEINDATABANKSEARCH } SLSSearchType; - -@interface SLSMoleculeDownloadController : NSObject -{ - NSString *codeForCurrentlyDownloadingMolecule, *titleForCurrentlyDownloadingMolecule; - NSMutableData *downloadedFileContents; - - long long downloadFileSize; - BOOL downloadCancelled; - NSURLConnection *downloadConnection; - SLSSearchType searchType; -} - -// Initialization and teardown -- (id)initWithID:(NSString *)pdbCode title:(NSString *)title searchType:(SLSSearchType)newSearchType; - -- (void)downloadNewMolecule; -- (BOOL)downloadMolecule; -- (void)downloadCompleted; -- (void)cancelDownload; - -@end \ No newline at end of file diff --git a/SLSMoleculeDownloadController.m b/SLSMoleculeDownloadController.m deleted file mode 100644 index a6df984..0000000 --- a/SLSMoleculeDownloadController.m +++ /dev/null @@ -1,262 +0,0 @@ -// -// SLSMoleculeDownloadViewController.m -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 7/2/2008. -// -// This controller manages the pop-up modal view for downloading new molecules from the Protein Data Bank - -#import "SLSMoleculeDownloadController.h" -#import "SLSMoleculeAppDelegate.h" - -@implementation SLSMoleculeDownloadController - -- (id)initWithID:(NSString *)pdbCode title:(NSString *)title searchType:(SLSSearchType)newSearchType; -{ - if ((self = [super init])) - { - // Initialization code - downloadedFileContents = nil; - downloadCancelled = NO; - - searchType = newSearchType; - - codeForCurrentlyDownloadingMolecule = [pdbCode copy]; - titleForCurrentlyDownloadingMolecule = [title copy]; - } - return self; -} - - -- (void)dealloc; -{ - [self cancelDownload]; -} - -#pragma mark - -#pragma mark Protein downloading - -- (void)downloadNewMolecule; -{ - // Check if you already have a protein by that name - // TODO: Put this check in the init method to grey out download button - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); - NSString *documentsDirectory = [paths objectAtIndex:0]; - - NSString *fileExtension = nil; - if (searchType == PROTEINDATABANKSEARCH) - { - fileExtension = @"pdb.gz"; - } - else - { - fileExtension = @"sdf"; - } - - if ([[NSFileManager defaultManager] fileExistsAtPath:[documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"%@.%@", codeForCurrentlyDownloadingMolecule, fileExtension]]]) - { - UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedStringFromTable(@"File already exists", @"Localized", nil) message:NSLocalizedStringFromTable(@"This molecule has already been downloaded", @"Localized", nil) - delegate:self cancelButtonTitle:NSLocalizedStringFromTable(@"OK", @"Localized", nil) otherButtonTitles: nil, nil]; - [alert show]; - [[NSNotificationCenter defaultCenter] postNotificationName:@"MoleculeDidFinishDownloading" object:nil]; - return; - } - - if (![self downloadMolecule]) - { - NSString *errorMessage = nil; - - if (searchType == PROTEINDATABANKSEARCH) - { - errorMessage = NSLocalizedStringFromTable(@"Could not connect to the Protein Data Bank", @"Localized", nil); - } - else - { - errorMessage = NSLocalizedStringFromTable(@"Could not connect to PubChem", @"Localized", nil); - } - - UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedStringFromTable(@"Connection failed", @"Localized", nil) message:errorMessage - delegate:self cancelButtonTitle:NSLocalizedStringFromTable(@"OK", @"Localized", nil) otherButtonTitles: nil, nil]; - [alert show]; - [[NSNotificationCenter defaultCenter] postNotificationName:@"MoleculeDidFinishDownloading" object:nil]; - return; - } -} - -- (BOOL)downloadMolecule; -{ - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; -// downloadStatusText.hidden = NO; -// downloadStatusText.text = NSLocalizedStringFromTable(@"Connecting...", @"Localized", nil); - -// NSString *locationOfRemotePDBFile = [NSString stringWithFormat:@"http://www.sunsetlakesoftware.com/sites/default/files/%@.pdb.gz", pdbCode]; - NSString *locationOfRemoteFile = nil; - if (searchType == PROTEINDATABANKSEARCH) - { - locationOfRemoteFile = [NSString stringWithFormat:@"http://www.rcsb.org/pdb/files/%@.pdb.gz", codeForCurrentlyDownloadingMolecule]; - } - else - { - locationOfRemoteFile = [NSString stringWithFormat:@"http://pubchem.ncbi.nlm.nih.gov/summary/summary.cgi?cid=%@&disopt=3DSaveSDF", codeForCurrentlyDownloadingMolecule]; - } - - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; - NSURLRequest *theRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:locationOfRemoteFile] - cachePolicy:NSURLRequestUseProtocolCachePolicy - timeoutInterval:60.0]; - downloadConnection = [[NSURLConnection alloc] initWithRequest:theRequest delegate:self]; - if (downloadConnection) - { - // Create the NSMutableData that will hold - // the received data - // receivedData is declared as a method instance elsewhere - downloadedFileContents = [NSMutableData data]; - } - else - { - // inform the user that the download could not be made - return NO; - } - return YES; -} - -- (void)downloadCompleted; -{ - downloadConnection = nil; - - - downloadedFileContents = nil; - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; -} - -- (void)cancelDownload; -{ - downloadCancelled = YES; -} - -#pragma mark - -#pragma mark URL connection delegate methods - -- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error; -{ - NSString *errorMessage = nil; - - if (searchType == PROTEINDATABANKSEARCH) - { - errorMessage = NSLocalizedStringFromTable(@"Could not connect to the Protein Data Bank", @"Localized", nil); - } - else - { - errorMessage = NSLocalizedStringFromTable(@"Could not connect to PubChem", @"Localized", nil); - } - - UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedStringFromTable(@"Connection failed", @"Localized", nil) message:errorMessage - delegate:self cancelButtonTitle:NSLocalizedStringFromTable(@"OK", @"Localized", nil) otherButtonTitles: nil, nil]; - [alert show]; - - [self downloadCompleted]; -} - -- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data; -{ - // Concatenate the new data with the existing data to build up the downloaded file - // Update the status of the download - - if (downloadCancelled) - { - [connection cancel]; - [self downloadCompleted]; - downloadCancelled = NO; - return; - } - [downloadedFileContents appendData:data]; -// downloadStatusBar.progress = (float)[downloadedFileContents length] / (float)downloadFileSize; -// downloadStatusText.text = NSLocalizedStringFromTable(@"Downloading", @"Localized", nil); -} - -- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response; -{ - downloadFileSize = [response expectedContentLength]; - - // Stop the spinning wheel and start the status bar for download - if ([response textEncodingName] != nil) - { - NSString *errorMessage = nil; - - if (searchType == PROTEINDATABANKSEARCH) - { - errorMessage = [NSString stringWithFormat:NSLocalizedStringFromTable(@"No protein with the code %@ exists in the data bank", @"Localized", nil), codeForCurrentlyDownloadingMolecule]; - } - else - { - errorMessage = [NSString stringWithFormat:NSLocalizedStringFromTable(@"No structure file for the compound with the code %@ exists at PubChem", @"Localized", nil), codeForCurrentlyDownloadingMolecule]; - } - - UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedStringFromTable(@"Could not find file", @"Localized", nil) message:errorMessage - delegate:self cancelButtonTitle:NSLocalizedStringFromTable(@"OK", @"Localized", nil) otherButtonTitles: nil, nil]; - [alert show]; - [connection cancel]; - [self downloadCompleted]; - return; - } - - if (downloadFileSize > 0) - { - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; - } -// downloadStatusText.text = NSLocalizedStringFromTable(@"Connected", @"Localized", nil); - - // TODO: Deal with a 404 error by checking filetype header -} - -- (void)connectionDidFinishLoading:(NSURLConnection *)connection; -{ -// downloadStatusText.text = NSLocalizedStringFromTable(@"Processing...", @"Localized", nil); - - // Close off the file and write it to disk - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); - NSString *documentsDirectory = [paths objectAtIndex:0]; - - NSString *fileExtension = nil; - if (searchType == PROTEINDATABANKSEARCH) - { - fileExtension = @"pdb.gz"; - } - else - { - fileExtension = @"sdf"; - } - - NSString *filename = [NSString stringWithFormat:@"%@.%@", codeForCurrentlyDownloadingMolecule, fileExtension]; - - NSError *error = nil; - if (![downloadedFileContents writeToFile:[documentsDirectory stringByAppendingPathComponent:filename] options:NSAtomicWrite error:&error]) - { - // TODO: Do some error handling here - return; - } - - // Notify about the addition of the new molecule - if (searchType == PROTEINDATABANKSEARCH) - { - [[NSNotificationCenter defaultCenter] postNotificationName:@"MoleculeDidFinishDownloading" object:filename]; - } - else - { - [[NSNotificationCenter defaultCenter] postNotificationName:@"MoleculeDidFinishDownloading" object:filename userInfo:[NSDictionary dictionaryWithObject:titleForCurrentlyDownloadingMolecule forKey:@"title"]]; - } - -// if ([SLSMoleculeAppDelegate isRunningOniPad]) -// { -// [self.navigationController popViewControllerAnimated:YES]; -// } - - [self downloadCompleted]; -} - -#pragma mark - -#pragma mark Accessors - -@end diff --git a/SLSMoleculeDownloadView.xib b/SLSMoleculeDownloadView.xib deleted file mode 100644 index 0e22906..0000000 --- a/SLSMoleculeDownloadView.xib +++ /dev/null @@ -1,514 +0,0 @@ - - - - 512 - 9E17 - 670 - 949.33 - 352.00 - - YES - - - - YES - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - YES - - IBFilesOwner - - - IBFirstResponder - - - - 292 - {320, 460} - - 3 - MQA - - 2 - - - NO - - - - 292 - - YES - - - 292 - {{20, 0}, {280, 127}} - - NO - YES - NO - Titleas afs;ijas;fij;weifj;woeijfwoiejf;oiwjefn iwe;foijwef;ijwe;fij;iw w;neoifnw;eifn;weoinfw;i - - 1 - MCAwIDAAA - - - 1 - 1.000000e+01 - 5 - 0 - - - - 292 - {{36, 155}, {247, 37}} - - NO - NO - 0 - 0 - - Helvetica-Bold - 1.500000e+01 - 16 - - 1 - Read more about this molecule - Read more about this molecule - Read more about this molecule - Read more about this molecule - - 1 - MSAxIDEAA - - - 1 - MC4xOTYwNzg0MyAwLjMwOTgwMzkzIDAuNTIxNTY4NjYAA - - - - - -2147483356 - {{84, 341}, {150, 9}} - - NO - YES - NO - - - - 292 - {{148, 313}, {20, 20}} - - NO - NO - NO - 2 - - - - -2147483356 - {{78, 284}, {162, 21}} - - NO - YES - NO - Checking status... - - - 1 - 1.000000e+01 - 1 - - - {320, 480} - - 9.929578e-01 - - - - 292 - - YES - - - 274 - {320, 416} - - NO - YES - YES - YES - - - - 292 - {{151, 197}, {20, 20}} - - NO - NO - NO - 2 - - - - -2147483356 - {{102, 168}, {118, 21}} - - NO - YES - NO - Loading page... - - - 1 - 1.000000e+01 - - - {320, 460} - - - 3 - MQA - - - NO - - - - - YES - - - view - - - - 4 - - - - pdbCodeSearchWebView - - - - 22 - - - - downloadStatusBar - - - - 32 - - - - delegate - - - - 34 - - - - indefiniteDownloadIndicator - - - - 36 - - - - downloadStatusText - - - - 38 - - - - moleculeTitleText - - - - 46 - - - - pdbDownloadDisplayView - - - - 48 - - - - pdbInformationDisplayButton - - - - 49 - - - - pdbInformationWebView - - - - 50 - - - - showWebPageForMolecule - - - 1 - - 54 - - - - webLoadingIndicator - - - - 58 - - - - webLoadingLabel - - - - 59 - - - - - YES - - 0 - - YES - - - - - - -1 - - - RmlsZSdzIE93bmVyA - - - -2 - - - - - 3 - - - Main download view - - - 5 - - - YES - - - - - - - - Molecule Download View - - - 6 - - - - - 13 - - - YES - - - - - - PDB Web Page - - - 16 - - - - - 30 - - - - - 35 - - - - - 37 - - - - - 8 - - - - - 56 - - - - - 57 - - - - - - - YES - - YES - -1.CustomClassName - -2.CustomClassName - 13.IBEditorWindowLastContentRect - 13.IBPluginDependency - 16.IBPluginDependency - 3.IBEditorWindowLastContentRect - 3.IBPluginDependency - 30.IBPluginDependency - 35.IBPluginDependency - 37.IBPluginDependency - 5.IBEditorWindowLastContentRect - 5.IBPluginDependency - 56.IBPluginDependency - 57.IBPluginDependency - 6.IBPluginDependency - 8.IBPluginDependency - - - YES - SLSMoleculeDownloadViewController - UIResponder - {{783, 92}, {320, 460}} - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - {{0, 274}, {320, 460}} - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - {{513, 170}, {320, 480}} - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - com.apple.InterfaceBuilder.IBCocoaTouchPlugin - - - - YES - - YES - - - YES - - - - - YES - - YES - - - YES - - - - 59 - - - - YES - - SLSMoleculeDownloadViewController - UIViewController - - YES - - YES - cancelDownload - downloadNewProtein - returnToDetailView - showWebPageForMolecule - - - YES - id - id - id - id - - - - YES - - YES - delegate - downloadStatusBar - downloadStatusText - indefiniteDownloadIndicator - moleculeTitleText - pdbCodeSearchWebView - pdbDownloadButton - pdbDownloadDisplayView - pdbInformationDisplayButton - pdbInformationWebView - webLoadingIndicator - webLoadingLabel - - - YES - id - UIProgressView - UILabel - UIActivityIndicatorView - UILabel - UIWebView - UIButton - UIView - UIButton - UIView - UIActivityIndicatorView - UILabel - - - - IBProjectSource - SLSMoleculeDownloadViewController.h - - - - - 0 - Molecules.xcodeproj - 3 - - diff --git a/SLSMoleculeGLView.h b/SLSMoleculeGLView.h deleted file mode 100644 index 67386e0..0000000 --- a/SLSMoleculeGLView.h +++ /dev/null @@ -1,26 +0,0 @@ -// -// SLSmoleculeGLView.h -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 5/18/2008. -// -// This view manages the OpenGL scene, with setup and rendering methods. Multitouch events are also handled -// here, although it might be best to refactor some of the code up to a controller. - -#import - -@class SLSOpenGLESRenderer; - -@interface SLSMoleculeGLView : UIView -{ - SLSOpenGLESRenderer *openGLESRenderer; - CGSize previousSize; -} - -@property(readonly) SLSOpenGLESRenderer *openGLESRenderer; - --(void)snapUIImage; - -@end diff --git a/SLSMoleculeGLView.m b/SLSMoleculeGLView.m deleted file mode 100644 index 941ca41..0000000 --- a/SLSMoleculeGLView.m +++ /dev/null @@ -1,138 +0,0 @@ -// -// SLSMoleculeGLView.m -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 5/18/2008. -// -// This view manages the OpenGL scene, with setup and rendering methods. Multitouch events are also handled -// here, although it might be best to refactor some of the code up to a controller. - - -#import -#import -#import "SLSOpenGLES11Renderer.h" -#import "SLSOpenGLES20Renderer.h" - -#import "SLSMoleculeGLView.h" -#import "SLSMolecule.h" - -@implementation SLSMoleculeGLView - -// Override the class method to return the OpenGL layer, as opposed to the normal CALayer -+ (Class) layerClass -{ - return [CAEAGLLayer class]; -} - -#pragma mark - -#pragma mark Initialization and breakdown - -- (id)initWithFrame:(CGRect)aRect -{ - if ((self = [super initWithFrame:aRect])) - { - self.multipleTouchEnabled = YES; - self.opaque = YES; - - previousSize = aRect.size; - - // Set scaling to account for Retina display - if ([self respondsToSelector:@selector(setContentScaleFactor:)]) - { - self.contentScaleFactor = [[UIScreen mainScreen] scale]; - } - - // Do OpenGL Core Animation layer setup - CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer; - - eaglLayer.opaque = YES; - eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys: - [NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGBA8, kEAGLDrawablePropertyColorFormat, nil]; -// [NSNumber numberWithBool:NO], kEAGLDrawablePropertyRetainedBacking, kEAGLColorFormatRGB565, kEAGLDrawablePropertyColorFormat, nil]; - - - EAGLContext *aContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; -// aContext = nil; - - if (!aContext) - { - aContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1]; - openGLESRenderer = [[SLSOpenGLES11Renderer alloc] initWithContext:aContext]; - } - else - { - openGLESRenderer = [[SLSOpenGLES20Renderer alloc] initWithContext:aContext]; - } - - - [openGLESRenderer createFramebuffersForLayer:eaglLayer]; - [openGLESRenderer clearScreen]; - } - return self; -} - --(void)snapUIImage -{ -// From http://stackoverflow.com/q/4787311/19679 - - int s = 1; - UIScreen* screen = [ UIScreen mainScreen ]; - if ( [ screen respondsToSelector:@selector(scale) ] ) - s = (int) [ screen scale ]; - - const int w = self.frame.size.width; - const int h = self.frame.size.height; - const NSInteger myDataLength = w * h * 4 * s * s; - // allocate array and read pixels into it. - GLubyte *buffer = (GLubyte *) malloc(myDataLength); - glReadPixels(0, 0, w*s, h*s, GL_RGBA, GL_UNSIGNED_BYTE, buffer); - // gl renders "upside down" so swap top to bottom into new array. - // there's gotta be a better way, but this works. - GLubyte *buffer2 = (GLubyte *) malloc(myDataLength); - for(int y = 0; y < h*s; y++) - { - memcpy( buffer2 + (h*s - 1 - y) * w * 4 * s, buffer + (y * 4 * w * s), w * 4 * s ); - } - free(buffer); // work with the flipped buffer, so get rid of the original one. - - // make data provider with data. - CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, buffer2, myDataLength, NULL); - // prep the ingredients - int bitsPerComponent = 8; - int bitsPerPixel = 32; - int bytesPerRow = 4 * w * s; - CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB(); - CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault; - CGColorRenderingIntent renderingIntent = kCGRenderingIntentDefault; - // make the cgimage - CGImageRef imageRef = CGImageCreate(w*s, h*s, bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider, NULL, NO, renderingIntent); - // then make the uiimage from that - UIImage *myImage = [ UIImage imageWithCGImage:imageRef scale:s orientation:UIImageOrientationUp ]; - UIImageWriteToSavedPhotosAlbum( myImage, nil, nil, nil ); - CGImageRelease( imageRef ); - CGDataProviderRelease(provider); - CGColorSpaceRelease(colorSpaceRef); - free(buffer2); -} - -#pragma mark - -#pragma mark UIView methods - -- (void)layoutSubviews -{ - CGSize newSize = self.bounds.size; - if (!CGSizeEqualToSize(newSize, previousSize)) - { - [[NSNotificationCenter defaultCenter] postNotificationName:@"GLViewSizeDidChange" object:nil]; - previousSize = newSize; - } -} - -#pragma mark - -#pragma mark Accessors - -@synthesize openGLESRenderer; - -@end diff --git a/SLSMoleculeGLViewController.h b/SLSMoleculeGLViewController.h deleted file mode 100644 index 3f84bd2..0000000 --- a/SLSMoleculeGLViewController.h +++ /dev/null @@ -1,79 +0,0 @@ -// -// SLSMoleculeGLViewController.h -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 6/30/2008. -// -// A controller for managing the OpenGL view of the molecule. - -#import -#import -#import - -@class SLSMolecule; -@class SLSOpenGLESRenderer; - -@interface SLSMoleculeGLViewController : UIViewController -{ - // User interface elements - UIActivityIndicatorView *scanningActivityIndicator; - UIProgressView *renderingProgressIndicator; - UILabel *renderingActivityLabel; - UIActionSheet *visualizationActionSheet; - - SLSOpenGLESRenderer *openGLESRenderer; - - SLSMolecule *moleculeToDisplay; - BOOL isAutorotating; - CADisplayLink *displayLink; - CFTimeInterval previousTimestamp; - BOOL shouldResizeDisplay; - - NSUInteger stepsSinceLastRotation; - - // Touch-handling - float startingTouchDistance, previousScale; - float instantObjectScale, instantXRotation, instantYRotation, instantXTranslation, instantYTranslation, instantZTranslation; - CGPoint lastMovementPosition, previousDirectionOfPanning; - BOOL twoFingersAreMoving, pinchGestureUnderway; -} - -@property (readwrite, strong, nonatomic) UIActionSheet *visualizationActionSheet; -@property (readwrite, strong, nonatomic) SLSMolecule *moleculeToDisplay; -@property (readwrite, strong, nonatomic) CADisplayLink *displayLink; - -// Display indicator control -- (void)showScanningIndicator:(NSNotification *)note; -- (void)updateScanningIndicator:(NSNotification *)note; -- (void)hideScanningIndicator:(NSNotification *)note; -- (void)showRenderingIndicator:(NSNotification *)note; -- (void)updateRenderingIndicator:(NSNotification *)note; -- (void)hideRenderingIndicator:(NSNotification *)note; - -// Autorotation of molecule -- (void)startOrStopAutorotation:(id)sender; -- (void)handleAutorotationTimer; - -// OpenGL molecule rendering -- (void)resizeView; -- (void)runOpenGLBenchmarks; -- (void)updateSizeOfGLView:(NSNotification *)note; -- (void)handleStartOfAutorotation; -- (void)handleEndOfAutorotation; - -// Manage molecule rendering state -- (void)handleFinishOfMoleculeRendering:(NSNotification *)note; -- (UIActionSheet *)actionSheetForVisualizationState; - -// Touch handling -- (float)distanceBetweenTouches:(NSSet *)touches; -- (CGPoint)commonDirectionOfTouches:(NSSet *)touches; -- (void)handleTouchesEnding:(NSSet *)touches withEvent:(UIEvent *)event; - -// Interface methods -- (IBAction)switchToTableView; - - -@end diff --git a/SLSMoleculeGLViewController.m b/SLSMoleculeGLViewController.m deleted file mode 100644 index dfe483a..0000000 --- a/SLSMoleculeGLViewController.m +++ /dev/null @@ -1,729 +0,0 @@ -// -// SLSMoleculeGLViewController.m -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 6/30/2008. -// -// A barebones controller for managing the OpenGL view of the molecule. It's pretty sparse, as some of the methods in the view really belong here. - -#import "SLSMoleculeGLViewController.h" -#import "SLSMoleculeGLView.h" -#import "SLSMolecule.h" -#import "SLSMoleculeAppDelegate.h" -#import "SLSOpenGLESRenderer.h" -#import "SLSOpenGLES20Renderer.h" - -//#define RUN_OPENGL_BENCHMARKS - -@implementation SLSMoleculeGLViewController - -#pragma mark - -#pragma mark Initialization and teardown - -- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil -{ - if ((self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil])) - { - // Set up an observer that catches the molecule update notifications and shows and updates the rendering indicator - NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; - [nc addObserver:self selector:@selector(showRenderingIndicator:) name:kSLSMoleculeRenderingStartedNotification object:nil]; - [nc addObserver:self selector:@selector(updateRenderingIndicator:) name:kSLSMoleculeRenderingUpdateNotification object:nil]; - [nc addObserver:self selector:@selector(hideRenderingIndicator:) name:kSLSMoleculeRenderingEndedNotification object:nil]; - - [nc addObserver:self selector:@selector(showScanningIndicator:) name:@"FileLoadingStarted" object:nil]; - [nc addObserver:self selector:@selector(updateScanningIndicator:) name:@"FileLoadingUpdate" object:nil]; - [nc addObserver:self selector:@selector(hideScanningIndicator:) name:@"FileLoadingEnded" object:nil]; - - [nc addObserver:self selector:@selector(updateSizeOfGLView:) name:@"GLViewSizeDidChange" object:nil]; - - isAutorotating = NO; - - // Initialize values for the touch interaction - previousScale = 1.0f; - instantObjectScale = 1.0f; - instantXRotation = 1.0f; - instantYRotation = 0.0f; - instantXTranslation = 0.0f; - instantYTranslation = 0.0f; - instantZTranslation = 0.0f; - twoFingersAreMoving = NO; - pinchGestureUnderway = NO; - stepsSinceLastRotation = 0; - previousTimestamp = 0; - - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleFinishOfMoleculeRendering:) name:@"MoleculeRenderingEnded" object:nil]; - } - return self; -} - -- (void)dealloc -{ -// [[NSNotificationCenter defaultCenter] removeObserver:self]; - [self.displayLink invalidate]; - -} - -- (void)loadView -{ - CGRect applicationFrame = [[UIScreen mainScreen] bounds]; - - SLSMoleculeGLView *glView = [[SLSMoleculeGLView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, applicationFrame.size.width, applicationFrame.size.height)]; - - self.view = glView; - openGLESRenderer = glView.openGLESRenderer; - -} - -#pragma mark - -#pragma mark Display indicator control - -- (void)showScanningIndicator:(NSNotification *)note; -{ - if (scanningActivityIndicator != nil) - { - [scanningActivityIndicator removeFromSuperview]; - scanningActivityIndicator = nil; - } - - scanningActivityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; - scanningActivityIndicator.frame = CGRectMake(round(self.view.frame.size.width / 2.0f - 37.0f / 2.0f), round(self.view.frame.size.height / 2.0f + 15.0f), 37.0f, 37.0f); - scanningActivityIndicator.hidesWhenStopped = YES; - [scanningActivityIndicator startAnimating]; - - if (renderingActivityLabel != nil) - { - [renderingActivityLabel removeFromSuperview]; - renderingActivityLabel = nil; - } - - renderingActivityLabel = [[UILabel alloc] initWithFrame:CGRectMake(round(self.view.frame.size.width / 2.0f - 219.0f / 2.0f), round(self.view.frame.size.height / 2.0f - 15.0f - 21.0f), 219.0f, 21.0f)]; - renderingActivityLabel.font = [UIFont systemFontOfSize:17.0f]; - renderingActivityLabel.text = [note object]; - renderingActivityLabel.textAlignment = NSTextAlignmentCenter; - renderingActivityLabel.backgroundColor = [UIColor clearColor]; - renderingActivityLabel.textColor = [UIColor whiteColor]; - - [self.view addSubview:scanningActivityIndicator]; - [self.view addSubview:renderingActivityLabel]; -} - -- (void)updateScanningIndicator:(NSNotification *)note; -{ - -} - -- (void)hideScanningIndicator:(NSNotification *)note; -{ - [renderingActivityLabel removeFromSuperview]; - renderingActivityLabel = nil; - - [scanningActivityIndicator removeFromSuperview]; - scanningActivityIndicator = nil; -} - -- (void)showRenderingIndicator:(NSNotification *)note; -{ - if (renderingProgressIndicator != nil) - { - [renderingProgressIndicator removeFromSuperview]; - renderingProgressIndicator = nil; - } - - float renderingIndicatorWidth = round(self.view.frame.size.width * 0.6); - renderingProgressIndicator = [[UIProgressView alloc] initWithFrame:CGRectMake(round(self.view.frame.size.width / 2.0f - renderingIndicatorWidth / 2.0f), round(self.view.frame.size.height / 2.0f + 15.0f), renderingIndicatorWidth, 9.0f)]; - [renderingProgressIndicator setProgress:0.0f]; - renderingProgressIndicator.progressViewStyle = UIProgressViewStyleBar; - - if (renderingActivityLabel != nil) - { - [renderingActivityLabel removeFromSuperview]; - renderingActivityLabel = nil; - } - - renderingActivityLabel = [[UILabel alloc] initWithFrame:CGRectMake(round(self.view.frame.size.width / 2.0f - 219.0f / 2.0f), round(self.view.frame.size.height / 2.0f - 15.0f - 21.0f), 219.0f, 21.0f)]; - renderingActivityLabel.font = [UIFont systemFontOfSize:17.0f]; - renderingActivityLabel.text = NSLocalizedStringFromTable(@"Rendering...", @"Localized", nil); - renderingActivityLabel.textAlignment = NSTextAlignmentCenter; - renderingActivityLabel.backgroundColor = [UIColor clearColor]; - renderingActivityLabel.textColor = [UIColor whiteColor]; - - [openGLESRenderer clearScreen]; - [renderingProgressIndicator setProgress:0.0]; - [self.view addSubview:renderingProgressIndicator]; - [self.view addSubview:renderingActivityLabel]; -} - -- (void)updateRenderingIndicator:(NSNotification *)note; -{ - float percentComplete = [(NSNumber *)[note object] floatValue]; - -// if ((percentComplete - renderingProgressIndicator.progress) > 0.01f) -// { - renderingProgressIndicator.progress = percentComplete; -// } -} - -- (void)hideRenderingIndicator:(NSNotification *)note; -{ - [renderingActivityLabel removeFromSuperview]; - [renderingProgressIndicator removeFromSuperview]; - - renderingActivityLabel = nil; - - renderingProgressIndicator = nil; -} - -#pragma mark - -#pragma mark Autorotation of molecule - -- (void)startOrStopAutorotation:(id)sender; -{ - if (isAutorotating) - { - [self.displayLink invalidate]; - self.displayLink = nil; - - [[NSNotificationCenter defaultCenter] postNotificationName:@"ToggleRotationSelected" object:[NSNumber numberWithBool:NO]]; - } - else - { - previousTimestamp = 0; - CADisplayLink *aDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleAutorotationTimer)]; - [aDisplayLink setFrameInterval:2]; - [aDisplayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode]; -// [aDisplayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes]; - - self.displayLink = aDisplayLink; - - [[NSNotificationCenter defaultCenter] postNotificationName:@"ToggleRotationSelected" object:[NSNumber numberWithBool:YES]]; - } - isAutorotating = !isAutorotating; -} - -- (void)handleAutorotationTimer; -{ - if (shouldResizeDisplay) - { - [self resizeView]; - shouldResizeDisplay = NO; - } - if (previousTimestamp == 0) - { - [openGLESRenderer rotateModelFromScreenDisplacementInX:1.0f inY:0.0f]; - } - else - { - [openGLESRenderer rotateModelFromScreenDisplacementInX:(30.0f * (displayLink.timestamp - previousTimestamp)) inY:0.0f]; - } - - [openGLESRenderer renderFrameForMolecule:moleculeToDisplay]; - - previousTimestamp = displayLink.timestamp; -} - -#pragma mark - -#pragma mark OpenGL molecule rendering - -- (void)resizeView; -{ -// [EAGLContext setCurrentContext:glView.context]; - [openGLESRenderer destroyFramebuffers]; - [openGLESRenderer createFramebuffersForLayer:(CAEAGLLayer *)self.view.layer]; - [openGLESRenderer configureProjection]; - if (displayLink == nil) - { - [openGLESRenderer renderFrameForMolecule:moleculeToDisplay]; - } -} - -- (void)runOpenGLBenchmarks; -{ - NSLog(NSLocalizedStringFromTable(@"Triangles: %d", @"Localized", nil), openGLESRenderer.totalNumberOfTriangles); - NSLog(NSLocalizedStringFromTable(@"Vertices: %d", @"Localized", nil), openGLESRenderer.totalNumberOfVertices); - CFAbsoluteTime elapsedTime, startTime = CFAbsoluteTimeGetCurrent(); -#define NUMBER_OF_FRAMES_FOR_TESTING 100 - - for (unsigned int testCounter = 0; testCounter < NUMBER_OF_FRAMES_FOR_TESTING; testCounter++) - { - // Do something - [openGLESRenderer rotateModelFromScreenDisplacementInX:1.0f inY:0.0]; - [openGLESRenderer renderFrameForMolecule:moleculeToDisplay]; - } - elapsedTime = CFAbsoluteTimeGetCurrent() - startTime; - // ElapsedTime contains seconds (or fractions thereof as decimals) - NSLog(NSLocalizedStringFromTable(@"Elapsed time: %f", @"Localized", nil), elapsedTime); - NSLog(@"Triangles per second: %f", (CGFloat)openGLESRenderer.totalNumberOfTriangles * (CGFloat)NUMBER_OF_FRAMES_FOR_TESTING / elapsedTime); -} - -- (void)updateSizeOfGLView:(NSNotification *)note; -{ -// if (displayLink == nil) -// { - [self resizeView]; -// } -// else -// { -// shouldResizeDisplay = YES; -// } -} - -- (void)handleStartOfAutorotation; -{ - [openGLESRenderer suspendRenderingDuringRotation]; -} - -- (void)handleEndOfAutorotation; -{ - [openGLESRenderer resumeRenderingDuringRotation]; - - if (!isAutorotating) - { - [openGLESRenderer renderFrameForMolecule:moleculeToDisplay]; - } -} - -#pragma mark - -#pragma mark Manage molecule rendering state - -- (void)handleFinishOfMoleculeRendering:(NSNotification *)note; -{ - [openGLESRenderer clearScreen]; - [NSThread sleepForTimeInterval:0.1]; - - [openGLESRenderer resetModelViewMatrix]; - -#ifdef RUN_OPENGL_BENCHMARKS - - [self.displayLink invalidate]; - self.displayLink = nil; - -// [[NSNotificationCenter defaultCenter] postNotificationName:@"ToggleRotationSelected" object:[NSNumber numberWithBool:NO]]; - [NSThread sleepForTimeInterval:0.2]; - - - [self runOpenGLBenchmarks]; -#else - if (!isAutorotating) - { - [self startOrStopAutorotation:self]; - } -#endif -} - -- (UIActionSheet *)actionSheetForVisualizationState; -{ - NSString *buttonTitle1; -// NSString *buttonTitle2; - NSString *cancelButtonTitle; - switch (moleculeToDisplay.currentVisualizationType) - { - case BALLANDSTICK: - { - buttonTitle1 = NSLocalizedStringFromTable(@"Spacefilling", @"Localized", nil); -// buttonTitle2 = NSLocalizedStringFromTable(@"Cylinders", @"Localized", nil); - cancelButtonTitle = NSLocalizedStringFromTable(@"Ball-and-stick", @"Localized", nil); - }; break; - case SPACEFILLING: - { - buttonTitle1 = NSLocalizedStringFromTable(@"Ball-and-stick", @"Localized", nil); -// buttonTitle2 = NSLocalizedStringFromTable(@"Cylinders", @"Localized", nil); - cancelButtonTitle = NSLocalizedStringFromTable(@"Spacefilling", @"Localized", nil); - }; break; - case CYLINDRICAL: - { - buttonTitle1 = NSLocalizedStringFromTable(@"Ball-and-stick", @"Localized", nil); -// buttonTitle2 = NSLocalizedStringFromTable(@"Spacefilling", @"Localized", nil); - cancelButtonTitle = NSLocalizedStringFromTable(@"Cylinders", @"Localized", nil); - }; break; - default: - { - buttonTitle1 = NSLocalizedStringFromTable(@"Spacefilling", @"Localized", nil); -// buttonTitle2 = NSLocalizedStringFromTable(@"Cylinders", @"Localized", nil); - cancelButtonTitle = NSLocalizedStringFromTable(@"Ball-and-stick", @"Localized", nil); - }; - } - - NSString *titleForActionSheet = NSLocalizedStringFromTable(@"Visualization mode", @"Localized", nil); - if ([SLSMoleculeAppDelegate isRunningOniPad]) - { - titleForActionSheet = nil; -// cancelButtonTitle = nil; - } - - UIActionSheet *actionSheet = [[UIActionSheet alloc] initWithTitle:titleForActionSheet - delegate:self cancelButtonTitle:cancelButtonTitle destructiveButtonTitle:nil - otherButtonTitles:buttonTitle1, nil]; -//otherButtonTitles:buttonTitle1, buttonTitle2, nil]; - if ([SLSMoleculeAppDelegate isRunningOniPad]) - { - actionSheet.actionSheetStyle = UIActionSheetStyleAutomatic; - } - else - { - actionSheet.actionSheetStyle = UIActionSheetStyleBlackTranslucent; - } - return actionSheet; -} - -#pragma mark - -#pragma mark Touch handling - -- (float)distanceBetweenTouches:(NSSet *)touches; -{ - int currentStage = 0; - CGPoint point1 = CGPointZero; - CGPoint point2 = CGPointZero; - - - for (UITouch *currentTouch in touches) - { - if (currentStage == 0) - { - point1 = [currentTouch locationInView:self.view]; - currentStage++; - } - else if (currentStage == 1) - { - point2 = [currentTouch locationInView:self.view]; - currentStage++; - } - else - { - } - } - return (sqrt((point1.x - point2.x) * (point1.x - point2.x) + (point1.y - point2.y) * (point1.y - point2.y))); -} - -- (CGPoint)commonDirectionOfTouches:(NSSet *)touches; -{ - // Check to make sure that both fingers are moving in the same direction - - int currentStage = 0; - CGPoint currentLocationOfTouch1 = CGPointZero, currentLocationOfTouch2 = CGPointZero, previousLocationOfTouch1 = CGPointZero, previousLocationOfTouch2 = CGPointZero; - - - for (UITouch *currentTouch in touches) - { - if (currentStage == 0) - { - previousLocationOfTouch1 = [currentTouch previousLocationInView:self.view]; - currentLocationOfTouch1 = [currentTouch locationInView:self.view]; - currentStage++; - } - else if (currentStage == 1) - { - previousLocationOfTouch2 = [currentTouch previousLocationInView:self.view]; - currentLocationOfTouch2 = [currentTouch locationInView:self.view]; - currentStage++; - } - else - { - } - } - - CGPoint directionOfTouch1, directionOfTouch2, commonDirection; - // The sign of the Y touches is inverted, due to the inverted coordinate system of the iPhone - directionOfTouch1.x = currentLocationOfTouch1.x - previousLocationOfTouch1.x; - directionOfTouch1.y = previousLocationOfTouch1.y - currentLocationOfTouch1.y; - directionOfTouch2.x = currentLocationOfTouch2.x - previousLocationOfTouch2.x; - directionOfTouch2.y = previousLocationOfTouch2.y - currentLocationOfTouch2.y; - - // A two-finger movement should result in the direction of both touches being positive or negative at the same time in X and Y - if (!( ((directionOfTouch1.x <= 0) && (directionOfTouch2.x <= 0)) || ((directionOfTouch1.x >= 0) && (directionOfTouch2.x >= 0)) )) - return CGPointZero; - if (!( ((directionOfTouch1.y <= 0) && (directionOfTouch2.y <= 0)) || ((directionOfTouch1.y >= 0) && (directionOfTouch2.y >= 0)) )) - return CGPointZero; - - // The movement ranges are averaged out - commonDirection.x = ((directionOfTouch1.x + directionOfTouch2.x) / 2.0f); - commonDirection.y = ((directionOfTouch1.y + directionOfTouch2.y) / 2.0f); - - - return commonDirection; -} - -- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event -{ - if (isAutorotating) - [self startOrStopAutorotation:nil]; - - NSMutableSet *currentTouches = [[event touchesForView:self.view] mutableCopy]; - [currentTouches minusSet:touches]; - - // New touches are not yet included in the current touches for the view - NSSet *totalTouches = [touches setByAddingObjectsFromSet:[event touchesForView:self.view]]; - if ([totalTouches count] > 1) - { - startingTouchDistance = [self distanceBetweenTouches:totalTouches]; - previousScale = 1.0f; - twoFingersAreMoving = NO; - pinchGestureUnderway = NO; - previousDirectionOfPanning = CGPointZero; - } - else - { - lastMovementPosition = [[touches anyObject] locationInView:self.view]; - } -} - -- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event; -{ - if (isAutorotating) - { - [self startOrStopAutorotation:nil]; - } - - if ([[event touchesForView:self.view] count] > 1) // Pinch gesture, possibly two-finger movement - { - CGPoint directionOfPanning = CGPointZero; - - // Two finger panning - if ([touches count] > 1) // Check to make sure that both fingers are moving - { - directionOfPanning = [self commonDirectionOfTouches:touches]; - } - - if ( (directionOfPanning.x != 0) || (directionOfPanning.y != 0) ) // Don't scale while doing the two-finger panning - { - if (pinchGestureUnderway) - { - - - if (sqrt(previousDirectionOfPanning.x * previousDirectionOfPanning.x + previousDirectionOfPanning.y * previousDirectionOfPanning.y) > 0.1 ) - { - pinchGestureUnderway = NO; - } - previousDirectionOfPanning.x += directionOfPanning.x; - previousDirectionOfPanning.y += directionOfPanning.y; - } - if (!pinchGestureUnderway) - { - twoFingersAreMoving = YES; - [openGLESRenderer translateModelByScreenDisplacementInX:directionOfPanning.x inY:directionOfPanning.y]; - [openGLESRenderer renderFrameForMolecule:moleculeToDisplay]; - - previousDirectionOfPanning = CGPointZero; - } - } - else - { - float newTouchDistance = [self distanceBetweenTouches:[event touchesForView:self.view]]; - if (twoFingersAreMoving) - { - // If fingers have moved more than 10% apart, start pinch gesture again - if ( fabs(1 - (newTouchDistance / startingTouchDistance) / previousScale) > 0.3 ) - { - twoFingersAreMoving = NO; - } - } - if (!twoFingersAreMoving) - { - // Scale using pinch gesture - [openGLESRenderer scaleModelByFactor:(newTouchDistance / startingTouchDistance) / previousScale]; - [openGLESRenderer renderFrameForMolecule:moleculeToDisplay]; - -// [self _drawViewByRotatingAroundX:0.0 rotatingAroundY:0.0 scaling:(newTouchDistance / startingTouchDistance) / previousScale translationInX:directionOfPanning.x translationInY:directionOfPanning.y]; - previousScale = (newTouchDistance / startingTouchDistance); - pinchGestureUnderway = YES; - } - } - } - else // Single-touch rotation of object - { - CGPoint currentMovementPosition = [[touches anyObject] locationInView:self.view]; - [openGLESRenderer rotateModelFromScreenDisplacementInX:(currentMovementPosition.x - lastMovementPosition.x) inY:(currentMovementPosition.y - lastMovementPosition.y)]; - [openGLESRenderer renderFrameForMolecule:moleculeToDisplay]; - - lastMovementPosition = currentMovementPosition; - } - -} - -- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event -{ - [self handleTouchesEnding:touches withEvent:event]; - - // This is placed here to avoid an infinite spawning of alerts under iPhone OS 4.0 - if (([[touches anyObject] tapCount] >= 2) && (![SLSMoleculeAppDelegate isRunningOniPad])) - { - if (moleculeToDisplay.isDoneRendering == YES) - { -// [(SLSMoleculeGLView *)self.view snapUIImage]; - - UIActionSheet *actionSheet = [self actionSheetForVisualizationState]; - [actionSheet showInView:self.view]; - } - } -} - -- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event -{ - [self handleTouchesEnding:touches withEvent:event]; -} - -- (void)handleTouchesEnding:(NSSet *)touches withEvent:(UIEvent *)event -{ - if (isAutorotating) - [self startOrStopAutorotation:nil]; - - NSMutableSet *remainingTouches = [[event touchesForView:self.view] mutableCopy]; - [remainingTouches minusSet:touches]; - if ([remainingTouches count] < 2) - { - twoFingersAreMoving = NO; - pinchGestureUnderway = NO; - previousDirectionOfPanning = CGPointZero; - - lastMovementPosition = [[remainingTouches anyObject] locationInView:self.view]; - } -} - -#pragma mark - -#pragma mark Interface methods - -- (IBAction)switchToTableView; -{ - if (moleculeToDisplay.isDoneRendering == NO) - { - return; - } - - if (isAutorotating) - { - [self startOrStopAutorotation:nil]; - [openGLESRenderer waitForLastFrameToFinishRendering]; - } - - [[NSNotificationCenter defaultCenter] postNotificationName:@"ToggleView" object:nil]; -} - -#pragma mark - -#pragma mark UIActionSheet delegate method - -- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex -{ - SLSVisualizationType newVisualizationType = (SLSVisualizationType)[[NSUserDefaults standardUserDefaults] integerForKey:@"currentVisualizationMode"]; - - switch (newVisualizationType) - { - case BALLANDSTICK: - { - if (buttonIndex == 0) - { - newVisualizationType = SPACEFILLING; - } -// else if (buttonIndex == 1) -// { -// newVisualizationType = CYLINDRICAL; -// } - }; break; - case SPACEFILLING: - { - if (buttonIndex == 0) - { - newVisualizationType = BALLANDSTICK; - } -// else if (buttonIndex == 1) -// { -// newVisualizationType = CYLINDRICAL; -// } - }; break; - case CYLINDRICAL: - { - if (buttonIndex == 0) - { - newVisualizationType = BALLANDSTICK; - } -// else if (buttonIndex == 1) -// { -// newVisualizationType = SPACEFILLING; -// } - }; break; - } - - if (moleculeToDisplay.currentVisualizationType != newVisualizationType) - { - if (isAutorotating) - { - [self startOrStopAutorotation:self]; - } - - moleculeToDisplay.currentVisualizationType = newVisualizationType; - [[NSUserDefaults standardUserDefaults] setInteger:newVisualizationType forKey:@"currentVisualizationMode"]; - - [openGLESRenderer freeVertexBuffers]; - [moleculeToDisplay performSelectorInBackground:@selector(renderMolecule:) withObject:openGLESRenderer]; - } - - visualizationActionSheet = nil; -} - -#pragma mark - -#pragma mark UIViewController methods - -- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation -{ - // Return YES for supported orientations -// return (interfaceOrientation == UIInterfaceOrientationPortrait); - return YES; -} - -- (void)didReceiveMemoryWarning -{ -} - -#pragma mark - -#pragma mark Accessors - -@synthesize visualizationActionSheet; -@synthesize moleculeToDisplay; -@synthesize displayLink; - -- (void)setMoleculeToDisplay:(SLSMolecule *)newMolecule; -{ - if (moleculeToDisplay == newMolecule) - { - return; - } - - if (isAutorotating) - { - [self startOrStopAutorotation:self]; - [openGLESRenderer waitForLastFrameToFinishRendering]; - } - -// [NSThread sleepForTimeInterval:0.2]; - - moleculeToDisplay.isBeingDisplayed = NO; - if (!moleculeToDisplay.isRenderingCancelled) - { - [openGLESRenderer freeVertexBuffers]; - } - - moleculeToDisplay = newMolecule; - if ([openGLESRenderer isKindOfClass:[SLSOpenGLES20Renderer class]]) - { - [moleculeToDisplay switchToDefaultVisualizationMode]; - - } - else - { - moleculeToDisplay.currentVisualizationType = (SLSVisualizationType)[[NSUserDefaults standardUserDefaults] integerForKey:@"currentVisualizationMode"]; - } - - moleculeToDisplay.isBeingDisplayed = YES; - [moleculeToDisplay performSelectorInBackground:@selector(renderMolecule:) withObject:openGLESRenderer]; - - instantObjectScale = 1.0f; - instantXRotation = 1.0f; - instantYRotation = 0.0f; - instantXTranslation = 0.0f; - instantYTranslation = 0.0f; - instantZTranslation = 0.0f; - - [[NSUserDefaults standardUserDefaults] synchronize]; -} - -@end diff --git a/SLSMoleculeLibraryTableCell.h b/SLSMoleculeLibraryTableCell.h deleted file mode 100644 index 77ecfef..0000000 --- a/SLSMoleculeLibraryTableCell.h +++ /dev/null @@ -1,22 +0,0 @@ -// -// SLSMoleculeLibraryTableCell.h -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 4/30/2011. -// - -#import -#import - -@interface SLSMoleculeLibraryTableCell : UITableViewCell -{ - CAGradientLayer *highlightGradientLayer; - BOOL isSelected; -} - -@property(strong, nonatomic) CAGradientLayer *highlightGradientLayer; -@property(assign, nonatomic) BOOL isSelected; - -@end diff --git a/SLSMoleculeLibraryTableCell.m b/SLSMoleculeLibraryTableCell.m deleted file mode 100644 index 4302698..0000000 --- a/SLSMoleculeLibraryTableCell.m +++ /dev/null @@ -1,43 +0,0 @@ -// -// SLSMoleculeLibraryTableCell.m -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 4/30/2011. -// - -#import "SLSMoleculeLibraryTableCell.h" - -@implementation SLSMoleculeLibraryTableCell - -#pragma mark - -#pragma mark Initialization and teardown - -- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier -{ - if ((self = [super initWithStyle:style reuseIdentifier:reuseIdentifier])) - { - isSelected = NO; - // Initialization code - } - return self; -} - - - -//- (void)setSelected:(BOOL)selected animated:(BOOL)animated -//{ -// -// [super setSelected:selected animated:animated]; -// -// // Configure the view for the selected state -//} - -#pragma mark - -#pragma mark Accessors - -@synthesize highlightGradientLayer; -@synthesize isSelected; - -@end diff --git a/SLSMoleculeRootViewController.h b/SLSMoleculeRootViewController.h deleted file mode 100644 index acb0741..0000000 --- a/SLSMoleculeRootViewController.h +++ /dev/null @@ -1,56 +0,0 @@ -// -// SLSMoleculeRootViewController.h -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 6/30/2008. -// -// This controller manages a root view into which the 3D view and the molecule table selection views and animated for the neat flipping effect - -#import -#import -#import "SLSMoleculeCustomDownloadViewController.h" - -@class SLSMoleculeGLViewController; -@class SLSMolecule; -@class SLSMoleculeTableViewController; - -@interface SLSMoleculeRootViewController : UIViewController -{ - SLSMoleculeGLViewController *glViewController; - UIButton *rotationButton; - UINavigationController *tableNavigationController; - SLSMoleculeTableViewController *tableViewController; - - SLSMolecule *bufferedMolecule, *previousMolecule; - NSMutableArray *molecules; - - BOOL toggleViewDisabled; - - sqlite3 *database; -} - -@property (nonatomic, strong) SLSMoleculeGLViewController *glViewController; -@property (nonatomic, readonly) UINavigationController *tableNavigationController; -@property (nonatomic, readonly) SLSMoleculeTableViewController *tableViewController; -@property (nonatomic, assign) sqlite3 *database; -@property (nonatomic, strong) NSMutableArray *molecules; - -// Manage the switching of views -- (void)toggleView:(NSNotification *)note; - -// Passthroughs for managing molecules -- (void)loadInitialMolecule; -- (void)selectedMoleculeDidChange:(NSInteger)newMoleculeIndex; -- (void)cancelMoleculeLoading; -- (void)updateTableListOfMolecules; - -- (void)customURLSelectedForMoleculeDownload:(NSNotification *)note; - -// Manage the switching of rotation state -- (void)toggleRotationButton:(NSNotification *)note; - - -@end - diff --git a/SLSMoleculeRootViewController.m b/SLSMoleculeRootViewController.m deleted file mode 100644 index 731ccb8..0000000 --- a/SLSMoleculeRootViewController.m +++ /dev/null @@ -1,303 +0,0 @@ -// -// SLSMoleculeRootViewController.m -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 6/30/2008. -// -// This controller manages a root view into which the 3D view and the molecule table selection views and animated for the neat flipping effect - -#import "SLSMoleculeAppDelegate.h" -#import "SLSMoleculeRootViewController.h" -#import "SLSMoleculeTableViewController.h" -#import "SLSMoleculeGLViewController.h" -#import "SLSMoleculeGLView.h" -#import "SLSMolecule.h" - - -@implementation SLSMoleculeRootViewController - -#pragma mark - -#pragma mark Initialiation and breakdown - -- (id)init; -{ - if ((self = [super init])) - { - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(toggleView:) name:@"ToggleView" object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(toggleRotationButton:) name:@"ToggleRotationSelected" object:nil]; - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(customURLSelectedForMoleculeDownload:) name:@"CustomURLForMoleculeSelected" object:nil]; - } - return self; -} - - -- (void)loadView -{ - CGRect mainScreenFrame = [[UIScreen mainScreen] bounds]; - - NSLog(@"Application frame: %f, %f", mainScreenFrame.size.width, mainScreenFrame.size.height); - - UIView *backgroundView = [[UIView alloc] initWithFrame:mainScreenFrame]; - backgroundView.backgroundColor = [UIColor blackColor]; - - self.view = backgroundView; - toggleViewDisabled = NO; - - SLSMoleculeGLViewController *viewController = [[SLSMoleculeGLViewController alloc] initWithNibName:nil bundle:nil]; - self.glViewController = viewController; - - [self.view addSubview:glViewController.view]; - - UIButton *infoButton = [UIButton buttonWithType:UIButtonTypeInfoLight]; - infoButton.frame = CGRectMake(glViewController.view.bounds.size.width - 70.0f, glViewController.view.bounds.size.height - 70.0f, 70.0f, 70.0f); - [infoButton addTarget:glViewController action:@selector(switchToTableView) forControlEvents:(UIControlEventTouchUpInside | UIControlEventTouchUpOutside)]; - [glViewController.view addSubview:infoButton]; - - rotationButton = [UIButton buttonWithType:UIButtonTypeCustom]; - - UIImage *rotationImage = [UIImage imageNamed:@"RotationIcon.png"]; - if (rotationImage == nil) - { - rotationImage = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"RotationIcon" ofType:@"png"]]; - } - [rotationButton setImage:rotationImage forState:UIControlStateNormal]; - - UIImage *selectedRotationImage = [UIImage imageNamed:@"RotationIconSelected.png"]; - if (selectedRotationImage == nil) - { - selectedRotationImage = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"RotationIconSelected" ofType:@"png"]]; - } - [rotationButton setImage:selectedRotationImage forState:UIControlStateSelected]; - - rotationButton.showsTouchWhenHighlighted = YES; - [rotationButton addTarget:glViewController action:@selector(startOrStopAutorotation:) forControlEvents:UIControlEventTouchUpInside]; - rotationButton.frame = CGRectMake(0.0f, glViewController.view.bounds.size.height - 70.0f, 70.0f, 70.0f); - rotationButton.clipsToBounds = NO; - [glViewController.view addSubview:rotationButton]; -} - -- (void)toggleView:(NSNotification *)note; -{ - if (molecules == nil) - return; - - UIView *tableView = self.tableNavigationController.view; - SLSMoleculeGLView *glView = (SLSMoleculeGLView *)glViewController.view; - - [UIView beginAnimations:nil context:NULL]; - [UIView setAnimationDuration:1]; - [UIView setAnimationTransition:([glView superview] ? UIViewAnimationTransitionFlipFromRight : UIViewAnimationTransitionFlipFromLeft) forView:self.view cache:YES]; - - if ([glView superview] != nil) - { - [self cancelMoleculeLoading]; - [tableNavigationController viewWillAppear:YES]; - [glViewController viewWillDisappear:YES]; - [glView removeFromSuperview]; - [self.view addSubview:tableView]; - [glViewController viewDidDisappear:YES]; - [tableNavigationController viewDidAppear:YES]; - [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleDefault]; - } - else - { - [glViewController viewWillAppear:YES]; - [tableNavigationController viewWillDisappear:YES]; - [tableView removeFromSuperview]; - [self.view addSubview:glView]; - - [tableNavigationController viewDidDisappear:YES]; - [glViewController viewDidAppear:YES]; - if (bufferedMolecule != previousMolecule) - { - previousMolecule = bufferedMolecule; - glViewController.moleculeToDisplay = bufferedMolecule; - } - else - previousMolecule.isBeingDisplayed = YES; - [[UIApplication sharedApplication] setStatusBarStyle:UIStatusBarStyleLightContent]; - } - [UIView commitAnimations]; -} - -#pragma mark - -#pragma mark Passthroughs for managing molecules - -- (void)loadInitialMolecule; -{ - NSInteger indexOfInitialMolecule = [[NSUserDefaults standardUserDefaults] integerForKey:@"indexOfLastSelectedMolecule"]; - if (indexOfInitialMolecule >= [molecules count]) - { - indexOfInitialMolecule = 0; - } - - if ([molecules count] > 0) - { - glViewController.moleculeToDisplay = [molecules objectAtIndex:indexOfInitialMolecule]; - } -} - -- (void)selectedMoleculeDidChange:(NSInteger)newMoleculeIndex; -{ - if (newMoleculeIndex >= [molecules count]) - { - newMoleculeIndex = 0; - } - - [[NSUserDefaults standardUserDefaults] setInteger:newMoleculeIndex forKey:@"indexOfLastSelectedMolecule"]; - [[NSUserDefaults standardUserDefaults] synchronize]; - - tableViewController.selectedIndex = newMoleculeIndex; - - // Defer sending the change message to the OpenGL view until the view is loaded, to make sure that rendering occurs only then - if ([molecules count] == 0) - { - bufferedMolecule = nil; - } - else - { - bufferedMolecule = [molecules objectAtIndex:newMoleculeIndex]; - } -} - -#pragma mark - -#pragma mark UIViewController methods - -- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation -{ - // Only allow free autorotation on the iPad - if ([SLSMoleculeAppDelegate isRunningOniPad]) - { - return YES; - } - else - { - return (interfaceOrientation == UIInterfaceOrientationPortrait); - } -} - -- (void)didReceiveMemoryWarning -{ -} - -- (void)cancelMoleculeLoading; -{ - if (!glViewController.moleculeToDisplay.isDoneRendering) - { - glViewController.moleculeToDisplay.isRenderingCancelled = YES; - [NSThread sleepForTimeInterval:0.1]; - } -} - -- (void)updateTableListOfMolecules; -{ - UITableView *tableView = (UITableView *)tableViewController.view; - [tableView reloadData]; -} - - -#pragma mark - -#pragma mark Manage the switching of rotation state - -- (void)toggleRotationButton:(NSNotification *)note; -{ - if ([[note object] boolValue]) - { - rotationButton.selected = YES; - } - else - { - rotationButton.selected = NO; - } -} - -- (void)customURLSelectedForMoleculeDownload:(NSNotification *)note; -{ - NSURL *customURLForMoleculeDownload = [note object]; - - bufferedMolecule = nil; - - if (![SLSMoleculeAppDelegate isRunningOniPad]) - { - [[NSNotificationCenter defaultCenter] postNotificationName:@"ToggleView" object:nil]; - } - //molecules://www.sunsetlakesoftware.com/sites/default/files/xenonPump.pdb - //html://www.sunsetlakesoftware.com/sites/default/files/xenonPump.pdb - - NSString *pathComponentForCustomURL = [[customURLForMoleculeDownload host] stringByAppendingString:[customURLForMoleculeDownload path]]; - NSString *customMoleculeHandlingURL = [NSString stringWithFormat:@"molecules://%@", pathComponentForCustomURL]; - -// [[UIApplication sharedApplication] openURL:[NSURL URLWithString:customMoleculeHandlingURL]]; - [(SLSMoleculeAppDelegate *)[[UIApplication sharedApplication] delegate] handleCustomURLScheme:[NSURL URLWithString:customMoleculeHandlingURL]]; -} - -#pragma mark - -#pragma mark Accessors - -@synthesize tableNavigationController; -@synthesize tableViewController; -@synthesize glViewController; -@synthesize database; -@synthesize molecules; - -- (void)setDatabase:(sqlite3 *)newValue -{ - database = newValue; - tableViewController.database = database; -} - -- (void)setMolecules:(NSMutableArray *)newValue; -{ - if (molecules == newValue) - { - return; - } - - molecules = newValue; - tableViewController.molecules = molecules; - - NSInteger indexOfInitialMolecule = [[NSUserDefaults standardUserDefaults] integerForKey:@"indexOfLastSelectedMolecule"]; - if (indexOfInitialMolecule >= [molecules count]) - { - indexOfInitialMolecule = 0; - } - - tableViewController.selectedIndex = indexOfInitialMolecule; -} - -- (UINavigationController *)tableNavigationController; -{ - if (tableNavigationController == nil) - { - bufferedMolecule = nil; - tableNavigationController = [[UINavigationController alloc] init]; - if ([SLSMoleculeAppDelegate isRunningOniPad]) - { - tableNavigationController.navigationBar.barStyle = UIBarStyleBlackOpaque; - } - - NSInteger indexOfInitialMolecule = [[NSUserDefaults standardUserDefaults] integerForKey:@"indexOfLastSelectedMolecule"]; - if (indexOfInitialMolecule >= [molecules count]) - indexOfInitialMolecule = 0; - tableViewController = [[SLSMoleculeTableViewController alloc] initWithStyle:UITableViewStylePlain initialSelectedMoleculeIndex:indexOfInitialMolecule]; - tableViewController.database = database; - tableViewController.molecules = molecules; - [tableNavigationController pushViewController:tableViewController animated:NO]; - tableViewController.delegate = self; - - // Need to correct the view rectangle of the navigation view to correct for the status bar gap - UIView *tableView = tableNavigationController.view; - CGRect tableFrame = tableView.frame; - tableFrame.origin.y -= 20; - tableView.frame = tableFrame; - toggleViewDisabled = NO; - } - - return tableNavigationController; -} - - - -@end diff --git a/SLSMoleculeSearchViewController.h b/SLSMoleculeSearchViewController.h deleted file mode 100644 index a877657..0000000 --- a/SLSMoleculeSearchViewController.h +++ /dev/null @@ -1,42 +0,0 @@ -// -// SLSMoleculeSearchViewController.h -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 7/22/2008. -// -// This handles the keyword searching functionality of the Protein Data Bank - -#import -#import "SLSMoleculeTableViewController.h" -#import "SLSMoleculeDownloadController.h" - -@interface SLSMoleculeSearchViewController : UITableViewController -{ - UISearchBar *keywordSearchBar; - NSMutableArray *searchResultTitles, *searchResultIDs, *searchResultIUPACNames; - NSMutableData *downloadedFileContents; - NSURLConnection *searchResultRetrievalConnection, *nextResultsRetrievalConnection; - NSUInteger currentPageOfResults; - BOOL searchCancelled, isDownloading, isRetrievingCompoundNames; - NSInteger indexOfDownloadingMolecule; - - SLSSearchType currentSearchType; - SLSMoleculeDownloadController *downloadController; - NSMutableString *currentXMLElementString; - NSXMLParser *searchResultsParser; - - BOOL insideIUPACName, insideSynonym; -} - -// Performing search -- (BOOL)performSearchWithKeyword:(NSString *)keyword; -- (void)processSearchResultsAppendingNewData:(BOOL)appendData; -- (void)processPDBSearchResults; -- (void)processPubChemKeywordSearch; -- (void)retrievePubChemCompoundTitles; - -- (BOOL)grabNextSetOfSearchResults; - -@end diff --git a/SLSMoleculeSearchViewController.m b/SLSMoleculeSearchViewController.m deleted file mode 100644 index b686f3f..0000000 --- a/SLSMoleculeSearchViewController.m +++ /dev/null @@ -1,872 +0,0 @@ -// -// SLSMoleculeSearchViewController.m -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 7/22/2008. -// -// This handles the keyword searching functionality of the Protein Data Bank - -#import "SLSMoleculeSearchViewController.h" -#import "SLSMoleculeDownloadController.h" -#import "SLSMoleculeTableViewController.h" -#import "VCTitleCase.h" -#import "SLSMoleculeAppDelegate.h" -#import "SLSMoleculeWebDetailViewController.h" - -#define MAX_SEARCH_RESULT_CODES 10 - -@implementation SLSMoleculeSearchViewController - -#pragma mark - -#pragma mark Initialization and teardown - -- (id)initWithStyle:(UITableViewStyle)style -{ - if ((self = [super initWithStyle:style])) - { - // Initialize the search bar and title - - self.view.frame = [[UIScreen mainScreen] bounds]; - self.view.autoresizesSubviews = YES; - - keywordSearchBar = [[UISearchBar alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 44.0f)]; - keywordSearchBar.placeholder = NSLocalizedStringFromTable(@"Search PubChem", @"Localized", nil); - keywordSearchBar.delegate = self; - keywordSearchBar.autocorrectionType = UITextAutocorrectionTypeNo; - keywordSearchBar.scopeButtonTitles = [NSArray arrayWithObjects:@"PubChem", @"Protein Data Bank", nil]; - keywordSearchBar.showsScopeBar = YES; - [keywordSearchBar sizeToFit]; - - currentSearchType = PUBCHEMSEARCH; - -// if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) -// { -// keywordSearchBar.barStyle = UIBarStyleBlack; -// } - [keywordSearchBar becomeFirstResponder]; - - self.navigationItem.title = NSLocalizedStringFromTable(@"Search For Molecules", @"Localized", nil); - self.navigationItem.rightBarButtonItem = nil; - - self.tableView.tableHeaderView = keywordSearchBar; - - downloadedFileContents = nil; - searchResultTitles = nil; - searchResultIDs = nil; - searchResultRetrievalConnection = nil; - nextResultsRetrievalConnection = nil; - searchCancelled = NO; - currentPageOfResults = 0; - - if ([SLSMoleculeAppDelegate isRunningOniPad]) - { - self.preferredContentSize = CGSizeMake(320.0, 700.0); - } - - } - return self; -} - -- (void)viewDidLoad; -{ - [super viewDidLoad]; - - if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) - { -// // self.tableView.backgroundColor = [UIColor colorWithRed:0.0f green:0.0f blue:0.054f alpha:1.0f]; -// self.tableView.backgroundColor = [UIColor blackColor]; -// self.tableView.separatorColor = [UIColor clearColor]; -// self.tableView.rowHeight = 50.0; - - self.tableView.backgroundColor = [UIColor whiteColor]; -// CAGradientLayer *shadowGradient = [SLSMoleculeTableViewController shadowGradientForSize:CGSizeMake(320.0f, self.navigationController.view.frame.size.height)]; -// [self.navigationController.view.layer setMask:shadowGradient]; -// self.navigationController.view.layer.masksToBounds = NO; - } - else - { - self.tableView.backgroundColor = [UIColor whiteColor]; - } -} - -- (void)dealloc -{ - downloadController = nil; - - currentXMLElementString = nil; - -} - -#pragma mark - -#pragma mark Performing search - -- (BOOL)performSearchWithKeyword:(NSString *)keyword; -{ - // Clear the old search results table - searchResultTitles = nil; - - searchResultIDs = nil; - - searchResultIUPACNames = nil; - - NSString *searchURL = nil; - - if (currentSearchType == PROTEINDATABANKSEARCH) - { - searchURL = [[NSString alloc] initWithFormat:@"http://www.rcsb.org/pdb/search/navbarsearch.do?newSearch=yes&isAuthorSearch=no&radioset=All&inputQuickSearch=%@&outformat=text&resultsperpage=%d", [keyword stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding], MAX_SEARCH_RESULT_CODES]; - } - else - { - //http://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi?db=pccompound&retmax=10&term=benzene - isRetrievingCompoundNames = NO; - - NSString *keywordWithFilter = [keyword stringByAppendingString:@" \"has 3d conformer\"[Filter]"]; - searchURL = [[NSString alloc] initWithFormat:@"http://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi?db=pccompound&retmax=%d&term=%@", MAX_SEARCH_RESULT_CODES, [keywordWithFilter stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]; - } - - - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; - - NSURLRequest *pdbSearchRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:searchURL] - cachePolicy:NSURLRequestUseProtocolCachePolicy - timeoutInterval:60.0]; - searchResultRetrievalConnection = [[NSURLConnection alloc] initWithRequest:pdbSearchRequest delegate:self]; - - downloadedFileContents = [NSMutableData data]; - - if (searchResultRetrievalConnection) - { - [self.tableView reloadData]; - } - else - { - return NO; - } - return YES; -} - -- (void)processSearchResultsAppendingNewData:(BOOL)appendData; -{ - if (!appendData) - { - searchResultRetrievalConnection = nil; - - searchResultTitles = [[NSMutableArray alloc] init]; - searchResultIDs = [[NSMutableArray alloc] init]; - } - else - { - nextResultsRetrievalConnection = nil; - } - - if (currentSearchType == PROTEINDATABANKSEARCH) - { - [self processPDBSearchResults]; - } - else - { - [self processPubChemKeywordSearch]; - } - - [self.tableView reloadData]; -} - -- (void)processPDBSearchResults; -{ - NSString *titlesAndPDBCodeString = [[NSString alloc] initWithData:downloadedFileContents encoding:NSASCIIStringEncoding]; - downloadedFileContents = nil; - - NSRange locationOfHTMLTag = [titlesAndPDBCodeString rangeOfString:@""]; - NSRange locationOfTitleEnd = [titlesAndPDBCodeString rangeOfString:@""]; - if ( (locationOfTitleStart.location == NSNotFound) || (locationOfTitleEnd.location == NSNotFound) ) - { - titleString = pdbCode; - } - else - { - // RCSB Protein Data Bank - Structure Summary for 1BNA - STRUCTURE OF A B-DNA DODECAMER. CONFORMATION AND DYNAMICS - - titleString = [titlesAndPDBCodeString substringWithRange:NSMakeRange(locationOfTitleStart.location + locationOfTitleStart.length, locationOfTitleEnd.location - (locationOfTitleStart.location + locationOfTitleStart.length))]; - NSRange beginningOfActualTitle = [titleString rangeOfString:pdbCode]; - if (beginningOfActualTitle.location != NSNotFound) - { - titleString = [titleString substringFromIndex:beginningOfActualTitle.location + 7]; - } - - } - - [searchResultTitles addObject:titleString]; - [searchResultIDs addObject:pdbCode]; - } - else - { - // Normal search result, so process as expected - if ([[[titlesAndPDBCodeString substringToIndex:5] stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] isEqualToString:@""]) - { - // No results match this query - currentPageOfResults = 1; - [self.tableView reloadData]; - return; - } - - NSUInteger length = [titlesAndPDBCodeString length]; - NSUInteger lineStart = 0, lineEnd = 0, contentsEnd = 0; - NSRange currentRange; - - while (lineEnd < length) - { - // NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; - - [titlesAndPDBCodeString getParagraphStart:&lineStart end:&lineEnd contentsEnd:&contentsEnd forRange:NSMakeRange(lineEnd, 0)]; - currentRange = NSMakeRange(lineStart, contentsEnd - lineStart); - NSString *currentLine = [titlesAndPDBCodeString substringWithRange:currentRange]; - - - NSArray *lineComponents = [currentLine componentsSeparatedByString:@"\t"]; - if ([lineComponents count] > 1) - { - NSString *pdbCode = [lineComponents objectAtIndex:0]; - NSString *moleculeTitle = [lineComponents objectAtIndex:1]; - if ((pdbCode != nil) && (moleculeTitle != nil)) - { - [searchResultTitles addObject:moleculeTitle]; - [searchResultIDs addObject:pdbCode]; - } - } - - // [pool release]; - } - } - - currentPageOfResults = 1; -} - -- (void)processPubChemKeywordSearch; -{ - currentXMLElementString = nil; - - searchResultsParser = [[NSXMLParser alloc] initWithData:downloadedFileContents]; - downloadedFileContents = nil; - - searchResultsParser.delegate = self; - [searchResultsParser setShouldResolveExternalEntities:YES]; - [searchResultsParser parse]; -} - -- (void)retrievePubChemCompoundTitles; -{ - NSMutableString *compoundIDList = [[NSMutableString alloc] init]; - - BOOL isFirstID = YES; - for (NSString *currentCompoundID in searchResultIDs) - { - if (!isFirstID) - { - [compoundIDList appendFormat:@",%@", currentCompoundID]; - } - else - { - [compoundIDList appendString:currentCompoundID]; - isFirstID = NO; - } - } - - //http://eutils.ncbi.nlm.nih.gov/entrez/eutils/esearch.fcgi?db=pccompound&retmax=10&term=benzene - isRetrievingCompoundNames = YES; - NSString *searchURL = [[NSString alloc] initWithFormat:@"http://eutils.ncbi.nlm.nih.gov/entrez/eutils/esummary.fcgi?db=pccompound&id=%@", compoundIDList]; - - - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; - - NSURLRequest *sdfSearchRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:searchURL] - cachePolicy:NSURLRequestUseProtocolCachePolicy - timeoutInterval:60.0]; - searchResultRetrievalConnection = [[NSURLConnection alloc] initWithRequest:sdfSearchRequest delegate:self]; - - downloadedFileContents = [NSMutableData data]; - - if (!searchResultRetrievalConnection) - { - // TODO: Some sort of error handling - } -} - -- (void)processPubChemCompoundTitles; -{ - - searchResultIUPACNames = [[NSMutableArray alloc] init]; - - searchResultRetrievalConnection = nil; - - currentXMLElementString = nil; - - searchResultsParser = [[NSXMLParser alloc] initWithData:downloadedFileContents]; - downloadedFileContents = nil; - - searchResultsParser.delegate = self; - [searchResultsParser setShouldResolveExternalEntities:YES]; - [searchResultsParser parse]; -} - -- (BOOL)grabNextSetOfSearchResults; -{ - currentPageOfResults++; - NSString *nextResultsURL = [[NSString alloc] initWithFormat:@"http://www.rcsb.org/pdb/results/results.do?outformat=text&gotopage=%ld", currentPageOfResults]; - - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES]; - - NSURLRequest *pdbSearchRequest=[NSURLRequest requestWithURL:[NSURL URLWithString:nextResultsURL] - cachePolicy:NSURLRequestUseProtocolCachePolicy - timeoutInterval:60.0]; - nextResultsRetrievalConnection = [[NSURLConnection alloc] initWithRequest:pdbSearchRequest delegate:self]; - - downloadedFileContents = [NSMutableData data]; - - if (nextResultsRetrievalConnection) - { - [self.tableView reloadData]; - } - else - { - return NO; - } - return YES; - -} - -#pragma mark - -#pragma mark UITableViewController methods - -- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView -{ - return 1; -} - -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section -{ - // Running a search, so display a status cell - if (searchResultRetrievalConnection != nil) - return 1; - else if (searchResultTitles == nil) - return 0; - // No results to the last search, so display one cell explaining that - else if ([searchResultTitles count] == 0) - return 1; - else - { - if (currentSearchType == PROTEINDATABANKSEARCH) - { - return [searchResultTitles count] + 1; - } - else - { - return [searchResultTitles count]; - } - } -} - -- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath -{ - return 60.0; -} - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath; -{ - UITableViewCell *cell; - // Running a search, so display a status cell - if ((searchResultRetrievalConnection != nil) || ((nextResultsRetrievalConnection != nil) && (indexPath.row >= [searchResultTitles count]))) - { - cell = [tableView dequeueReusableCellWithIdentifier:@"SearchInProgress"]; - if (cell == nil) - { -// cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"SearchInProgress"] autorelease]; - cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"SearchInProgress"]; - -// if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) -// { -// cell.backgroundColor = [UIColor blackColor]; -// cell.textLabel.textColor = [UIColor colorWithWhite:0.8 alpha:1.0]; -// } -// else -// { - cell.textLabel.textColor = [UIColor blackColor]; -// } - - cell.textLabel.font = [UIFont boldSystemFontOfSize:12.0]; - - // CGRect frame = CGRectMake(CGRectGetMaxX(cell.contentView.bounds) - 250.0, 5.0, 240.0, 32.0); -// CGRect frame = CGRectMake(CGRectGetMaxX(cell.contentView.bounds) - 70.0, 14.0, 32.0, 32.0); - CGRect frame = CGRectMake(CGRectGetMaxX(cell.contentView.bounds) - 70.0f, 20.0f, 20.0f, 20.0f); - UIActivityIndicatorView *spinningIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; - [spinningIndicator startAnimating]; - spinningIndicator.frame = frame; - [cell.contentView addSubview:spinningIndicator]; - cell.accessoryType = UITableViewCellAccessoryNone; - cell.textLabel.font = [UIFont systemFontOfSize:16.0]; - cell.textLabel.textAlignment = NSTextAlignmentCenter; - } - cell.textLabel.text = NSLocalizedStringFromTable(@"Searching...", @"Localized", nil); - } - else if (searchResultTitles == nil) - { - cell = nil; - } - // No results to the last search, so display one cell explaining that - else if ([searchResultTitles count] == 0) - { - cell = [tableView dequeueReusableCellWithIdentifier:@"NoResults"]; - if (cell == nil) - { -// cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"NoResults"] autorelease]; - cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"NoResults"]; - -// if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) -// { -// cell.backgroundColor = [UIColor blackColor]; -// cell.textLabel.textColor = [UIColor colorWithWhite:0.8 alpha:1.0]; -// } -// else -// { - cell.textLabel.textColor = [UIColor blackColor]; -// } - - cell.textLabel.font = [UIFont systemFontOfSize:16.0]; - cell.textLabel.text = NSLocalizedStringFromTable(@"No results", @"Localized", nil); - cell.textLabel.textAlignment = NSTextAlignmentCenter; - cell.accessoryType = UITableViewCellAccessoryNone; - } - } - else if ((isDownloading) && ([indexPath row] == indexOfDownloadingMolecule)) - { - cell = [tableView dequeueReusableCellWithIdentifier:@"DownloadInProgress"]; - if (cell == nil) - { - -// cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"DownloadInProgress"] autorelease]; - cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"DownloadInProgress"]; - -// if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) -// { -// cell.backgroundColor = [UIColor blackColor]; -// cell.textLabel.textColor = [UIColor colorWithWhite:0.8 alpha:1.0]; -// } -// else -// { - cell.textLabel.textColor = [UIColor blackColor]; -// } - - cell.textLabel.font = [UIFont boldSystemFontOfSize:12.0]; - - // CGRect frame = CGRectMake(CGRectGetMaxX(cell.contentView.bounds) - 250.0, 5.0, 240.0, 32.0); - // CGRect frame = CGRectMake(CGRectGetMaxX(cell.contentView.bounds) - 70.0, 14.0, 32.0, 32.0); - CGRect frame = CGRectMake(CGRectGetMaxX(cell.contentView.bounds) - 70.0f, 20.0f, 20.0f, 20.0f); - UIActivityIndicatorView *spinningIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; - [spinningIndicator startAnimating]; - spinningIndicator.frame = frame; - [cell.contentView addSubview:spinningIndicator]; - cell.accessoryType = UITableViewCellAccessoryNone; - cell.textLabel.font = [UIFont systemFontOfSize:16.0]; - cell.textLabel.textAlignment = NSTextAlignmentCenter; - } - cell.textLabel.text = NSLocalizedStringFromTable(@"Downloading...", @"Localized", nil); - } - else - { - if ([indexPath row] >= [searchResultTitles count]) - { - cell = [tableView dequeueReusableCellWithIdentifier:@"LoadMore"]; - if (cell == nil) - { -// cell = [[[UITableViewCell alloc] initWithFrame:CGRectZero reuseIdentifier:@"LoadMore"] autorelease]; - cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"LoadMore"]; - -// if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) -// { -// cell.backgroundColor = [UIColor blackColor]; -// cell.textLabel.textColor = [UIColor colorWithWhite:0.8 alpha:1.0]; -// } -// else -// { - cell.textLabel.textColor = [UIColor blackColor]; -// } - - cell.textLabel.font = [UIFont systemFontOfSize:16.0]; - cell.textLabel.textAlignment = NSTextAlignmentCenter; - cell.textLabel.text = NSLocalizedStringFromTable(@"Load next 10 results", @"Localized", nil); - cell.accessoryType = UITableViewCellAccessoryNone; - cell.detailTextLabel.text = @""; - } - } - else - { - cell = [tableView dequeueReusableCellWithIdentifier:NSLocalizedStringFromTable(@"Results", @"Localized", nil)]; - if (cell == nil) - { - cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:NSLocalizedStringFromTable(@"Results", @"Localized", nil)]; - -// if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) -// { -// cell.backgroundColor = [UIColor blackColor]; -// cell.textLabel.textColor = [UIColor colorWithWhite:0.8 alpha:1.0]; -// CAGradientLayer *glowGradientLayer = [SLSMoleculeTableViewController glowGradientForSize:CGSizeMake(self.view.frame.size.width, 60.0)]; -// -// [cell.layer insertSublayer:glowGradientLayer atIndex:10]; -// } -// else -// { - cell.textLabel.textColor = [UIColor blackColor]; -// } - cell.selectionStyle = UITableViewCellSelectionStyleNone; - - cell.textLabel.font = [UIFont boldSystemFontOfSize:14.0]; - cell.textLabel.numberOfLines = 2; - cell.detailTextLabel.font = [UIFont boldSystemFontOfSize:14.0]; - -// cell.textLabel.font = [UIFont boldSystemFontOfSize:12.0]; -// cell.detailTextLabel.font = [UIFont boldSystemFontOfSize:12.0]; - } - - if ((isDownloading) && ([indexPath row] != indexOfDownloadingMolecule)) - { -// cell.contentView.backgroundColor = [UIColor colorWithWhite:0.6 alpha:1.0]; - cell.textLabel.textColor = [UIColor colorWithWhite:0.3 alpha:1.0]; - cell.detailTextLabel.textColor = [UIColor colorWithWhite:0.2 alpha:1.0]; - } - - cell.textLabel.text = [searchResultTitles objectAtIndex:[indexPath row]]; - if (currentSearchType == PROTEINDATABANKSEARCH) - { - cell.detailTextLabel.text = [searchResultIDs objectAtIndex:[indexPath row]]; - } - else - { - cell.detailTextLabel.text = [searchResultIUPACNames objectAtIndex:[indexPath row]]; - } - - cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton; - } - } - - return cell; -} - -- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath -{ - if ((isDownloading) && ([indexPath row] != indexOfDownloadingMolecule)) - { - cell.backgroundColor = [UIColor colorWithWhite:0.4 alpha:1.0]; - } - else - { - cell.backgroundColor = [UIColor whiteColor]; - } -} - -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath -{ - // Prevent any crashes by clicking on a non-normal cell - if (searchResultRetrievalConnection != nil) - { - return; - } - else if (searchResultTitles == nil) - { - return; - } - // No results to the last search, so display one cell explaining that - else if ([searchResultTitles count] == 0) - { - return; - } - else if (isDownloading) - { - return; - } - - if (indexPath.row >= [searchResultTitles count]) - { - [self grabNextSetOfSearchResults]; - } - else - { - indexOfDownloadingMolecule = indexPath.row; - isDownloading = YES; - self.tableView.backgroundColor = [UIColor colorWithWhite:0.4 alpha:1.0]; - self.tableView.separatorColor = [UIColor colorWithWhite:0.4 alpha:1.0]; - - [self.tableView reloadData]; - - NSString *selectedTitle = [searchResultTitles objectAtIndex:[indexPath row]]; - NSString *selectedID = [searchResultIDs objectAtIndex:[indexPath row]]; - - downloadController = [[SLSMoleculeDownloadController alloc] initWithID:selectedID title:selectedTitle searchType:currentSearchType]; - - [downloadController downloadNewMolecule]; -// -// SLSMoleculeDownloadViewController *downloadViewController = [[SLSMoleculeDownloadViewController alloc] initWithPDBCode:selectedPDBCode andTitle:selectedTitle]; -// -// [self.navigationController pushViewController:downloadViewController animated:YES]; -// [downloadViewController release]; - } -} - -- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath -{ -// NSInteger index = [indexPath row]; - - NSString *selectedID = [searchResultIDs objectAtIndex:[indexPath row]]; - NSString *webDetailAddress = nil; - - if (currentSearchType == PROTEINDATABANKSEARCH) - { - webDetailAddress = [NSString stringWithFormat:@"http://www.rcsb.org/pdb/explore/explore.do?structureId=%@", selectedID]; - } - else - { - webDetailAddress = [NSString stringWithFormat:@"http://pubchem.ncbi.nlm.nih.gov/summary/summary.cgi?cid=%@", selectedID]; - } - - SLSMoleculeWebDetailViewController *detailViewController = [[SLSMoleculeWebDetailViewController alloc] initWithURL:[NSURL URLWithString:webDetailAddress]]; - - [self.navigationController pushViewController:detailViewController animated:YES]; -} - -- (void)didReceiveMemoryWarning -{ -} - -#pragma mark - -#pragma mark UIViewController methods - -- (void)viewWillDisappear:(BOOL)animated -{ - keywordSearchBar.delegate = self; - - [super viewWillDisappear:animated]; -} - -- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation { - // Overriden to allow any orientation. - return YES; -} - -#pragma mark - -#pragma mark UISearchBarDelegate methods - -- (void)searchBarSearchButtonClicked:(UISearchBar *)searchBar; -{ - // Hide the keyboard once search has been initiated - [searchBar resignFirstResponder]; - [self performSearchWithKeyword:searchBar.text]; -} - -- (void)searchBar:(UISearchBar *)searchBar selectedScopeButtonIndexDidChange:(NSInteger)selectedScope -{ - searchResultTitles = nil; - - searchResultIDs = nil; - - switch (selectedScope) - { - case PUBCHEMSEARCH: - { - keywordSearchBar.placeholder = NSLocalizedStringFromTable(@"Search PubChem", @"Localized", nil); - }; break; - case PROTEINDATABANKSEARCH: - default: - { - keywordSearchBar.placeholder = NSLocalizedStringFromTable(@"Search RCSB Protein Data Bank", @"Localized", nil); - }; break; - } - - currentSearchType = (SLSSearchType)selectedScope; - [self.tableView reloadData]; -} - -#pragma mark - -#pragma mark NSURLConnection delegate methods - -- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error; -{ - NSString *connectionError = nil; - if (currentSearchType == PROTEINDATABANKSEARCH) - { - connectionError = NSLocalizedStringFromTable(@"Could not connect to the Protein Data Bank", @"Localized", nil); - } - else - { - connectionError = NSLocalizedStringFromTable(@"Could not connect to PubChem", @"Localized", nil); - } - - UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedStringFromTable(@"Connection failed", @"Localized", nil) message:connectionError - delegate:self cancelButtonTitle:NSLocalizedStringFromTable(@"OK", @"Localized", nil) otherButtonTitles: nil, nil]; - [alert show]; - - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; - - downloadedFileContents = nil; - - searchResultRetrievalConnection = nil; - - nextResultsRetrievalConnection = nil; - - [self.tableView reloadData]; -} - -- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data; -{ - // Concatenate the new data with the existing data to build up the downloaded file - // Update the status of the download - - [downloadedFileContents appendData:data]; - - if (searchCancelled) - { - [connection cancel]; - downloadedFileContents = nil; - - // Release connection? - [self.tableView reloadData]; - - searchCancelled = NO; - return; - } -} - -- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response; -{ - // TODO: Deal with a 404 error by checking filetype header -} - -- (void)connectionDidFinishLoading:(NSURLConnection *)connection; -{ - [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; - - if (isRetrievingCompoundNames) - { - [self processPubChemCompoundTitles]; - } - else - { - if (connection == searchResultRetrievalConnection) - { - [self processSearchResultsAppendingNewData:NO]; - } - else - { - [self processSearchResultsAppendingNewData:YES]; - } - } -} - -#pragma mark - -#pragma mark NSXMLParser delegate methods - -// Append new characters from within the element to an existing, or newly created, string -- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string -{ - if (currentXMLElementString == nil) - { - currentXMLElementString = [[NSMutableString alloc] init]; - } - [currentXMLElementString appendString:string]; -} - -- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict -{ - if (isRetrievingCompoundNames) - { - if ([elementName isEqualToString:@"Item"]) - { - NSString *attributeName = [attributeDict valueForKey:@"Name"]; - - if ([attributeName isEqualToString:@"IUPACName"]) - { - insideIUPACName = YES; - } - else if ([attributeName isEqualToString:@"SynonymList"]) - { - insideSynonym = YES; - } - } - } -} - -- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName -{ - if (!isRetrievingCompoundNames) - { - if ([elementName isEqualToString:@"Id"]) - { - // Last item is nil, check for that - if (currentXMLElementString != nil) - { - NSString *trimmedID = [currentXMLElementString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; - [searchResultIDs addObject:trimmedID]; - } - } - } - else - { - if (insideIUPACName) - { - NSString *trimmedIUPACName = [currentXMLElementString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]]; - - [searchResultIUPACNames addObject:trimmedIUPACName]; - insideIUPACName = NO; - } - else if (insideSynonym) - { - NSString *tweakedSynonym = [[currentXMLElementString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] titlecaseString]; - [searchResultTitles addObject:tweakedSynonym]; - - insideSynonym = NO; - } - } - - currentXMLElementString = nil; -} - -- (void)parserDidEndDocument:(NSXMLParser *)parser; -{ -// [self finishParsingXML]; - if (!isRetrievingCompoundNames) - { - [self retrievePubChemCompoundTitles]; - } - else - { - [self.tableView reloadData]; - isRetrievingCompoundNames = NO; - } -} - -- (void)parser:(NSXMLParser *)parser parseErrorOccurred:(NSError *)parseError -{ - -} - -#pragma mark - -#pragma mark Accessors - - -@end - diff --git a/SLSMoleculeTableViewController.h b/SLSMoleculeTableViewController.h deleted file mode 100644 index 7f35f8d..0000000 --- a/SLSMoleculeTableViewController.h +++ /dev/null @@ -1,48 +0,0 @@ -// -// SLSMoleculeTableViewController.h -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 6/30/2008. -// -// This controller manages the root table of molecules that are stored on the device - -#import -#import - -#import "SLSMoleculeRootViewController.h" -#import "SLSMoleculeSearchViewController.h" -#import "SLSMoleculeDownloadController.h" -#import "SLSMoleculeDetailViewController.h" -#import "SLSMoleculeCustomDownloadViewController.h" - - -@interface SLSMoleculeTableViewController : UITableViewController -{ - NSMutableArray *molecules; - SLSMoleculeRootViewController *__unsafe_unretained delegate; - NSInteger selectedIndex; - UIColor *tableTextColor; - - sqlite3 *database; -} - -@property(readwrite,unsafe_unretained) SLSMoleculeRootViewController *delegate; -@property(readwrite,assign) sqlite3 *database; -@property(readwrite,strong) NSMutableArray *molecules; -@property(readwrite) NSInteger selectedIndex; - -// Initialization and teardown -- (id)initWithStyle:(UITableViewStyle)style initialSelectedMoleculeIndex:(NSInteger)initialSelectedMoleculeIndex; - -// Table customization -+ (CAGradientLayer *)glowGradientForSize:(CGSize)gradientSize; -+ (CAGradientLayer *)shadowGradientForSize:(CGSize)gradientSize; - -- (IBAction)displayMoleculeDownloadView; -- (IBAction)switchBackToGLView; - -- (void)moleculeDidFinishDownloading:(NSNotification *)note; - -@end diff --git a/SLSMoleculeTableViewController.m b/SLSMoleculeTableViewController.m deleted file mode 100644 index 06097c2..0000000 --- a/SLSMoleculeTableViewController.m +++ /dev/null @@ -1,432 +0,0 @@ -// -// SLSMoleculeTableViewController.m -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 6/30/2008. -// -// This controller manages the root table of molecules that are stored on the device - -#import "SLSMoleculeTableViewController.h" -#import "SLSMoleculeRootViewController.h" -#import "SLSMoleculeDataSourceViewController.h" -#import "SLSMoleculeSearchViewController.h" -#import "SLSMolecule.h" -#import "SLSMoleculeAppDelegate.h" -#import "SLSMoleculeLibraryTableCell.h" - -@implementation SLSMoleculeTableViewController - -#pragma mark - -#pragma mark Initialization and breakdown - -- (id)initWithStyle:(UITableViewStyle)style initialSelectedMoleculeIndex:(NSInteger)initialSelectedMoleculeIndex; -{ - if ((self = [super initWithStyle:style])) - { - self.title = NSLocalizedStringFromTable(@"Molecules", @"Localized", nil); - selectedIndex = initialSelectedMoleculeIndex; - - self.navigationItem.rightBarButtonItem = self.editButtonItem; - - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(moleculeDidFinishDownloading:) name:@"MoleculeDidFinishDownloading" object:nil]; - - - if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) - { - self.preferredContentSize = CGSizeMake(320.0, 600.0); - } - - if ([SLSMoleculeAppDelegate isRunningOniPad]) - { -// self.tableView.backgroundColor = [UIColor blackColor]; -// tableTextColor = [[UIColor whiteColor] retain]; - self.preferredContentSize = CGSizeMake(320.0, 600.0); - - UIBarButtonItem *downloadButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemAdd target:self action:@selector(displayMoleculeDownloadView)]; - self.navigationItem.leftBarButtonItem = downloadButtonItem; - } - else - { -// tableTextColor = [[UIColor blackColor] retain]; - UIBarButtonItem *modelButtonItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedStringFromTable(@"3D Model", @"Localized", nil) style:UIBarButtonItemStylePlain target:self action:@selector(switchBackToGLView)]; - self.navigationItem.leftBarButtonItem = modelButtonItem; - } - } - return self; -} - -- (void)viewDidLoad; -{ - [super viewDidLoad]; - - if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) - { - // self.tableView.backgroundColor = [UIColor colorWithRed:0.0f green:0.0f blue:0.054f alpha:1.0f]; - self.tableView.backgroundColor = [UIColor blackColor]; - self.tableView.separatorColor = [UIColor clearColor]; - self.tableView.rowHeight = 50.0; - -// CAGradientLayer *shadowGradient = [SLSMoleculeTableViewController shadowGradientForSize:CGSizeMake(320.0f, self.navigationController.view.frame.size.height)]; -// [self.navigationController.view.layer setMask:shadowGradient]; -// self.navigationController.view.layer.masksToBounds = NO; - } - else - { - self.tableView.backgroundColor = [UIColor whiteColor]; - } -} - - -#pragma mark - -#pragma mark View switching - -- (IBAction)switchBackToGLView; -{ - [[NSNotificationCenter defaultCenter] postNotificationName:@"ToggleView" object:nil]; -} - -- (IBAction)displayMoleculeDownloadView; -{ - SLSMoleculeSearchViewController *searchViewController = [[SLSMoleculeSearchViewController alloc] initWithStyle:UITableViewStylePlain]; - - [self.navigationController pushViewController:searchViewController animated:YES]; - -/* - SLSMoleculeDataSourceViewController *dataSourceViewController = [[SLSMoleculeDataSourceViewController alloc] initWithStyle:UITableViewStylePlain]; - - [self.navigationController pushViewController:dataSourceViewController animated:YES]; - [dataSourceViewController release]; - */ -} - -- (void)moleculeDidFinishDownloading:(NSNotification *)note; -{ - if ([note object] == nil) - { - [self.navigationController popToViewController:self animated:YES]; - return; - } - - NSString *filename = [note object]; - - // Add the new protein to the list by gunzipping the data and pulling out the title - - SLSMolecule *newMolecule = [[SLSMolecule alloc] initWithFilename:filename database:database title:[[note userInfo] objectForKey:@"title"]]; - if (newMolecule == nil) - { - UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedStringFromTable(@"Error in downloaded file", @"Localized", nil) message:NSLocalizedStringFromTable(@"The molecule file is either corrupted or not of a supported format", @"Localized", nil) - delegate:self cancelButtonTitle:NSLocalizedStringFromTable(@"OK", @"Localized", nil) otherButtonTitles: nil, nil]; - [alert show]; - - // Delete the corrupted or sunsupported file - NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); - NSString *documentsDirectory = [paths objectAtIndex:0]; - - NSError *error = nil; - if (![[NSFileManager defaultManager] removeItemAtPath:[documentsDirectory stringByAppendingPathComponent:filename] error:&error]) - { - UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedStringFromTable(@"Could not delete file", @"Localized", nil) message:[error localizedDescription] - delegate:self cancelButtonTitle:NSLocalizedStringFromTable(@"OK", @"Localized", nil) otherButtonTitles: nil, nil]; - [alert show]; - return; - } - - } - else - { - [molecules addObject:newMolecule]; - - if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) - { - selectedIndex = ([molecules count] - 1); - - [self.delegate selectedMoleculeDidChange:selectedIndex]; - } - - [self.tableView reloadData]; -// [self.tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:[NSIndexPath indexPathForRow:([molecules count] - 1) inSection:0]] withRowAnimation:UITableViewRowAnimationBottom]; - } - - [self.navigationController popToViewController:self animated:YES]; -} - -#pragma mark - -#pragma mark Table customization - -+ (CAGradientLayer *)glowGradientForSize:(CGSize)gradientSize; -{ - CAGradientLayer *newGlow = [[CAGradientLayer alloc] init]; - // self.tableView.rowHeight = 20.0f + MAXHEIGHTFOREQUATIONSINTABLEVIEW; - - CGRect newGlowFrame = CGRectMake(0, 0, gradientSize.width, gradientSize.height); - newGlow.frame = newGlowFrame; - UIColor *topColor = [UIColor colorWithRed:1.0f green:1.0f blue:1.0f alpha:0.20f]; - UIColor *middleColor = [UIColor colorWithRed:1.0f green:1.0f blue:1.0f alpha:0.0f]; - UIColor *bottomColor = [UIColor colorWithRed:1.0f green:1.0f blue:1.0f alpha:0.08f]; - - newGlow.colors = [NSArray arrayWithObjects:(id)[topColor CGColor], (id)[middleColor CGColor], (id)[bottomColor CGColor], nil]; - return newGlow; -} - -+ (CAGradientLayer *)shadowGradientForSize:(CGSize)gradientSize; -{ - CAGradientLayer *newShadow = [[CAGradientLayer alloc] init]; - newShadow.startPoint = CGPointMake(1.0f, 0.5); - newShadow.endPoint = CGPointMake(0.9f, 0.5); - - CGRect newShadowFrame = CGRectMake(0, 0, gradientSize.width, gradientSize.height); - newShadow.frame = newShadowFrame; - UIColor *rightColor = [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.5f]; - UIColor *leftColor = [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:1.0f]; - newShadow.colors = [NSArray arrayWithObjects:(id)[rightColor CGColor], (id)[leftColor CGColor], nil]; - return newShadow; -} - -#pragma mark - -#pragma mark Table view data source delegate methods - -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath; -{ - UITableViewCell *cell; - NSInteger index = [indexPath row]; - - if ([SLSMoleculeAppDelegate isRunningOniPad]) - index++; - - if (index == 0) - { - cell = [tableView dequeueReusableCellWithIdentifier:@"Download"]; - if (cell == nil) - { - cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"Download"]; - - if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) - { - cell.backgroundColor = [UIColor blackColor]; - cell.textLabel.textColor = [UIColor whiteColor]; - cell.selectionStyle = UITableViewCellSelectionStyleNone; - } - else - { - cell.textLabel.textColor = [UIColor blackColor]; - } - - } - - cell.textLabel.text = NSLocalizedStringFromTable(@"Download new molecules", @"Localized", nil); - cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator; - cell.textLabel.textColor = [UIColor blackColor]; - } - else - { - cell = [tableView dequeueReusableCellWithIdentifier:@"Molecules"]; - if (cell == nil) - { - cell = [[SLSMoleculeLibraryTableCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:@"Molecules"]; - - if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) - { - cell.backgroundColor = [UIColor blackColor]; - cell.textLabel.textColor = [UIColor colorWithWhite:0.8 alpha:1.0]; - cell.selectionStyle = UITableViewCellSelectionStyleNone; - CAGradientLayer *glowGradientLayer = [SLSMoleculeTableViewController glowGradientForSize:CGSizeMake(self.view.frame.size.width, 50.0)]; - [(SLSMoleculeLibraryTableCell *)cell setHighlightGradientLayer:glowGradientLayer]; - - [cell.layer insertSublayer:glowGradientLayer atIndex:10]; - } - else - { - cell.textLabel.textColor = [UIColor blackColor]; - } - } - - if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) - { - if ((index - 1) == selectedIndex) - { - cell.textLabel.textColor = [UIColor colorWithRed:0 green:0.73 blue:0.95 alpha:1.0]; - - if (![(SLSMoleculeLibraryTableCell *)cell isSelected]) - { - CAGradientLayer *glowGradient = [(SLSMoleculeLibraryTableCell *)cell highlightGradientLayer]; - UIColor *topColor = [UIColor colorWithRed:0.5f green:0.7f blue:1.0f alpha:0.6f]; - UIColor *middleColor = [UIColor colorWithRed:0.5f green:0.7f blue:1.0f alpha:0.1f]; - UIColor *bottomColor = [UIColor colorWithRed:0.5585f green:0.672f blue:1.0f alpha:0.30f]; - glowGradient.colors = [NSArray arrayWithObjects:(id)[topColor CGColor], (id)[middleColor CGColor], (id)[bottomColor CGColor], nil]; - - [(SLSMoleculeLibraryTableCell *)cell setIsSelected:YES]; - } - } - else - { - cell.textLabel.textColor = [UIColor colorWithWhite:0.8 alpha:1.0]; - - if ([(SLSMoleculeLibraryTableCell *)cell isSelected]) - { - CAGradientLayer *glowGradient = [(SLSMoleculeLibraryTableCell *)cell highlightGradientLayer]; - UIColor *topColor = [UIColor colorWithRed:1.0f green:1.0f blue:1.0f alpha:0.20f]; - UIColor *middleColor = [UIColor colorWithRed:1.0f green:1.0f blue:1.0f alpha:0.0f]; - UIColor *bottomColor = [UIColor colorWithRed:1.0f green:1.0f blue:1.0f alpha:0.08f]; - glowGradient.colors = [NSArray arrayWithObjects:(id)[topColor CGColor], (id)[middleColor CGColor], (id)[bottomColor CGColor], nil]; - - [(SLSMoleculeLibraryTableCell *)cell setIsSelected:NO]; - } - } - } - else - { - if ((index - 1) == selectedIndex) - { - cell.textLabel.textColor = [UIColor blueColor]; - } - else - { - cell.textLabel.textColor = [UIColor blackColor]; - } - } - - cell.textLabel.text = [[molecules objectAtIndex:(index-1)] compound]; - - NSString *fileNameWithoutExtension = [[molecules objectAtIndex:(index-1)] filenameWithoutExtension]; - cell.detailTextLabel.text = fileNameWithoutExtension; - - - cell.accessoryType = UITableViewCellAccessoryDetailDisclosureButton; - } - - return cell; -} - -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section; -{ - if ([SLSMoleculeAppDelegate isRunningOniPad]) - { - return [molecules count]; - } - else - { - return ([molecules count] + 1); - } -} - -- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath -{ - NSInteger index = [indexPath row]; - if ([SLSMoleculeAppDelegate isRunningOniPad]) - { - index++; - indexPath = [NSIndexPath indexPathForRow:index inSection:[indexPath section]]; - } - - if (index == 0) - { - [self displayMoleculeDownloadView]; - } - else - { - selectedIndex = (index - 1); - - [self.delegate selectedMoleculeDidChange:(index - 1)]; - [tableView deselectRowAtIndexPath:indexPath animated:NO]; - [tableView reloadData]; - } -} - -- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath -{ - NSInteger index = [indexPath row]; - if ([SLSMoleculeAppDelegate isRunningOniPad]) - index++; - - if (index == 0) - [self displayMoleculeDownloadView]; - else - { - // Display detail view for the protein - SLSMoleculeDetailViewController *detailViewController = [[SLSMoleculeDetailViewController alloc] initWithStyle:UITableViewStyleGrouped andMolecule: [molecules objectAtIndex:(index - 1)]]; - - [self.navigationController pushViewController:detailViewController animated:YES]; - - } -} - -// Make sure that the "Download new molecules" item is not deletable -- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath; -{ - if ([SLSMoleculeAppDelegate isRunningOniPad]) - { - return UITableViewCellEditingStyleDelete; - } - else - { - if ([indexPath row] == 0) - { - return UITableViewCellEditingStyleNone; - } - else - { - return UITableViewCellEditingStyleDelete; - } - } -} - -// Manage deletion of a protein from disk -- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath -{ - NSInteger index = [indexPath row]; - if ([SLSMoleculeAppDelegate isRunningOniPad]) - { - index++; - } - - if (index == 0) // Can't delete the Download new molecules item - { - return; - } - // If row is deleted, remove it from the list. - if (editingStyle == UITableViewCellEditingStyleDelete) - { - [[molecules objectAtIndex:(index - 1)] deleteMolecule]; - [molecules removeObjectAtIndex:(index - 1)]; - if ( (index - 1) == selectedIndex ) - { - if ([molecules count] < 1) - { - [self.delegate selectedMoleculeDidChange:0]; - } - else - { - selectedIndex = 0; - [self.delegate selectedMoleculeDidChange:0]; - } - } - else if ( (index - 1) < selectedIndex ) - { - selectedIndex--; - } - [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; - [tableView reloadData]; - } -} - -- (void)didReceiveMemoryWarning -{ -} - -- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation -{ - // Overriden to allow any orientation. - return YES; -} - -#pragma mark - -#pragma mark Accessors - -@synthesize delegate; -@synthesize database; -@synthesize molecules; -@synthesize selectedIndex; - -@end diff --git a/SLSMoleculeWebDetailViewController.h b/SLSMoleculeWebDetailViewController.h deleted file mode 100644 index b3292bd..0000000 --- a/SLSMoleculeWebDetailViewController.h +++ /dev/null @@ -1,29 +0,0 @@ -// -// SLSMoleculeWebDetailViewController.h -// Molecules -// -// Created by Brad Larson on 4/30/2011. -// Copyright 2011 Sunset Lake Software LLC. All rights reserved. -// - -#import - - -@interface SLSMoleculeWebDetailViewController : UIViewController -{ - UIWebView *webDetailView; - NSURL *moleculeDetailWebPageURL; - - UIActivityIndicatorView *loadingActivityIndicator; -} - -@property (strong, nonatomic) NSURL *moleculeDetailWebPageURL; - -// Initialization and teardown -- (id)initWithURL:(NSURL *)moleculeWebPageURL; - -// Web navigation -- (void)goBackInWebView; - - -@end diff --git a/SLSMoleculeWebDetailViewController.m b/SLSMoleculeWebDetailViewController.m deleted file mode 100644 index b127e9a..0000000 --- a/SLSMoleculeWebDetailViewController.m +++ /dev/null @@ -1,132 +0,0 @@ -// -// SLSMoleculeWebDetailViewController.m -// Molecules -// -// Created by Brad Larson on 4/30/2011. -// Copyright 2011 Sunset Lake Software LLC. All rights reserved. -// - -#import "SLSMoleculeWebDetailViewController.h" - - -@implementation SLSMoleculeWebDetailViewController - -- (id)initWithURL:(NSURL *)moleculeWebPageURL; -{ - self = [super initWithNibName:nil bundle:nil]; - if (self) - { - self.moleculeDetailWebPageURL = moleculeWebPageURL; - } - return self; -} - -- (void)loadView -{ - webDetailView = [[UIWebView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 320.0f, 480.0f)]; - webDetailView.backgroundColor = [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:1.0f]; - webDetailView.scalesPageToFit = YES; - - webDetailView.delegate = self; - self.view = webDetailView; - self.view.backgroundColor = [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:1.0f]; - - self.title = @"Molecule Details"; - - // UIBarButtonItem *backButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemPlay target:self action:@selector(closeHelpView)]; - // backButtonItem.layer.transform = CATransform3DMakeScale(-1.0f, 1.0f, 1.0f); - UIBarButtonItem *backButtonItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedStringFromTable(@"Back", @"Localized", nil) style:UIBarButtonItemStylePlain target:self action:@selector(goBackInWebView)]; - backButtonItem.enabled = NO; - self.navigationItem.rightBarButtonItem = backButtonItem; - - NSURLRequest *theRequest = [NSURLRequest requestWithURL:moleculeDetailWebPageURL cachePolicy:NSURLRequestUseProtocolCachePolicy timeoutInterval:60.0]; - [webDetailView loadRequest:theRequest]; - - loadingActivityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray]; - CGSize indicatorSize = loadingActivityIndicator.frame.size; - loadingActivityIndicator.frame = CGRectMake(round(webDetailView.frame.size.width / 2.0f - indicatorSize.width / 2.0f), round(webDetailView.frame.size.height / 2.0f + indicatorSize.height / 2.0f), indicatorSize.width, indicatorSize.height); - [webDetailView addSubview:loadingActivityIndicator]; - loadingActivityIndicator.hidesWhenStopped = YES; - [loadingActivityIndicator startAnimating]; -} - -- (void)dealloc -{ - webDetailView.delegate = nil; - - [loadingActivityIndicator removeFromSuperview]; - loadingActivityIndicator = nil; - -} - -- (void)didReceiveMemoryWarning -{ - // Releases the view if it doesn't have a superview. - [super didReceiveMemoryWarning]; - - // Release any cached data, images, etc that aren't in use. -} - -#pragma mark - View lifecycle - -/* -// Implement loadView to create a view hierarchy programmatically, without using a nib. -- (void)loadView -{ -} -*/ - -/* -// Implement viewDidLoad to do additional setup after loading the view, typically from a nib. -- (void)viewDidLoad -{ - [super viewDidLoad]; -} -*/ - -- (void)viewDidUnload -{ - [super viewDidUnload]; - // Release any retained subviews of the main view. - // e.g. self.myOutlet = nil; -} - -- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation -{ - // Return YES for supported orientations - return (interfaceOrientation == UIInterfaceOrientationPortrait); -} - -#pragma mark - -#pragma mark Web navigation - -- (void)goBackInWebView; -{ - [webDetailView goBack]; -} - - -#pragma mark - -#pragma mark UIWebViewDelegate methods - -- (void)webViewDidFinishLoad:(UIWebView *)webView -{ - [loadingActivityIndicator removeFromSuperview]; - loadingActivityIndicator = nil; - - if (![webDetailView canGoBack]) - { - self.navigationItem.rightBarButtonItem.enabled = NO; - } - else - { - self.navigationItem.rightBarButtonItem.enabled = YES; - } -} - -#pragma mark - -#pragma mark Accessors - -@synthesize moleculeDetailWebPageURL; - -@end diff --git a/SLSMoleculeiPadRootViewController.h b/SLSMoleculeiPadRootViewController.h deleted file mode 100644 index e955474..0000000 --- a/SLSMoleculeiPadRootViewController.h +++ /dev/null @@ -1,37 +0,0 @@ -// -// SLSMoleculeiPadRootViewController.h -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 2/20/2010. -// - -#import "SLSMoleculeRootViewController.h" - -@class UIPopoverController; - -@interface SLSMoleculeiPadRootViewController : SLSMoleculeRootViewController -{ - UIImage *unselectedRotationImage, *selectedRotationImage; - UIBarButtonItem *rotationBarButton, *spacerItem, *visualizationBarButton, *colorKeyBarButton; - UIToolbar *mainToolbar; - UIPopoverController *downloadOptionsPopover, *moleculeTablePopover, *colorKeyPopover; - - UIScreen *externalScreen; - - UIWindow *externalWindow; -} - -// Bar response methods -//- (void)showMolecules:(id)sender; -- (void)showVisualizationModes:(id)sender; -- (void)showDownloadOptions:(id)sender; -- (void)showColorKey:(id)sender; - -// External monitor support -- (void)handleConnectionOfMonitor:(NSNotification *)note; -- (void)handleDisconnectionOfMonitor:(NSNotification *)note; -- (void)displayOnExternalOrLocalScreen:(id)sender; - -@end diff --git a/SLSMoleculeiPadRootViewController.m b/SLSMoleculeiPadRootViewController.m deleted file mode 100644 index 08dd087..0000000 --- a/SLSMoleculeiPadRootViewController.m +++ /dev/null @@ -1,340 +0,0 @@ - // -// SLSMoleculeiPadRootViewController.m -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 2/20/2010. -// -// The download toolbar icon in this application is courtesy of Joseph Wain / glyphish.com -// See the GlyphishIconLicense.txt file for more information on these icons - - -#import "SLSMoleculeiPadRootViewController.h" -#import "SLSMoleculeGLViewController.h" -#import "SLSMoleculeDataSourceViewController.h" -#import "SLSMoleculeGLView.h" -#import "SLSAtomColorKeyController.h" - -@implementation SLSMoleculeiPadRootViewController - -// Implement loadView to create a view hierarchy programmatically, without using a nib. -- (void)loadView -{ - CGRect mainScreenFrame = [[UIScreen mainScreen] bounds]; - - UIView *backgroundView = [[UIView alloc] initWithFrame:mainScreenFrame]; - backgroundView.opaque = YES; - backgroundView.backgroundColor = [UIColor blackColor]; - backgroundView.autoresizesSubviews = YES; - self.view = backgroundView; - - SLSMoleculeGLViewController *viewController = [[SLSMoleculeGLViewController alloc] initWithNibName:nil bundle:nil]; - self.glViewController = viewController; - - [self.view addSubview:glViewController.view]; - glViewController.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; - - mainToolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0.0f, 0.0f, glViewController.view.frame.size.width, 44.0f)]; - mainToolbar.autoresizingMask = UIViewAutoresizingFlexibleWidth; - mainToolbar.tintColor = [UIColor blackColor]; - [backgroundView addSubview:mainToolbar]; - - UIImage *screenImage = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"98-palette" ofType:@"png"]]; - colorKeyBarButton = [[UIBarButtonItem alloc] initWithImage:screenImage style:UIBarButtonItemStylePlain target:self action:@selector(showColorKey:)]; - colorKeyBarButton.width = 44.0f; - - UIImage *visualizationImage = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"VisualizationIcon" ofType:@"png"]]; - visualizationBarButton = [[UIBarButtonItem alloc] initWithImage:visualizationImage style:UIBarButtonItemStylePlain target:self action:@selector(showVisualizationModes:)]; - visualizationBarButton.width = 44.0f; - - spacerItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil]; - - unselectedRotationImage = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"RotationIconiPad" ofType:@"png"]]; - rotationBarButton = [[UIBarButtonItem alloc] initWithImage:unselectedRotationImage style:UIBarButtonItemStylePlain target:glViewController action:@selector(startOrStopAutorotation:)]; - rotationBarButton.width = 44.0f; - - [mainToolbar setItems:[NSArray arrayWithObjects:spacerItem, colorKeyBarButton, visualizationBarButton, rotationBarButton, nil] animated:NO]; - - glViewController.view.frame = CGRectMake(mainScreenFrame.origin.x, mainToolbar.bounds.size.height, mainScreenFrame.size.width, mainScreenFrame.size.height - mainToolbar.bounds.size.height); -} - -- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation -{ - // Overriden to allow any orientation. - return YES; -} - -- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration -{ - [glViewController handleStartOfAutorotation]; -} - -- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation -{ - [glViewController handleEndOfAutorotation]; -} - -- (void)didReceiveMemoryWarning -{ - // Releases the view if it doesn't have a superview. - [super didReceiveMemoryWarning]; - - // Release any cached data, images, etc that aren't in use. -} - -- (void)viewDidUnload -{ - [super viewDidUnload]; - // Release any retained subviews of the main view. - // e.g. self.myOutlet = nil; -} - - -#pragma mark - -#pragma mark Bar response methods - -- (void)showVisualizationModes:(id)sender; -{ - if (glViewController.visualizationActionSheet != nil) - { - return; - } - - UIActionSheet *actionSheet = [glViewController actionSheetForVisualizationState]; - [actionSheet showFromBarButtonItem:visualizationBarButton animated:YES]; - glViewController.visualizationActionSheet = actionSheet; - - [moleculeTablePopover dismissPopoverAnimated:YES]; - moleculeTablePopover = nil; - - [downloadOptionsPopover dismissPopoverAnimated:YES]; - downloadOptionsPopover = nil; - - [colorKeyPopover dismissPopoverAnimated:YES]; - colorKeyPopover = nil; -} - -- (void)showDownloadOptions:(id)sender; -{ - if (downloadOptionsPopover != nil) - { - return; - } - - UINavigationController *downloadNavigationController = [[UINavigationController alloc] init]; - downloadNavigationController.navigationBar.barStyle = UIBarStyleBlackOpaque; - - SLSMoleculeDataSourceViewController *dataSourceViewController = [[SLSMoleculeDataSourceViewController alloc] initWithStyle:UITableViewStylePlain]; -// dataSourceViewController.delegate = self; - [downloadNavigationController pushViewController:dataSourceViewController animated:NO]; - - downloadOptionsPopover = [[UIPopoverController alloc] initWithContentViewController:downloadNavigationController]; - [downloadOptionsPopover setDelegate:self]; - [downloadOptionsPopover presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; -// [downloadOptionsPopover release]; - - [glViewController.visualizationActionSheet dismissWithClickedButtonIndex:2 animated:YES]; - glViewController.visualizationActionSheet = nil; - - [moleculeTablePopover dismissPopoverAnimated:YES]; - moleculeTablePopover = nil; - - [colorKeyPopover dismissPopoverAnimated:YES]; - colorKeyPopover = nil; -} - -- (void)showColorKey:(id)sender; -{ - if (colorKeyPopover != nil) - { - return; - } - - UINavigationController *colorKeyNavigationController = [[UINavigationController alloc] init]; - colorKeyNavigationController.navigationBar.barStyle = UIBarStyleBlackOpaque; - - SLSAtomColorKeyController *dataSourceViewController = [[SLSAtomColorKeyController alloc] initWithStyle:UITableViewStylePlain]; - [colorKeyNavigationController pushViewController:dataSourceViewController animated:NO]; - - colorKeyPopover = [[UIPopoverController alloc] initWithContentViewController:colorKeyNavigationController]; - [colorKeyPopover setDelegate:self]; - [colorKeyPopover presentPopoverFromBarButtonItem:sender permittedArrowDirections:UIPopoverArrowDirectionAny animated:YES]; - - [glViewController.visualizationActionSheet dismissWithClickedButtonIndex:2 animated:YES]; - glViewController.visualizationActionSheet = nil; - - [moleculeTablePopover dismissPopoverAnimated:YES]; - moleculeTablePopover = nil; - - [downloadOptionsPopover dismissPopoverAnimated:YES]; - downloadOptionsPopover = nil; -} - -#pragma mark - -#pragma mark Passthroughs for managing molecules - -- (void)selectedMoleculeDidChange:(NSInteger)newMoleculeIndex; -{ - [super selectedMoleculeDidChange:newMoleculeIndex]; - - glViewController.moleculeToDisplay = bufferedMolecule; - - [moleculeTablePopover dismissPopoverAnimated:YES]; - moleculeTablePopover = nil; -} - -#pragma mark - -#pragma mark Manage the switching of rotation state - -- (void)toggleRotationButton:(NSNotification *)note; -{ - if ([[note object] boolValue]) - { - if (selectedRotationImage == nil) - { - selectedRotationImage = [[UIImage alloc] initWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"RotationIconiPadCancel" ofType:@"png"]]; - } - - rotationBarButton.image = selectedRotationImage; - } - else - { - rotationBarButton.image = unselectedRotationImage; - } -} - -#pragma mark - -#pragma mark External monitor support - -- (void)handleConnectionOfMonitor:(NSNotification *)note; -{ - externalScreen = [note object]; - NSMutableArray *items = [[mainToolbar items] mutableCopy]; -// [items insertObject:screenBarButton atIndex:[items indexOfObject:spacerItem] + 1]; - [mainToolbar setItems:items animated:YES]; -} - -- (void)handleDisconnectionOfMonitor:(NSNotification *)note; -{ - NSMutableArray *items = [[mainToolbar items] mutableCopy]; -// [items removeObject:screenBarButton]; - [mainToolbar setItems:items animated:YES]; - - if (externalWindow != nil) - { - [self.view addSubview:glViewController.view]; - [glViewController updateSizeOfGLView:nil]; - externalWindow = nil; - } - externalScreen = nil; -} - -- (void)displayOnExternalOrLocalScreen:(id)sender; -{ - if (externalWindow != nil) - { - // External window exists, need to move back locally - [self.view addSubview:glViewController.view]; - CGRect mainScreenFrame = [[UIScreen mainScreen] bounds]; - glViewController.view.frame = CGRectMake(mainScreenFrame.origin.x, mainToolbar.bounds.size.height, mainScreenFrame.size.width, mainScreenFrame.size.height - mainToolbar.bounds.size.height); - - // Move view back to local window - externalWindow = nil; - } - else - { - // Being displayed locally, move to external window - CGRect externalBounds = [externalScreen bounds]; - externalWindow = [[UIWindow alloc] initWithFrame:externalBounds]; - externalWindow.backgroundColor = [UIColor whiteColor]; - externalWindow.screen = externalScreen; - - -// if (glViewController.is - -// [glViewController.view removeFromSuperview]; -// glViewController.view = nil; -// -// glViewController.view = [[SLSMoleculeGLView alloc] initWithFrame:externalBounds]; - - -// SLSMoleculeGLView *glView = (SLSMoleculeGLView *)glViewController.view; -// [EAGLContext setCurrentContext:glView.context]; -// [opengl destroyFramebuffer]; - - -// glView.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES1]; - [externalWindow addSubview:glViewController.view]; - -// [EAGLContext setCurrentContext:glView.context]; -// [glView createFramebuffer]; -// [glView configureProjection]; -// [glViewController _drawViewByRotatingAroundX:0.0f rotatingAroundY:0.0f scaling:1.0f translationInX:0.0f translationInY:0.0f]; - -// UILabel *helloWorld = [[UILabel alloc] initWithFrame:CGRectMake(200.0f, 400.0f, 400.0f, 60.0f)]; -// helloWorld.text = @"This page intentionally left blank."; -// [externalWindow addSubview:helloWorld]; -// [helloWorld release]; - - glViewController.view.frame = externalBounds; - [externalWindow makeKeyAndVisible]; - } -} - -#pragma mark - -#pragma mark UISplitViewControllerDelegate methods - -- (void)splitViewController:(UISplitViewController*)svc popoverController:(UIPopoverController*)pc willPresentViewController:(UIViewController *)aViewController -{ - [downloadOptionsPopover dismissPopoverAnimated:YES]; - downloadOptionsPopover = nil; - - [glViewController.visualizationActionSheet dismissWithClickedButtonIndex:2 animated:YES]; - glViewController.visualizationActionSheet = nil; - - moleculeTablePopover = pc; -} - -- (void)splitViewController:(UISplitViewController*)svc willHideViewController:(UIViewController *)aViewController withBarButtonItem:(UIBarButtonItem*)barButtonItem forPopoverController:(UIPopoverController*)pc -{ - [(UINavigationController *)aViewController navigationBar].barStyle = UIBarStyleBlackOpaque; -// barButtonItem.title = @"Molecules"; - NSMutableArray *items = [[mainToolbar items] mutableCopy]; - [items insertObject:barButtonItem atIndex:0]; - [mainToolbar setItems:items animated:YES]; -} - -- (void)splitViewController:(UISplitViewController*)svc willShowViewController:(UIViewController *)aViewController invalidatingBarButtonItem:(UIBarButtonItem *)button -{ - [(UINavigationController *)aViewController navigationBar].barStyle = UIBarStyleBlackOpaque; - - NSMutableArray *items = [[mainToolbar items] mutableCopy]; - [items removeObjectAtIndex:0]; - [mainToolbar setItems:items animated:YES]; -} - -#pragma mark - -#pragma mark UIPopoverControllerDelegate methods - -- (void)popoverControllerDidDismissPopover:(UIPopoverController *)popoverController; -{ - if (popoverController == downloadOptionsPopover) - { - downloadOptionsPopover = nil; - } - else if (popoverController == moleculeTablePopover) - { - moleculeTablePopover = nil; - } - else if (popoverController == colorKeyPopover) - { - colorKeyPopover = nil; - } -} - - -#pragma mark - -#pragma mark Accessors - -@end diff --git a/SLSOpenGLES11Renderer.h b/SLSOpenGLES11Renderer.h deleted file mode 100644 index 6d55f49..0000000 --- a/SLSOpenGLES11Renderer.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// SLSOpenGLES11Renderer.h -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 4/12/2011. -// -// This is the old renderer, split out into a separate class for OpenGL ES 1.1 devices - -#import "SLSOpenGLESRenderer.h" - -@interface SLSOpenGLES11Renderer : SLSOpenGLESRenderer -{ -} - -// Molecule 3-D geometry generation -- (void)addNormal:(GLfloat *)newNormal forAtomType:(SLSAtomType)atomType; -- (void)addBondNormal:(GLfloat *)newNormal; - -@end diff --git a/SLSOpenGLES11Renderer.m b/SLSOpenGLES11Renderer.m deleted file mode 100644 index 6c69233..0000000 --- a/SLSOpenGLES11Renderer.m +++ /dev/null @@ -1,585 +0,0 @@ -// -// SLSOpenGLES11Renderer.m -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 4/12/2011. -// - -#import "SLSOpenGLES11Renderer.h" -#import "SLSMolecule.h" - -@implementation SLSOpenGLES11Renderer - -#pragma mark - -#pragma mark Icosahedron tables - -// These are from the OpenGL documentation at www.opengl.org -#define X .525731112119133606 -#define Z .850650808352039932 - -static GLfloat vdata[12][3] = -{ - {-X, 0.0f, Z}, - {0.0f, Z, X}, - {X, 0.0f, Z}, - {-Z, X, 0.0f}, - {0.0f, Z, -X}, - {Z, X, 0.0f}, - {Z, -X, 0.0f}, - {X, 0.0f, -Z}, - {-X, 0.0f, -Z}, - {0.0f, -Z, -X}, - {0.0f, -Z, X}, - {-Z, -X, 0.0f} -}; - -static GLushort tindices[20][3] = -{ - {0,1,2}, - {0,3,1}, - {3,4,1}, - {1,4,5}, - {1,5,2}, - {5,6,2}, - {5,7,6}, - {4,7,5}, - {4,8,7}, - {8,9,7}, - {9,6,7}, - {9,10,6}, - {9,11,10}, - {11,0,10}, - {0,2,10}, - {10,2,6}, - {3,0,11}, - {3,11,8}, - {3,8,4}, - {9,8,11} -}; - -#pragma mark - -#pragma mark Bond edge tables - -static GLfloat bondEdges[4][3] = -{ - {0,1,0}, {0,0,1}, {0,-1,0}, {0,0,-1} -}; - -static GLushort bondIndices[8][3] = -{ - {0,1,2}, {1,3,2}, {2,3,4}, {3,5,4}, {5,7,4}, {4,7,6}, {6,7,0}, {7,1,0} -}; - -#pragma mark - -#pragma mark OpenGL helper functions - -void normalize(GLfloat *v) -{ - GLfloat d = sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]); - v[0] /= d; - v[1] /= d; - v[2] /= d; -} - -#pragma mark - -#pragma mark Initialization and teardown - -- (id)initWithContext:(EAGLContext *)newContext; -{ - if (!(self = [super initWithContext:newContext])) - { - return nil; - } - - return self; -} - -- (void)dealloc -{ - [self freeVertexBuffers]; - -} - - -#pragma mark - -#pragma mark OpenGL drawing support - -- (BOOL)createFramebuffersForLayer:(CAEAGLLayer *)glLayer; -{ - dispatch_async(openGLESContextQueue, ^{ - [EAGLContext setCurrentContext:context]; - - glGenFramebuffersOES(1, &viewFramebuffer); - glGenRenderbuffersOES(1, &viewRenderbuffer); - - glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer); - glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); - - // Need this to make the layer dimensions an even multiple of 32 for performance reasons - // Also, the 4.2 Simulator will not display the - /* CGRect layerBounds = glLayer.bounds; - CGFloat newWidth = (CGFloat)((int)layerBounds.size.width / 32) * 32.0f; - CGFloat newHeight = (CGFloat)((int)layerBounds.size.height / 32) * 32.0f; - glLayer.bounds = CGRectMake(layerBounds.origin.x, layerBounds.origin.y, newWidth, newHeight); - */ - [context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:glLayer]; - glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer); - - glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth); - glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight); - - glGenRenderbuffersOES(1, &viewDepthBuffer); - glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewDepthBuffer); - glRenderbufferStorageOES(GL_RENDERBUFFER_OES, GL_DEPTH_COMPONENT16_OES, backingWidth, backingHeight); - glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, viewDepthBuffer); - - if(glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) - { - return; - } - - glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); - }); - - return YES; -} - -- (void)destroyFramebuffers -{ - dispatch_async(openGLESContextQueue, ^{ - glDeleteFramebuffersOES(1, &viewFramebuffer); - viewFramebuffer = 0; - glDeleteRenderbuffersOES(1, &viewRenderbuffer); - viewRenderbuffer = 0; - - if(viewDepthBuffer) - { - glDeleteRenderbuffersOES(1, &viewDepthBuffer); - viewDepthBuffer = 0; - } - }); -} - -- (void)configureLighting; -{ - const GLfloat lightAmbient[] = {0.2, 0.2, 0.2, 1.0}; - const GLfloat lightDiffuse[] = {1.0, 1.0, 1.0, 1.0}; - const GLfloat matAmbient[] = {1.0, 1.0, 1.0, 1.0}; - const GLfloat matDiffuse[] = {1.0, 1.0, 1.0, 1.0}; - const GLfloat lightPosition[] = {0.466, -0.466, 0, 0}; - const GLfloat lightShininess = 20.0; - - glEnable(GL_LIGHTING); - glEnable(GL_LIGHT0); - glEnable(GL_COLOR_MATERIAL); - glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, matAmbient); - glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, matDiffuse); - glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, lightShininess); - glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmbient); - glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiffuse); - glLightfv(GL_LIGHT0, GL_POSITION, lightPosition); - - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_LEQUAL); - - glShadeModel(GL_SMOOTH); - glDisable(GL_NORMALIZE); - glEnable(GL_RESCALE_NORMAL); - - glEnableClientState (GL_VERTEX_ARRAY); - glEnableClientState (GL_NORMAL_ARRAY); - glDisableClientState (GL_COLOR_ARRAY); - - glDisable(GL_ALPHA_TEST); - glDisable(GL_FOG); - glEnable(GL_CULL_FACE); - glCullFace(GL_FRONT); - - // glEnable(GL_LINE_SMOOTH); -} - -- (void)clearScreen; -{ - dispatch_async(openGLESContextQueue, ^{ - [EAGLContext setCurrentContext:context]; - - glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer); - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); - [context presentRenderbuffer:GL_RENDERBUFFER_OES]; - }); -} - -- (void)startDrawingFrame; -{ - [EAGLContext setCurrentContext:context]; - - glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer); - glViewport(0, 0, backingWidth, backingHeight); - // glScissor(0, 0, backingWidth, backingHeight); -} - -- (void)configureProjection; -{ - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - - // glOrthof(-32768.0f, 32768.0f, -1.5f * 32768.0f, 1.5f * 32768.0f, -10.0f * 32768.0f, 4.0f * 32768.0f); - glOrthof(-32768.0f, 32768.0f, -((float)backingHeight / (float)backingWidth) * 32768.0f, ((float)backingHeight / (float)backingWidth) * 32768.0f, -10.0f * 32768.0f, 4.0f * 32768.0f); -} - -- (void)presentRenderBuffer; -{ - glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer); - [context presentRenderbuffer:GL_RENDERBUFFER_OES]; -} - -#pragma mark - -#pragma mark Actual OpenGL rendering - -- (void)renderFrameForMolecule:(SLSMolecule *)molecule; -{ - if (dispatch_semaphore_wait(frameRenderingSemaphore, DISPATCH_TIME_NOW) != 0) - { - return; - } - - dispatch_async(openGLESContextQueue, ^{ -// CFAbsoluteTime elapsedTime, startTime = CFAbsoluteTimeGetCurrent(); - - isFrameRenderingFinished = NO; - - [self startDrawingFrame]; - - if (isFirstDrawingOfMolecule) - { - [self configureProjection]; - } - - GLfloat currentModelViewMatrix[16] = {0.402560,0.094840,0.910469,0.000000, 0.913984,-0.096835,-0.394028,0.000000, 0.050796,0.990772,-0.125664,0.000000, 0.000000,0.000000,0.000000,1.000000}; - - glMatrixMode(GL_MODELVIEW); - - // Reset rotation system - if (isFirstDrawingOfMolecule) - { - glLoadIdentity(); - glMultMatrixf(currentModelViewMatrix); - [self configureLighting]; - - isFirstDrawingOfMolecule = NO; - } - - // Set the new matrix that has been calculated from the Core Animation transform - [self convert3DTransform:¤tCalculatedMatrix toMatrix:currentModelViewMatrix]; - - glLoadMatrixf(currentModelViewMatrix); - - // Black background, with depth buffer enabled - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - if (molecule.isDoneRendering) - { - [self drawMolecule]; - } - - [self presentRenderBuffer]; - isFrameRenderingFinished = YES; - -// elapsedTime = CFAbsoluteTimeGetCurrent() - startTime; -// NSLog(@"Render time: %.1f ms, Triangles per second: %.0f", elapsedTime * 1000.0, (CGFloat)totalNumberOfTriangles / elapsedTime); - - dispatch_semaphore_signal(frameRenderingSemaphore); - }); -} - -#pragma mark - -#pragma mark Molecule 3-D geometry generation - -- (void)addVertex:(GLfloat *)newVertex forAtomType:(SLSAtomType)atomType; -{ - if (atomVBOs[atomType] == nil) - { - atomVBOs[atomType] = [[NSMutableData alloc] init]; - } - - GLshort shortVertex[4]; - shortVertex[0] = (GLshort)MAX(MIN(round(newVertex[0] * 32767.0f), 32767), -32767); - shortVertex[1] = (GLshort)MAX(MIN(round(newVertex[1] * 32767.0f), 32767), -32767); - shortVertex[2] = (GLshort)MAX(MIN(round(newVertex[2] * 32767.0f), 32767), -32767); - shortVertex[3] = 0; - - // if ( ((newVertex[0] < -1.0f) || (newVertex[0] > 1.0f)) || ((newVertex[1] < -1.0f) || (newVertex[1] > 1.0f)) || ((newVertex[2] < -1.0f) || (newVertex[2] > 1.0f)) ) - // { - // NSLog(@"Vertex outside range: %f, %f, %f", newVertex[0], newVertex[1], newVertex[2]); - // } - - [atomVBOs[atomType] appendBytes:shortVertex length:(sizeof(GLshort) * 4)]; - - // [m_vertexArray appendBytes:newVertex length:(sizeof(GLfloat) * 3)]; - numberOfAtomVertices[atomType]++; - totalNumberOfVertices++; -} - -- (void)addBondVertex:(GLfloat *)newVertex; -{ - if (bondVBOs[currentBondVBO] == nil) - { - bondVBOs[currentBondVBO] = [[NSMutableData alloc] init]; - } - - GLshort shortVertex[4]; - shortVertex[0] = (GLshort)MAX(MIN(round(newVertex[0] * 32767.0f), 32767), -32767); - shortVertex[1] = (GLshort)MAX(MIN(round(newVertex[1] * 32767.0f), 32767), -32767); - shortVertex[2] = (GLshort)MAX(MIN(round(newVertex[2] * 32767.0f), 32767), -32767); - shortVertex[3] = 0; - - // if ( ((newVertex[0] < -1.0f) || (newVertex[0] > 1.0f)) || ((newVertex[1] < -1.0f) || (newVertex[1] > 1.0f)) || ((newVertex[2] < -1.0f) || (newVertex[2] > 1.0f)) ) - // { - // NSLog(@"Vertex outside range: %f, %f, %f", newVertex[0], newVertex[1], newVertex[2]); - // } - - [bondVBOs[currentBondVBO] appendBytes:shortVertex length:(sizeof(GLshort) * 4)]; - - // [m_vertexArray appendBytes:newVertex length:(sizeof(GLfloat) * 3)]; - numberOfBondVertices[currentBondVBO]++; - totalNumberOfVertices++; -} - -- (void)addNormal:(GLfloat *)newNormal forAtomType:(SLSAtomType)atomType; -{ - if (atomVBOs[atomType] == nil) - { - atomVBOs[atomType] = [[NSMutableData alloc] init]; - } - - GLshort shortNormals[4]; - shortNormals[0] = (GLshort)round(newNormal[0] * 32767.0f); - shortNormals[1] = (GLshort)round(newNormal[1] * 32767.0f); - shortNormals[2] = (GLshort)round(newNormal[2] * 32767.0f); - shortNormals[3] = 0; - - [atomVBOs[atomType] appendBytes:shortNormals length:(sizeof(GLshort) * 4)]; - // [m_vertexArray appendBytes:newNormal length:(sizeof(GLfloat) * 3)]; -} - -- (void)addBondNormal:(GLfloat *)newNormal; -{ - if (bondVBOs[currentBondVBO] == nil) - { - bondVBOs[currentBondVBO] = [[NSMutableData alloc] init]; - } - - GLshort shortNormals[4]; - shortNormals[0] = (GLshort)round(newNormal[0] * 32767.0f); - shortNormals[1] = (GLshort)round(newNormal[1] * 32767.0f); - shortNormals[2] = (GLshort)round(newNormal[2] * 32767.0f); - shortNormals[3] = 0; - - [bondVBOs[currentBondVBO] appendBytes:shortNormals length:(sizeof(GLshort) * 4)]; -} - -- (void)addAtomToVertexBuffers:(SLSAtomType)atomType atPoint:(SLS3DPoint)newPoint; -{ - float radiusScaleFactor = overallMoleculeScaleFactor * atomRadiusScaleFactor; - GLfloat newVertex[3]; - GLfloat atomRadius = 0.4f; - - GLushort baseToAddToIndices = numberOfAtomVertices[atomType]; - - SLSAtomProperties currentAtomProperties = atomProperties[atomType]; - - atomRadius = currentAtomProperties.atomRadius; - atomRadius *= radiusScaleFactor; - - for (int currentCounter = 0; currentCounter < 12; currentCounter++) - { - // Adjust radius and shift to match center - newVertex[0] = (vdata[currentCounter][0] * atomRadius) + newPoint.x; - newVertex[1] = (vdata[currentCounter][1] * atomRadius) + newPoint.y; - newVertex[2] = (vdata[currentCounter][2] * atomRadius) + newPoint.z; - - // Add vertex from table - [self addVertex:newVertex forAtomType:atomType]; - - // Just use original icosahedron for normals - newVertex[0] = vdata[currentCounter][0]; - newVertex[1] = vdata[currentCounter][1]; - newVertex[2] = vdata[currentCounter][2]; - - // Add sphere normal - [self addNormal:newVertex forAtomType:atomType]; - } - - GLushort indexHolder; - for (int currentCounter = 0; currentCounter < 20; currentCounter++) - { - totalNumberOfTriangles++; - for (unsigned int internalCounter = 0; internalCounter < 3; internalCounter++) - { - indexHolder = baseToAddToIndices + tindices[currentCounter][internalCounter]; - [self addIndex:&indexHolder forAtomType:atomType]; - } - } -} - -- (void)addBondToVertexBuffersWithStartPoint:(SLS3DPoint)startPoint endPoint:(SLS3DPoint)endPoint bondColor:(GLubyte *)bondColor bondType:(SLSBondType)bondType; -{ - float radiusScaleFactor = overallMoleculeScaleFactor * bondRadiusScaleFactor; - - if (currentBondVBO >= MAX_BOND_VBOS) - { - return; - } - // SLS3DPoint startPoint, endPoint; - // if ( (startValue == nil) || (endValue == nil) ) - // return; - // [startValue getValue:&startPoint]; - // [endValue getValue:&endPoint]; - - GLfloat bondRadius = radiusScaleFactor; - - GLfloat xDifference = endPoint.x - startPoint.x; - GLfloat yDifference = endPoint.y - startPoint.y; - GLfloat zDifference = endPoint.z - startPoint.z; - GLfloat xyHypotenuse = sqrt(xDifference * xDifference + yDifference * yDifference); - GLfloat xzHypotenuse = sqrt(xDifference * xDifference + zDifference * zDifference); - - GLushort baseToAddToIndices = numberOfBondVertices[currentBondVBO]; - if (baseToAddToIndices > 65500) - { - baseToAddToIndices = 0; - numberOfBondVertices[currentBondVBO] = 0; - numberOfBondIndices[currentBondVBO] = 0; - - currentBondVBO++; - if (currentBondVBO >= MAX_BOND_VBOS) - { - return; - } - } - - // Do first edge vertices, colors, and normals - for (unsigned int edgeCounter = 0; edgeCounter < 4; edgeCounter++) - { - SLS3DPoint calculatedNormal; - GLfloat edgeNormal[3], edgeVertex[3]; - - if (xyHypotenuse == 0) - { - calculatedNormal.x = bondEdges[edgeCounter][0]; - calculatedNormal.y = bondEdges[edgeCounter][1]; - } - else - { - calculatedNormal.x = bondEdges[edgeCounter][0] * xDifference / xyHypotenuse - bondEdges[edgeCounter][1] * yDifference / xyHypotenuse; - calculatedNormal.y = bondEdges[edgeCounter][0] * yDifference / xyHypotenuse + bondEdges[edgeCounter][1] * xDifference / xyHypotenuse; - } - - if (xzHypotenuse == 0) - { - calculatedNormal.z = bondEdges[edgeCounter][2]; - } - else - { - calculatedNormal.z = calculatedNormal.x * zDifference / xzHypotenuse + bondEdges[edgeCounter][2] * xDifference / xzHypotenuse; - calculatedNormal.x = calculatedNormal.x * xDifference / xzHypotenuse - bondEdges[edgeCounter][2] * zDifference / xzHypotenuse; - } - - edgeVertex[0] = (calculatedNormal.x * bondRadius) + startPoint.x; - edgeVertex[1] = (calculatedNormal.y * bondRadius) + startPoint.y; - edgeVertex[2] = (calculatedNormal.z * bondRadius) + startPoint.z; - [self addBondVertex:edgeVertex]; - - edgeNormal[0] = calculatedNormal.x; - edgeNormal[1] = calculatedNormal.y; - edgeNormal[2] = calculatedNormal.z; - - [self addBondNormal:edgeNormal]; - - edgeVertex[0] = (calculatedNormal.x * bondRadius) + endPoint.x; - edgeVertex[1] = (calculatedNormal.y * bondRadius) + endPoint.y; - edgeVertex[2] = (calculatedNormal.z * bondRadius) + endPoint.z; - [self addBondVertex:edgeVertex]; - [self addBondNormal:edgeNormal]; - } - - for (unsigned int currentCounter = 0; currentCounter < 8; currentCounter++) - { - totalNumberOfTriangles++; - - for (unsigned int internalCounter = 0; internalCounter < 3; internalCounter++) - { - GLushort indexHolder = baseToAddToIndices + bondIndices[currentCounter][internalCounter]; - [self addBondIndex:&indexHolder]; - } - } -} - -#pragma mark - -#pragma mark OpenGL drawing routines - -- (void)bindVertexBuffersForMolecule; -{ - [super bindVertexBuffersForMolecule]; - - isSceneReady = YES; -} - -- (void)drawMolecule; -{ - // Draw all atoms first, binned based on their type - for (unsigned int currentAtomType = 0; currentAtomType < NUM_ATOMTYPES; currentAtomType++) - { - if (atomIndexBufferHandle[currentAtomType] != 0) - { - glColor4f((GLfloat)atomProperties[currentAtomType].redComponent / 255.0f , (GLfloat)atomProperties[currentAtomType].greenComponent / 255.0f, (GLfloat)atomProperties[currentAtomType].blueComponent / 255.0f, 1.0); - - // Bind the buffers - glBindBuffer(GL_ARRAY_BUFFER, atomVertexBufferHandles[currentAtomType]); - glVertexPointer(3, GL_SHORT, 16, (char *)NULL + 0); - glNormalPointer(GL_SHORT, 16, (char *)NULL + 8); - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, atomIndexBufferHandle[currentAtomType]); - - // Do the actual drawing to the screen - glDrawElements(GL_TRIANGLES, numberOfIndicesInBuffer[currentAtomType], GL_UNSIGNED_SHORT, NULL); - - // Unbind the buffers - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glBindBuffer(GL_ARRAY_BUFFER, 0); - } - } - - for (unsigned int currentBondVBOIndex = 0; currentBondVBOIndex < MAX_BOND_VBOS; currentBondVBOIndex++) - { - // Draw bonds next - if (bondVertexBufferHandle[currentBondVBOIndex] != 0) - { - GLubyte bondColor[4] = {200,200,200,255}; // Bonds are grey by default - - glColor4f((GLfloat)bondColor[0] / 255.0f , (GLfloat)bondColor[1] / 255.0f, (GLfloat)bondColor[2] / 255.0f, 1.0); - - // Bind the buffers - glBindBuffer(GL_ARRAY_BUFFER, bondVertexBufferHandle[currentBondVBOIndex]); - glVertexPointer(3, GL_SHORT, 16, (char *)NULL + 0); - glNormalPointer(GL_SHORT, 16, (char *)NULL + 8); - - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bondIndexBufferHandle[currentBondVBOIndex]); - - // Do the actual drawing to the screen - glDrawElements(GL_TRIANGLES, numberOfBondIndicesInBuffer[currentBondVBOIndex], GL_UNSIGNED_SHORT, NULL); - - // Unbind the buffers - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glBindBuffer(GL_ARRAY_BUFFER, 0); - } - } -} - -@end diff --git a/SLSOpenGLES20Renderer.h b/SLSOpenGLES20Renderer.h deleted file mode 100644 index d234b69..0000000 --- a/SLSOpenGLES20Renderer.h +++ /dev/null @@ -1,115 +0,0 @@ -// -// SLSOpenGLES20Renderer.h -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 4/12/2011. -// - -#import "SLSOpenGLESRenderer.h" - -#define ENABLETEXTUREDISPLAYDEBUGGING 1 - -@class GLProgram; - -@interface SLSOpenGLES20Renderer : SLSOpenGLESRenderer -{ - GLProgram *sphereDepthWriteProgram; - GLint sphereDepthWritePositionAttribute, sphereDepthWriteImpostorSpaceAttribute, sphereDepthWriteModelViewMatrix; - GLint sphereDepthWriteRadius, sphereDepthWriteOrthographicMatrix, sphereDepthWriteTranslation; - - GLProgram *sphereDepthProgram; - GLint sphereDepthPositionAttribute, sphereDepthImpostorSpaceAttribute, sphereDepthModelViewMatrix; - GLint sphereDepthRadius, sphereDepthOrthographicMatrix, sphereDepthTranslation, sphereDepthMapTexture; - GLint sphereDepthPrecalculatedDepthTexture; - - GLProgram *cylinderDepthProgram; - GLint cylinderDepthPositionAttribute, cylinderDepthDirectionAttribute, cylinderDepthImpostorSpaceAttribute, cylinderDepthModelViewMatrix, cylinderDepthTranslation; - GLint cylinderDepthRadius, cylinderDepthOrthographicMatrix; - - GLuint depthPassTexture; - GLuint depthPassFramebuffer, depthPassDepthBuffer; - - GLProgram *sphereRaytracingProgram; - GLint sphereRaytracingPositionAttribute, sphereRaytracingImpostorSpaceAttribute, sphereRaytracingAOOffsetAttribute, sphereRaytracingModelViewMatrix; - GLint sphereRaytracingLightPosition, sphereRaytracingRadius, sphereRaytracingColor, sphereRaytracingOrthographicMatrix, sphereRaytracingInverseModelViewMatrix, sphereRaytracingTranslation; - GLint sphereRaytracingDepthTexture, sphereRaytracingPrecalculatedDepthTexture, sphereRaytracingAOTexture, sphereRaytracingTexturePatchWidth, sphereRaytracingPrecalculatedAOLookupTexture, sphereRaytracingDepthMapTexture; - - GLProgram *cylinderRaytracingProgram; - GLint cylinderRaytracingPositionAttribute, cylinderRaytracingDirectionAttribute, cylinderRaytracingImpostorSpaceAttribute, cylinderRaytracingAOOffsetAttribute, cylinderRaytracingModelViewMatrix, cylinderRaytracingTranslation; - GLint cylinderRaytracingLightPosition, cylinderRaytracingRadius, cylinderRaytracingColor, cylinderRaytracingOrthographicMatrix; - GLint cylinderRaytracingDepthTexture, cylinderRaytracingInverseModelViewMatrix, cylinderRaytracingAOTexture, cylinderRaytracingTexturePatchWidth; - - GLProgram *sphereAmbientOcclusionProgram; - GLint sphereAmbientOcclusionPositionAttribute, sphereAmbientOcclusionImpostorSpaceAttribute, sphereAmbientOcclusionAOOffsetAttribute, sphereAmbientOcclusionModelViewMatrix; - GLint sphereAmbientOcclusionRadius, sphereAmbientOcclusionOrthographicMatrix, sphereAmbientOcclusionInverseModelViewMatrix, sphereAmbientOcclusionTexturePatchWidth, sphereAmbientOcclusionIntensityFactor; - GLint sphereAmbientOcclusionDepthTexture, sphereAmbientOcclusionPrecalculatedDepthTexture; - - GLProgram *cylinderAmbientOcclusionProgram; - GLint cylinderAmbientOcclusionPositionAttribute, cylinderAmbientOcclusionDirectionAttribute, cylinderAmbientOcclusionImpostorSpaceAttribute, cylinderAmbientOcclusionAOOffsetAttribute, cylinderAmbientOcclusionModelViewMatrix; - GLint cylinderAmbientOcclusionRadius, cylinderAmbientOcclusionOrthographicMatrix, cylinderAmbientOcclusionInverseModelViewMatrix, cylinderAmbientOcclusionTexturePatchWidth, cylinderAmbientOcclusionIntensityFactor; - GLint cylinderAmbientOcclusionDepthTexture; - - GLProgram *sphereAOLookupPrecalculationProgram; - GLint sphereAOLookupImpostorSpaceAttribute, sphereAOLookupInverseModelViewMatrix; - GLint sphereAOLookupPrecalculatedDepthTexture; - - GLuint sphereAOLookupTexture; - GLuint sphereAOLookupFramebuffer; - -#ifdef ENABLETEXTUREDISPLAYDEBUGGING - GLProgram *passthroughProgram; - GLint passthroughPositionAttribute, passthroughTextureCoordinateAttribute; - GLint passthroughTexture; -#endif - - GLuint ambientOcclusionTexture; - GLuint ambientOcclusionFramebuffer; - - GLuint sphereDepthMappingTexture; - - GLfloat previousAmbientOcclusionOffset[2]; - GLfloat lightDirection[3]; - GLfloat orthographicMatrix[9]; - GLfloat accumulatedModelTranslation[3]; - - CGSize currentViewportSize; - - unsigned int widthOfAtomAOTexturePatch; - GLfloat normalizedAOTexturePatchWidth; - - unsigned int ambientOcclusionTextureWidth, ambientOcclusionLookupTextureWidth, sphereDepthTextureWidth; - - BOOL shouldDrawBonds; -} - -// OpenGL drawing support -- (void)initializeDepthShaders; -- (void)initializeAmbientOcclusionShaders; -- (void)initializeRaytracingShaders; -- (void)loadOrthoMatrix:(GLfloat *)matrix left:(GLfloat)left right:(GLfloat)right bottom:(GLfloat)bottom top:(GLfloat)top near:(GLfloat)near far:(GLfloat)far; -- (BOOL)createFramebuffer:(GLuint *)framebufferPointer size:(CGSize)bufferSize renderBuffer:(GLuint *)renderbufferPointer depthBuffer:(GLuint *)depthbufferPointer texture:(GLuint *)backingTexturePointer layer:(CAEAGLLayer *)layer; -- (void)switchToDisplayFramebuffer; -- (void)switchToDepthPassFramebuffer; -- (void)switchToAmbientOcclusionFramebuffer; -- (void)switchToAOLookupFramebuffer; -- (void)generateSphereDepthMapTexture; - -// Molecule 3-D geometry generation -- (void)addTextureCoordinate:(GLfloat *)newTextureCoordinate forAtomType:(SLSAtomType)atomType; -- (void)addAmbientOcclusionTextureOffset:(GLfloat *)ambientOcclusionOffset forAtomType:(SLSAtomType)atomType; -- (void)addBondDirection:(GLfloat *)newDirection; -- (void)addBondTextureCoordinate:(GLfloat *)newTextureCoordinate; -- (void)addBondAmbientOcclusionTextureOffset:(GLfloat *)ambientOcclusionOffset; - -// OpenGL drawing routines -- (void)renderDepthTextureForModelViewMatrix:(GLfloat *)depthModelViewMatrix translation:(GLfloat *)modelTranslation scale:(GLfloat)scaleFactor; -- (void)writeDepthValuesForOpaqueAreasForModelViewMatrix:(GLfloat *)depthModelViewMatrix translation:(GLfloat *)modelTranslation scale:(GLfloat)scaleFactor; -- (void)renderRaytracedSceneForModelViewMatrix:(GLfloat *)raytracingModelViewMatrix inverseMatrix:(GLfloat *)inverseMatrix translation:(GLfloat *)modelTranslation scale:(GLfloat)scaleFactor; -- (void)renderAmbientOcclusionTextureForModelViewMatrix:(GLfloat *)ambientOcclusionModelViewMatrix inverseMatrix:(GLfloat *)inverseMatrix fractionOfTotal:(GLfloat)fractionOfTotal; -- (void)prepareAmbientOcclusionMap; -- (void)precalculateAOLookupTextureForInverseMatrix:(GLfloat *)inverseMatrix; -- (void)displayTextureToScreen:(GLuint)textureToDisplay; - -@end diff --git a/SLSOpenGLES20Renderer.m b/SLSOpenGLES20Renderer.m deleted file mode 100644 index bee4b43..0000000 --- a/SLSOpenGLES20Renderer.m +++ /dev/null @@ -1,1913 +0,0 @@ -// -// SLSOpenGLES20Renderer.m -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 4/12/2011. -// - -//#define USEWHITEBACKGROUND - -#import "SLSOpenGLES20Renderer.h" -#import "GLProgram.h" - -@implementation SLSOpenGLES20Renderer - -#pragma mark - -#pragma mark Initialization and teardown - -- (id)initWithContext:(EAGLContext *)newContext; -{ - if (!(self = [super initWithContext:newContext])) - { - return nil; - } - - // 0.312757, 0.248372, 0.916785 - // 0.0, -0.7071, 0.7071 - - [EAGLContext setCurrentContext:context]; - GLint maxTextureSize; - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); - - // Use higher-resolution textures on the A5 and higher GPUs, because they can support it - if (maxTextureSize > 2048) - { -// ambientOcclusionTextureWidth = 2048; -// ambientOcclusionLookupTextureWidth = 512; -// sphereDepthTextureWidth = 512; - ambientOcclusionTextureWidth = 1024; - ambientOcclusionLookupTextureWidth = 128; - sphereDepthTextureWidth = 1024; - } - else - { - ambientOcclusionTextureWidth = 512; - ambientOcclusionLookupTextureWidth = 128; - sphereDepthTextureWidth = 256; - } - - currentViewportSize = CGSizeZero; - - lightDirection[0] = 0.312757; - lightDirection[1] = 0.248372; - lightDirection[2] = 0.916785; - - [self initializeDepthShaders]; - [self initializeAmbientOcclusionShaders]; - [self initializeRaytracingShaders]; - - return self; -} - -- (void)dealloc -{ - [self freeVertexBuffers]; - - if (ambientOcclusionFramebuffer) - { - glDeleteFramebuffers(1, &ambientOcclusionFramebuffer); - ambientOcclusionFramebuffer = 0; - } - - if (ambientOcclusionTexture) - { - glDeleteTextures(1, &ambientOcclusionTexture); - ambientOcclusionTexture = 0; - } - - if (sphereAOLookupFramebuffer) - { - glDeleteFramebuffers(1, &sphereAOLookupFramebuffer); - sphereAOLookupFramebuffer = 0; - } - - if (sphereAOLookupTexture) - { - glDeleteTextures(1, &sphereAOLookupTexture); - sphereAOLookupTexture = 0; - } - -} - -#pragma mark - -#pragma mark Model manipulation - -- (void)rotateModelFromScreenDisplacementInX:(float)xRotation inY:(float)yRotation; -{ - // Perform incremental rotation based on current angles in X and Y - GLfloat totalRotation = sqrt(xRotation*xRotation + yRotation*yRotation); - - CATransform3D temporaryMatrix = CATransform3DRotate(currentCalculatedMatrix, totalRotation * M_PI / 180.0, - ((-xRotation/totalRotation) * currentCalculatedMatrix.m12 + (-yRotation/totalRotation) * currentCalculatedMatrix.m11), - ((-xRotation/totalRotation) * currentCalculatedMatrix.m22 + (-yRotation/totalRotation) * currentCalculatedMatrix.m21), - ((-xRotation/totalRotation) * currentCalculatedMatrix.m32 + (-yRotation/totalRotation) * currentCalculatedMatrix.m31)); - - if ((temporaryMatrix.m11 >= -100.0) && (temporaryMatrix.m11 <= 100.0)) - { -// currentCalculatedMatrix = CATransform3DMakeRotation(M_PI, 0.0, 0.0, 1.0); - - currentCalculatedMatrix = temporaryMatrix; - } -} - -- (void)translateModelByScreenDisplacementInX:(float)xTranslation inY:(float)yTranslation; -{ - // Translate the model by the accumulated amount - float currentScaleFactor = sqrt(pow(currentCalculatedMatrix.m11, 2.0f) + pow(currentCalculatedMatrix.m12, 2.0f) + pow(currentCalculatedMatrix.m13, 2.0f)); - - xTranslation = xTranslation * [[UIScreen mainScreen] scale] / (currentScaleFactor * currentScaleFactor * backingWidth * 0.5); - yTranslation = yTranslation * [[UIScreen mainScreen] scale] / (currentScaleFactor * currentScaleFactor * backingWidth * 0.5); - - // Use the (0,4,8) components to figure the eye's X axis in the model coordinate system, translate along that - // Use the (1,5,9) components to figure the eye's Y axis in the model coordinate system, translate along that - - accumulatedModelTranslation[0] += xTranslation * currentCalculatedMatrix.m11 + yTranslation * currentCalculatedMatrix.m12; - accumulatedModelTranslation[1] += xTranslation * currentCalculatedMatrix.m21 + yTranslation * currentCalculatedMatrix.m22; - accumulatedModelTranslation[2] += xTranslation * currentCalculatedMatrix.m31 + yTranslation * currentCalculatedMatrix.m32; -} - -- (void)resetModelViewMatrix; -{ - [super resetModelViewMatrix]; - - accumulatedModelTranslation[0] = 0.0; - accumulatedModelTranslation[1] = 0.0; - accumulatedModelTranslation[2] = 0.0; -} - -#pragma mark - -#pragma mark OpenGL drawing support - -- (void)loadOrthoMatrix:(GLfloat *)matrix left:(GLfloat)left right:(GLfloat)right bottom:(GLfloat)bottom top:(GLfloat)top near:(GLfloat)near far:(GLfloat)far; -{ - GLfloat r_l = right - left; - GLfloat t_b = top - bottom; - GLfloat f_n = far - near; - - matrix[0] = 2.0f / r_l; - matrix[1] = 0.0f; - matrix[2] = 0.0f; - - matrix[3] = 0.0f; - matrix[4] = 2.0f / t_b; - matrix[5] = 0.0f; - - matrix[6] = 0.0f; - matrix[7] = 0.0f; - matrix[8] = 2.0f / f_n; - - [sphereDepthProgram use]; - glUniformMatrix3fv(sphereDepthOrthographicMatrix, 1, 0, orthographicMatrix); - - [sphereDepthWriteProgram use]; - glUniformMatrix3fv(sphereDepthWriteOrthographicMatrix, 1, 0, orthographicMatrix); - - [cylinderDepthProgram use]; - glUniformMatrix3fv(cylinderDepthOrthographicMatrix, 1, 0, orthographicMatrix); - - [sphereRaytracingProgram use]; - glUniformMatrix3fv(sphereRaytracingOrthographicMatrix, 1, 0, orthographicMatrix); - - [cylinderRaytracingProgram use]; - glUniformMatrix3fv(cylinderRaytracingOrthographicMatrix, 1, 0, orthographicMatrix); - - [sphereAmbientOcclusionProgram use]; - glUniformMatrix3fv(sphereAmbientOcclusionOrthographicMatrix, 1, 0, orthographicMatrix); - - [cylinderAmbientOcclusionProgram use]; - glUniformMatrix3fv(cylinderAmbientOcclusionOrthographicMatrix, 1, 0, orthographicMatrix); - -} - -- (BOOL)createFramebuffersForLayer:(CAEAGLLayer *)glLayer; -{ - dispatch_async(openGLESContextQueue, ^{ - [EAGLContext setCurrentContext:context]; - - // Need this to make the layer dimensions an even multiple of 32 for performance reasons - // Also, the 4.2 Simulator will not display the frame otherwise - /* CGRect layerBounds = glLayer.bounds; - CGFloat newWidth = (CGFloat)((int)layerBounds.size.width / 32) * 32.0f; - CGFloat newHeight = (CGFloat)((int)layerBounds.size.height / 32) * 32.0f; - - NSLog(@"Bounds before: %@", NSStringFromCGRect(glLayer.bounds)); - - glLayer.bounds = CGRectMake(layerBounds.origin.x, layerBounds.origin.y, newWidth, newHeight); - - NSLog(@"Bounds after: %@", NSStringFromCGRect(glLayer.bounds)); - */ - glEnable(GL_TEXTURE_2D); - - [self createFramebuffer:&viewFramebuffer size:CGSizeZero renderBuffer:&viewRenderbuffer depthBuffer:&viewDepthBuffer texture:NULL layer:glLayer]; - // [self createFramebuffer:&depthPassFramebuffer size:CGSizeMake(backingWidth, backingHeight) renderBuffer:&depthPassRenderbuffer depthBuffer:&depthPassDepthBuffer texture:&depthPassTexture layer:glLayer]; - [self createFramebuffer:&depthPassFramebuffer size:CGSizeMake(backingWidth, backingHeight) renderBuffer:NULL depthBuffer:&depthPassDepthBuffer texture:&depthPassTexture layer:glLayer]; - - if (!ambientOcclusionFramebuffer) - { - [self createFramebuffer:&ambientOcclusionFramebuffer size:CGSizeMake(ambientOcclusionTextureWidth, ambientOcclusionTextureWidth) renderBuffer:NULL depthBuffer:NULL texture:&ambientOcclusionTexture layer:glLayer]; - } - - if (!sphereAOLookupFramebuffer) - { - [self createFramebuffer:&sphereAOLookupFramebuffer size:CGSizeMake(ambientOcclusionLookupTextureWidth, ambientOcclusionLookupTextureWidth) renderBuffer:NULL depthBuffer:NULL texture:&sphereAOLookupTexture layer:glLayer]; - } - - [self switchToDisplayFramebuffer]; - glViewport(0, 0, backingWidth, backingHeight); - - currentViewportSize = CGSizeMake(backingWidth, backingHeight); - - // [self loadOrthoMatrix:orthographicMatrix left:-1.0 right:1.0 bottom:(-1.0 * (GLfloat)backingHeight / (GLfloat)backingWidth) top:(1.0 * (GLfloat)backingHeight / (GLfloat)backingWidth) near:-3.0 far:3.0]; - // [self loadOrthoMatrix:orthographicMatrix left:-1.0 right:1.0 bottom:(-1.0 * (GLfloat)backingHeight / (GLfloat)backingWidth) top:(1.0 * (GLfloat)backingHeight / (GLfloat)backingWidth) near:-2.0 far:2.0]; - // [self loadOrthoMatrix:orthographicMatrix left:-1.0 right:1.0 bottom:(-1.0 * (GLfloat)backingHeight / (GLfloat)backingWidth) top:(1.0 * (GLfloat)backingHeight / (GLfloat)backingWidth) near:-0.5 far:0.5]; - [self loadOrthoMatrix:orthographicMatrix left:-1.0 right:1.0 bottom:(-1.0 * (GLfloat)backingHeight / (GLfloat)backingWidth) top:(1.0 * (GLfloat)backingHeight / (GLfloat)backingWidth) near:-1.0 far:1.0]; - - // 0 - Depth pass texture - // 1 - Ambient occlusion texture - // 2 - AO lookup texture - // 3 - Sphere depth precalculation texture - - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, depthPassTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); - - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, ambientOcclusionTexture); - - glActiveTexture(GL_TEXTURE2); - glBindTexture(GL_TEXTURE_2D, sphereAOLookupTexture); - - glActiveTexture(GL_TEXTURE3); - glBindTexture(GL_TEXTURE_2D, sphereDepthMappingTexture); - }); - - return YES; -} - -- (BOOL)createFramebuffer:(GLuint *)framebufferPointer size:(CGSize)bufferSize renderBuffer:(GLuint *)renderbufferPointer depthBuffer:(GLuint *)depthbufferPointer texture:(GLuint *)backingTexturePointer layer:(CAEAGLLayer *)layer; -{ - glGenFramebuffers(1, framebufferPointer); - glBindFramebuffer(GL_FRAMEBUFFER, *framebufferPointer); - - if (renderbufferPointer != NULL) - { - glGenRenderbuffers(1, renderbufferPointer); - glBindRenderbuffer(GL_RENDERBUFFER, *renderbufferPointer); - - if (backingTexturePointer == NULL) - { - [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:layer]; - glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth); - glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight); - bufferSize = CGSizeMake(backingWidth, backingHeight); - } - else - { - glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8_OES, bufferSize.width, bufferSize.height); - } - - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, *renderbufferPointer); - } - - if (depthbufferPointer != NULL) - { - glGenRenderbuffers(1, depthbufferPointer); - glBindRenderbuffer(GL_RENDERBUFFER, *depthbufferPointer); - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, bufferSize.width, bufferSize.height); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, *depthbufferPointer); - } - - if (backingTexturePointer != NULL) - { - if ( (ambientOcclusionTexture == 0) || (*backingTexturePointer != ambientOcclusionTexture)) - { - if (*backingTexturePointer != 0) - { - glDeleteTextures(1, backingTexturePointer); - } - - glGenTextures(1, backingTexturePointer); - - glBindTexture(GL_TEXTURE_2D, *backingTexturePointer); - if (*backingTexturePointer == ambientOcclusionTexture) - { -// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); -// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE); -// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); - - - - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bufferSize.width, bufferSize.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); -// glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, bufferSize.width, bufferSize.height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, 0); - } - else if (*backingTexturePointer == sphereAOLookupTexture) - { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); -// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); -// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE); - - - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bufferSize.width, bufferSize.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - } - else - { - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); -// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); -// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE); - - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bufferSize.width, bufferSize.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - } - } - else - { - glBindTexture(GL_TEXTURE_2D, *backingTexturePointer); - } - - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, *backingTexturePointer, 0); - glBindTexture(GL_TEXTURE_2D, 0); - } - - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (status != GL_FRAMEBUFFER_COMPLETE) - { - NSLog(@"Incomplete FBO: %d", status); - assert(false); - } - - return YES; -} - -- (void)initializeDepthShaders; -{ - if (sphereDepthProgram != nil) - { - return; - } - - [EAGLContext setCurrentContext:context]; - - sphereDepthProgram = [[GLProgram alloc] initWithVertexShaderFilename:@"SphereDepth" fragmentShaderFilename:@"SphereDepth"]; - [sphereDepthProgram addAttribute:@"position"]; - [sphereDepthProgram addAttribute:@"inputImpostorSpaceCoordinate"]; - if (![sphereDepthProgram link]) - { - NSLog(@"Raytracing shader link failed"); - NSString *progLog = [sphereDepthProgram programLog]; - NSLog(@"Program Log: %@", progLog); - NSString *fragLog = [sphereDepthProgram fragmentShaderLog]; - NSLog(@"Frag Log: %@", fragLog); - NSString *vertLog = [sphereDepthProgram vertexShaderLog]; - NSLog(@"Vert Log: %@", vertLog); - sphereDepthProgram = nil; - } - - sphereDepthPositionAttribute = [sphereDepthProgram attributeIndex:@"position"]; - sphereDepthImpostorSpaceAttribute = [sphereDepthProgram attributeIndex:@"inputImpostorSpaceCoordinate"]; - sphereDepthModelViewMatrix = [sphereDepthProgram uniformIndex:@"modelViewProjMatrix"]; - sphereDepthRadius = [sphereDepthProgram uniformIndex:@"sphereRadius"]; - sphereDepthOrthographicMatrix = [sphereDepthProgram uniformIndex:@"orthographicMatrix"]; - sphereDepthPrecalculatedDepthTexture = [sphereDepthProgram uniformIndex:@"precalculatedSphereDepthTexture"]; - sphereDepthTranslation = [sphereDepthProgram uniformIndex:@"translation"]; - sphereDepthMapTexture = [sphereDepthProgram uniformIndex:@"sphereDepthMap"]; - - [sphereDepthProgram use]; - glEnableVertexAttribArray(sphereDepthPositionAttribute); - glEnableVertexAttribArray(sphereDepthImpostorSpaceAttribute); - - cylinderDepthProgram = [[GLProgram alloc] initWithVertexShaderFilename:@"CylinderDepth" fragmentShaderFilename:@"CylinderDepth"]; - [cylinderDepthProgram addAttribute:@"position"]; - [cylinderDepthProgram addAttribute:@"direction"]; - [cylinderDepthProgram addAttribute:@"inputImpostorSpaceCoordinate"]; - - if (![cylinderDepthProgram link]) - { - NSLog(@"Raytracing shader link failed"); - NSString *progLog = [cylinderDepthProgram programLog]; - NSLog(@"Program Log: %@", progLog); - NSString *fragLog = [cylinderDepthProgram fragmentShaderLog]; - NSLog(@"Frag Log: %@", fragLog); - NSString *vertLog = [cylinderDepthProgram vertexShaderLog]; - NSLog(@"Vert Log: %@", vertLog); - cylinderDepthProgram = nil; - } - - cylinderDepthPositionAttribute = [cylinderDepthProgram attributeIndex:@"position"]; - cylinderDepthDirectionAttribute = [cylinderDepthProgram attributeIndex:@"direction"]; - cylinderDepthImpostorSpaceAttribute = [cylinderDepthProgram attributeIndex:@"inputImpostorSpaceCoordinate"]; - cylinderDepthModelViewMatrix = [cylinderDepthProgram uniformIndex:@"modelViewProjMatrix"]; - cylinderDepthRadius = [cylinderDepthProgram uniformIndex:@"cylinderRadius"]; - cylinderDepthOrthographicMatrix = [cylinderDepthProgram uniformIndex:@"orthographicMatrix"]; - cylinderDepthTranslation = [cylinderDepthProgram uniformIndex:@"translation"]; - - [cylinderDepthProgram use]; - glEnableVertexAttribArray(cylinderDepthPositionAttribute); - glEnableVertexAttribArray(cylinderDepthDirectionAttribute); - glEnableVertexAttribArray(cylinderDepthImpostorSpaceAttribute); - - sphereDepthWriteProgram = [[GLProgram alloc] initWithVertexShaderFilename:@"SphereDepthWrite" fragmentShaderFilename:@"SphereDepthWrite"]; - [sphereDepthWriteProgram addAttribute:@"position"]; - [sphereDepthWriteProgram addAttribute:@"inputImpostorSpaceCoordinate"]; - if (![sphereDepthWriteProgram link]) - { - NSLog(@"Raytracing shader link failed"); - NSString *progLog = [sphereDepthWriteProgram programLog]; - NSLog(@"Program Log: %@", progLog); - NSString *fragLog = [sphereDepthWriteProgram fragmentShaderLog]; - NSLog(@"Frag Log: %@", fragLog); - NSString *vertLog = [sphereDepthWriteProgram vertexShaderLog]; - NSLog(@"Vert Log: %@", vertLog); - sphereDepthWriteProgram = nil; - } - - sphereDepthWritePositionAttribute = [sphereDepthWriteProgram attributeIndex:@"position"]; - sphereDepthWriteImpostorSpaceAttribute = [sphereDepthWriteProgram attributeIndex:@"inputImpostorSpaceCoordinate"]; - sphereDepthWriteModelViewMatrix = [sphereDepthWriteProgram uniformIndex:@"modelViewProjMatrix"]; - sphereDepthWriteRadius = [sphereDepthWriteProgram uniformIndex:@"sphereRadius"]; - sphereDepthWriteOrthographicMatrix = [sphereDepthWriteProgram uniformIndex:@"orthographicMatrix"]; - sphereDepthWriteTranslation = [sphereDepthWriteProgram uniformIndex:@"translation"]; - - [sphereDepthWriteProgram use]; - glEnableVertexAttribArray(sphereDepthWritePositionAttribute); - glEnableVertexAttribArray(sphereDepthWriteImpostorSpaceAttribute); -} - -- (void)initializeAmbientOcclusionShaders; -{ - if (sphereAmbientOcclusionProgram != nil) - { - return; - } - - [EAGLContext setCurrentContext:context]; - - sphereAmbientOcclusionProgram = [[GLProgram alloc] initWithVertexShaderFilename:@"SphereAmbientOcclusion" fragmentShaderFilename:@"SphereAmbientOcclusion"]; - [sphereAmbientOcclusionProgram addAttribute:@"position"]; - [sphereAmbientOcclusionProgram addAttribute:@"inputImpostorSpaceCoordinate"]; - [sphereAmbientOcclusionProgram addAttribute:@"ambientOcclusionTextureOffset"]; - if (![sphereAmbientOcclusionProgram link]) - { - NSLog(@"Raytracing shader link failed"); - NSString *progLog = [sphereAmbientOcclusionProgram programLog]; - NSLog(@"Program Log: %@", progLog); - NSString *fragLog = [sphereAmbientOcclusionProgram fragmentShaderLog]; - NSLog(@"Frag Log: %@", fragLog); - NSString *vertLog = [sphereAmbientOcclusionProgram vertexShaderLog]; - NSLog(@"Vert Log: %@", vertLog); - sphereAmbientOcclusionProgram = nil; - } - - sphereAmbientOcclusionPositionAttribute = [sphereAmbientOcclusionProgram attributeIndex:@"position"]; - sphereAmbientOcclusionImpostorSpaceAttribute = [sphereAmbientOcclusionProgram attributeIndex:@"inputImpostorSpaceCoordinate"]; - sphereAmbientOcclusionAOOffsetAttribute = [sphereAmbientOcclusionProgram attributeIndex:@"ambientOcclusionTextureOffset"]; - sphereAmbientOcclusionModelViewMatrix = [sphereAmbientOcclusionProgram uniformIndex:@"modelViewProjMatrix"]; - sphereAmbientOcclusionRadius = [sphereAmbientOcclusionProgram uniformIndex:@"sphereRadius"]; - sphereAmbientOcclusionDepthTexture = [sphereAmbientOcclusionProgram uniformIndex:@"depthTexture"]; - sphereAmbientOcclusionOrthographicMatrix = [sphereAmbientOcclusionProgram uniformIndex:@"orthographicMatrix"]; - sphereAmbientOcclusionPrecalculatedDepthTexture = [sphereAmbientOcclusionProgram uniformIndex:@"precalculatedSphereDepthTexture"]; - sphereAmbientOcclusionInverseModelViewMatrix = [sphereAmbientOcclusionProgram uniformIndex:@"inverseModelViewProjMatrix"]; - sphereAmbientOcclusionTexturePatchWidth = [sphereAmbientOcclusionProgram uniformIndex:@"ambientOcclusionTexturePatchWidth"]; - sphereAmbientOcclusionIntensityFactor = [sphereAmbientOcclusionProgram uniformIndex:@"intensityFactor"]; - - [sphereAmbientOcclusionProgram use]; - glEnableVertexAttribArray(sphereAmbientOcclusionPositionAttribute); - glEnableVertexAttribArray(sphereAmbientOcclusionImpostorSpaceAttribute); - glEnableVertexAttribArray(sphereAmbientOcclusionAOOffsetAttribute); - - cylinderAmbientOcclusionProgram = [[GLProgram alloc] initWithVertexShaderFilename:@"CylinderAmbientOcclusion" fragmentShaderFilename:@"CylinderAmbientOcclusion"]; - [cylinderAmbientOcclusionProgram addAttribute:@"position"]; - [cylinderAmbientOcclusionProgram addAttribute:@"direction"]; - [cylinderAmbientOcclusionProgram addAttribute:@"inputImpostorSpaceCoordinate"]; - [cylinderAmbientOcclusionProgram addAttribute:@"ambientOcclusionTextureOffset"]; - if (![cylinderAmbientOcclusionProgram link]) - { - NSLog(@"Raytracing shader link failed"); - NSString *progLog = [cylinderAmbientOcclusionProgram programLog]; - NSLog(@"Program Log: %@", progLog); - NSString *fragLog = [cylinderAmbientOcclusionProgram fragmentShaderLog]; - NSLog(@"Frag Log: %@", fragLog); - NSString *vertLog = [cylinderAmbientOcclusionProgram vertexShaderLog]; - NSLog(@"Vert Log: %@", vertLog); - cylinderAmbientOcclusionProgram = nil; - } - - cylinderAmbientOcclusionPositionAttribute = [cylinderAmbientOcclusionProgram attributeIndex:@"position"]; - cylinderAmbientOcclusionDirectionAttribute = [cylinderAmbientOcclusionProgram attributeIndex:@"direction"]; - cylinderAmbientOcclusionImpostorSpaceAttribute = [cylinderAmbientOcclusionProgram attributeIndex:@"inputImpostorSpaceCoordinate"]; - cylinderAmbientOcclusionAOOffsetAttribute = [cylinderAmbientOcclusionProgram attributeIndex:@"ambientOcclusionTextureOffset"]; - cylinderAmbientOcclusionModelViewMatrix = [cylinderAmbientOcclusionProgram uniformIndex:@"modelViewProjMatrix"]; - cylinderAmbientOcclusionRadius = [cylinderAmbientOcclusionProgram uniformIndex:@"cylinderRadius"]; - cylinderAmbientOcclusionDepthTexture = [cylinderAmbientOcclusionProgram uniformIndex:@"depthTexture"]; - cylinderAmbientOcclusionOrthographicMatrix = [cylinderAmbientOcclusionProgram uniformIndex:@"orthographicMatrix"]; - cylinderAmbientOcclusionInverseModelViewMatrix = [cylinderAmbientOcclusionProgram uniformIndex:@"inverseModelViewProjMatrix"]; - cylinderAmbientOcclusionTexturePatchWidth = [cylinderAmbientOcclusionProgram uniformIndex:@"ambientOcclusionTexturePatchWidth"]; - cylinderAmbientOcclusionIntensityFactor = [cylinderAmbientOcclusionProgram uniformIndex:@"intensityFactor"]; - - [cylinderAmbientOcclusionProgram use]; - glEnableVertexAttribArray(cylinderAmbientOcclusionPositionAttribute); - glEnableVertexAttribArray(cylinderAmbientOcclusionDirectionAttribute); - glEnableVertexAttribArray(cylinderAmbientOcclusionImpostorSpaceAttribute); - glEnableVertexAttribArray(cylinderAmbientOcclusionAOOffsetAttribute); -} - -- (void)initializeRaytracingShaders; -{ - if (sphereRaytracingProgram != nil) - { - return; - } - - [EAGLContext setCurrentContext:context]; - - sphereRaytracingProgram = [[GLProgram alloc] initWithVertexShaderFilename:@"SphereRaytracing" fragmentShaderFilename:@"SphereRaytracing"]; - [sphereRaytracingProgram addAttribute:@"position"]; - [sphereRaytracingProgram addAttribute:@"inputImpostorSpaceCoordinate"]; - [sphereRaytracingProgram addAttribute:@"ambientOcclusionTextureOffset"]; - if (![sphereRaytracingProgram link]) - { - NSLog(@"Raytracing shader link failed"); - NSString *progLog = [sphereRaytracingProgram programLog]; - NSLog(@"Program Log: %@", progLog); - NSString *fragLog = [sphereRaytracingProgram fragmentShaderLog]; - NSLog(@"Frag Log: %@", fragLog); - NSString *vertLog = [sphereRaytracingProgram vertexShaderLog]; - NSLog(@"Vert Log: %@", vertLog); - sphereRaytracingProgram = nil; - } - - sphereRaytracingPositionAttribute = [sphereRaytracingProgram attributeIndex:@"position"]; - sphereRaytracingImpostorSpaceAttribute = [sphereRaytracingProgram attributeIndex:@"inputImpostorSpaceCoordinate"]; - sphereRaytracingAOOffsetAttribute = [sphereRaytracingProgram attributeIndex:@"ambientOcclusionTextureOffset"]; - sphereRaytracingModelViewMatrix = [sphereRaytracingProgram uniformIndex:@"modelViewProjMatrix"]; - sphereRaytracingLightPosition = [sphereRaytracingProgram uniformIndex:@"lightPosition"]; - sphereRaytracingRadius = [sphereRaytracingProgram uniformIndex:@"sphereRadius"]; - sphereRaytracingColor = [sphereRaytracingProgram uniformIndex:@"sphereColor"]; - sphereRaytracingDepthTexture = [sphereRaytracingProgram uniformIndex:@"depthTexture"]; - sphereRaytracingOrthographicMatrix = [sphereRaytracingProgram uniformIndex:@"orthographicMatrix"]; - sphereRaytracingPrecalculatedDepthTexture = [sphereRaytracingProgram uniformIndex:@"precalculatedSphereDepthTexture"]; - sphereRaytracingInverseModelViewMatrix = [sphereRaytracingProgram uniformIndex:@"inverseModelViewProjMatrix"]; - sphereRaytracingTexturePatchWidth = [sphereRaytracingProgram uniformIndex:@"ambientOcclusionTexturePatchWidth"]; - sphereRaytracingAOTexture = [sphereRaytracingProgram uniformIndex:@"ambientOcclusionTexture"]; - sphereRaytracingPrecalculatedAOLookupTexture = [sphereRaytracingProgram uniformIndex:@"precalculatedAOLookupTexture"]; - sphereRaytracingTranslation = [sphereRaytracingProgram uniformIndex:@"translation"]; - sphereRaytracingDepthMapTexture = [sphereRaytracingProgram uniformIndex:@"sphereDepthMap"]; - - [sphereRaytracingProgram use]; - glEnableVertexAttribArray(sphereRaytracingPositionAttribute); - glEnableVertexAttribArray(sphereRaytracingImpostorSpaceAttribute); - glEnableVertexAttribArray(sphereRaytracingAOOffsetAttribute); - - cylinderRaytracingProgram = [[GLProgram alloc] initWithVertexShaderFilename:@"CylinderRaytracing" fragmentShaderFilename:@"CylinderRaytracing"]; - [cylinderRaytracingProgram addAttribute:@"position"]; - [cylinderRaytracingProgram addAttribute:@"direction"]; - [cylinderRaytracingProgram addAttribute:@"inputImpostorSpaceCoordinate"]; - [cylinderRaytracingProgram addAttribute:@"ambientOcclusionTextureOffset"]; - - if (![cylinderRaytracingProgram link]) - { - NSLog(@"Raytracing shader link failed"); - NSString *progLog = [cylinderRaytracingProgram programLog]; - NSLog(@"Program Log: %@", progLog); - NSString *fragLog = [cylinderRaytracingProgram fragmentShaderLog]; - NSLog(@"Frag Log: %@", fragLog); - NSString *vertLog = [cylinderRaytracingProgram vertexShaderLog]; - NSLog(@"Vert Log: %@", vertLog); - cylinderRaytracingProgram = nil; - } - - cylinderRaytracingPositionAttribute = [cylinderRaytracingProgram attributeIndex:@"position"]; - cylinderRaytracingDirectionAttribute = [cylinderRaytracingProgram attributeIndex:@"direction"]; - cylinderRaytracingImpostorSpaceAttribute = [cylinderRaytracingProgram attributeIndex:@"inputImpostorSpaceCoordinate"]; - cylinderRaytracingAOOffsetAttribute = [cylinderRaytracingProgram attributeIndex:@"ambientOcclusionTextureOffset"]; - cylinderRaytracingModelViewMatrix = [cylinderRaytracingProgram uniformIndex:@"modelViewProjMatrix"]; - cylinderRaytracingLightPosition = [cylinderRaytracingProgram uniformIndex:@"lightPosition"]; - cylinderRaytracingRadius = [cylinderRaytracingProgram uniformIndex:@"cylinderRadius"]; - cylinderRaytracingColor = [cylinderRaytracingProgram uniformIndex:@"cylinderColor"]; - cylinderRaytracingDepthTexture = [cylinderRaytracingProgram uniformIndex:@"depthTexture"]; - cylinderRaytracingOrthographicMatrix = [cylinderRaytracingProgram uniformIndex:@"orthographicMatrix"]; - cylinderRaytracingInverseModelViewMatrix = [cylinderRaytracingProgram uniformIndex:@"inverseModelViewProjMatrix"]; - cylinderRaytracingTexturePatchWidth = [cylinderRaytracingProgram uniformIndex:@"ambientOcclusionTexturePatchWidth"]; - cylinderRaytracingAOTexture = [cylinderRaytracingProgram uniformIndex:@"ambientOcclusionTexture"]; - cylinderRaytracingTranslation = [cylinderRaytracingProgram uniformIndex:@"translation"]; - - [cylinderRaytracingProgram use]; - glEnableVertexAttribArray(cylinderRaytracingPositionAttribute); - glEnableVertexAttribArray(cylinderRaytracingImpostorSpaceAttribute); - glEnableVertexAttribArray(cylinderRaytracingAOOffsetAttribute); - glEnableVertexAttribArray(cylinderRaytracingDirectionAttribute); - -#ifdef ENABLETEXTUREDISPLAYDEBUGGING - passthroughProgram = [[GLProgram alloc] initWithVertexShaderFilename:@"PlainDisplay" fragmentShaderFilename:@"PlainDisplay"]; - [passthroughProgram addAttribute:@"position"]; - [passthroughProgram addAttribute:@"inputTextureCoordinate"]; - - if (![passthroughProgram link]) - { - NSLog(@"Raytracing shader link failed"); - NSString *progLog = [passthroughProgram programLog]; - NSLog(@"Program Log: %@", progLog); - NSString *fragLog = [passthroughProgram fragmentShaderLog]; - NSLog(@"Frag Log: %@", fragLog); - NSString *vertLog = [passthroughProgram vertexShaderLog]; - NSLog(@"Vert Log: %@", vertLog); - passthroughProgram = nil; - } - - passthroughPositionAttribute = [passthroughProgram attributeIndex:@"position"]; - passthroughTextureCoordinateAttribute = [passthroughProgram attributeIndex:@"inputTextureCoordinate"]; - passthroughTexture = [passthroughProgram uniformIndex:@"texture"]; - - [passthroughProgram use]; - glEnableVertexAttribArray(passthroughPositionAttribute); - glEnableVertexAttribArray(passthroughTextureCoordinateAttribute); - -#endif - - - sphereAOLookupPrecalculationProgram = [[GLProgram alloc] initWithVertexShaderFilename:@"SphereAOLookup" fragmentShaderFilename:@"SphereAOLookup"]; - [sphereAOLookupPrecalculationProgram addAttribute:@"inputImpostorSpaceCoordinate"]; - if (![sphereAOLookupPrecalculationProgram link]) - { - NSLog(@"Raytracing shader link failed"); - NSString *progLog = [sphereAOLookupPrecalculationProgram programLog]; - NSLog(@"Program Log: %@", progLog); - NSString *fragLog = [sphereAOLookupPrecalculationProgram fragmentShaderLog]; - NSLog(@"Frag Log: %@", fragLog); - NSString *vertLog = [sphereAOLookupPrecalculationProgram vertexShaderLog]; - NSLog(@"Vert Log: %@", vertLog); - sphereAOLookupPrecalculationProgram = nil; - } - - sphereAOLookupImpostorSpaceAttribute = [sphereAOLookupPrecalculationProgram attributeIndex:@"inputImpostorSpaceCoordinate"]; - sphereAOLookupPrecalculatedDepthTexture = [sphereAOLookupPrecalculationProgram uniformIndex:@"precalculatedSphereDepthTexture"]; - sphereAOLookupInverseModelViewMatrix = [sphereAOLookupPrecalculationProgram uniformIndex:@"inverseModelViewProjMatrix"]; - - [sphereAOLookupPrecalculationProgram use]; - glEnableVertexAttribArray(sphereAOLookupImpostorSpaceAttribute); - - [self generateSphereDepthMapTexture]; - -// glDisable(GL_DEPTH_TEST); - glEnable(GL_DEPTH_TEST); - glDepthFunc(GL_LEQUAL); - glDisable(GL_ALPHA_TEST); - glEnable(GL_BLEND); - glBlendEquation(GL_FUNC_ADD); - glBlendFunc(GL_ONE, GL_ONE); - - glEnable(GL_CULL_FACE); - glCullFace(GL_BACK); - -// glAlphaFunc(GL_ALWAYS, 0); -// glDepthMask(GL_FALSE); -} - -- (void)switchToDisplayFramebuffer; -{ - glBindFramebuffer(GL_FRAMEBUFFER, viewFramebuffer); - glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer); - - CGSize newViewportSize = CGSizeMake(backingWidth, backingHeight); - - if (!CGSizeEqualToSize(newViewportSize, currentViewportSize)) - { - glViewport(0, 0, backingWidth, backingHeight); - currentViewportSize = newViewportSize; - } -} - -- (void)switchToDepthPassFramebuffer; -{ - glBindFramebuffer(GL_FRAMEBUFFER, depthPassFramebuffer); - - CGSize newViewportSize = CGSizeMake(backingWidth, backingHeight); - - if (!CGSizeEqualToSize(newViewportSize, currentViewportSize)) - { - glViewport(0, 0, backingWidth, backingHeight); - currentViewportSize = newViewportSize; - } -} - -- (void)switchToAmbientOcclusionFramebuffer; -{ - glBindFramebuffer(GL_FRAMEBUFFER, ambientOcclusionFramebuffer); - - CGSize newViewportSize = CGSizeMake(ambientOcclusionTextureWidth, ambientOcclusionTextureWidth); - - if (!CGSizeEqualToSize(newViewportSize, currentViewportSize)) - { - glViewport(0, 0, ambientOcclusionTextureWidth, ambientOcclusionTextureWidth); - currentViewportSize = newViewportSize; - } -} - -- (void)switchToAOLookupFramebuffer; -{ - glBindFramebuffer(GL_FRAMEBUFFER, sphereAOLookupFramebuffer); - - CGSize newViewportSize = CGSizeMake(ambientOcclusionLookupTextureWidth, ambientOcclusionLookupTextureWidth); - - if (!CGSizeEqualToSize(newViewportSize, currentViewportSize)) - { - glViewport(0, 0, ambientOcclusionLookupTextureWidth, ambientOcclusionLookupTextureWidth); - currentViewportSize = newViewportSize; - } -} - -- (void)generateSphereDepthMapTexture; -{ -// CFTimeInterval previousTimestamp = CFAbsoluteTimeGetCurrent(); - - // Luminance for depth: This takes only 95 ms on an iPad 1, so it's worth it for the 8% - 18% per-frame speedup - // Full lighting precalculation: This only takes 264 ms on an iPad 1 - - unsigned char *sphereDepthTextureData = (unsigned char *)malloc(sphereDepthTextureWidth * sphereDepthTextureWidth * 4); - - glGenTextures(1, &sphereDepthMappingTexture); - - glActiveTexture(GL_TEXTURE3); - glBindTexture(GL_TEXTURE_2D, sphereDepthMappingTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); -// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); -// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); -// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); -// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); -// glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); - - for (unsigned int currentColumnInTexture = 0; currentColumnInTexture < sphereDepthTextureWidth; currentColumnInTexture++) - { - float normalizedYLocation = -1.0 + 2.0 * (float)currentColumnInTexture / (float)sphereDepthTextureWidth; - for (unsigned int currentRowInTexture = 0; currentRowInTexture < sphereDepthTextureWidth; currentRowInTexture++) - { - float normalizedXLocation = -1.0 + 2.0 * (float)currentRowInTexture / (float)sphereDepthTextureWidth; - unsigned char currentDepthByte = 0, currentAmbientLightingByte = 0, currentSpecularLightingByte = 0, alphaByte = 0; - - float distanceFromCenter = sqrt(normalizedXLocation * normalizedXLocation + normalizedYLocation * normalizedYLocation); - float currentSphereDepth = 0.0; - float lightingNormalX = normalizedXLocation, lightingNormalY = normalizedYLocation; - - if (distanceFromCenter <= 1.0) - { - // First, calculate the depth of the sphere at this point - currentSphereDepth = sqrt(1.0 - distanceFromCenter * distanceFromCenter); - currentDepthByte = round(255.0 * currentSphereDepth); - - alphaByte = 255; - } - else - { - float normalizationFactor = sqrt(normalizedXLocation * normalizedXLocation + normalizedYLocation * normalizedYLocation); - lightingNormalX = lightingNormalX / normalizationFactor; - lightingNormalY = lightingNormalY / normalizationFactor; - } - - // Then, do the ambient lighting factor - float dotProductForLighting = lightingNormalX * lightDirection[0] + lightingNormalY * lightDirection[1] + currentSphereDepth * lightDirection[2]; - if (dotProductForLighting < 0.0) - { - dotProductForLighting = 0.0; - } - else if (dotProductForLighting > 1.0) - { - dotProductForLighting = 1.0; - } - -// float ambientLightingProduct = dotProductForLighting + 0.4; -// ambientLightingProduct = MIN(1.0, ambientLightingProduct); - - currentAmbientLightingByte = round(255.0 * dotProductForLighting); - - // Finally, do the specular lighting factor - float specularIntensity = pow(dotProductForLighting, 40.0); -// currentSpecularLightingByte = round(255.0 * specularIntensity * 0.48); -// currentSpecularLightingByte = round(255.0 * specularIntensity * 0.6); - currentSpecularLightingByte = round(255.0 * specularIntensity * 0.5); - - sphereDepthTextureData[currentColumnInTexture * sphereDepthTextureWidth * 4 + (currentRowInTexture * 4)] = currentDepthByte; - sphereDepthTextureData[currentColumnInTexture * sphereDepthTextureWidth * 4 + (currentRowInTexture * 4) + 1] = currentAmbientLightingByte; - sphereDepthTextureData[currentColumnInTexture * sphereDepthTextureWidth * 4 + (currentRowInTexture * 4) + 2] = currentSpecularLightingByte; - sphereDepthTextureData[currentColumnInTexture * sphereDepthTextureWidth * 4 + (currentRowInTexture * 4) + 3] = alphaByte; -/* - float lightingIntensity = 0.2 + 1.3 * clamp(dot(lightPosition, normal), 0.0, 1.0) * ambientOcclusionIntensity.r; - finalSphereColor *= lightingIntensity; - - // Per fragment specular lighting - lightingIntensity = clamp(dot(lightPosition, normal), 0.0, 1.0); - lightingIntensity = pow(lightingIntensity, 60.0) * ambientOcclusionIntensity.r * 1.2; - finalSphereColor += vec3(0.4, 0.4, 0.4) * lightingIntensity + vec3(1.0, 1.0, 1.0) * 0.2 * ambientOcclusionIntensity.r; -*/ - - } - } - -// glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, sphereDepthTextureWidth, sphereDepthTextureWidth, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, sphereDepthTextureData); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, sphereDepthTextureWidth, sphereDepthTextureWidth, 0, GL_RGBA, GL_UNSIGNED_BYTE, sphereDepthTextureData); - glGenerateMipmap(GL_TEXTURE_2D); -// glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE); - - free(sphereDepthTextureData); - -// CFTimeInterval frameDuration = CFAbsoluteTimeGetCurrent() - previousTimestamp; - -// NSLog(@"Texture generation duration: %f ms", frameDuration * 1000.0); - -} - -- (void)destroyFramebuffers; -{ - dispatch_async(openGLESContextQueue, ^{ - [EAGLContext setCurrentContext:context]; - - if (viewFramebuffer) - { - glDeleteFramebuffers(1, &viewFramebuffer); - viewFramebuffer = 0; - } - - if (viewRenderbuffer) - { - glDeleteRenderbuffers(1, &viewRenderbuffer); - viewRenderbuffer = 0; - } - - if (viewDepthBuffer) - { - glDeleteRenderbuffers(1, &viewDepthBuffer); - viewDepthBuffer = 0; - } - - if (depthPassFramebuffer) - { - glDeleteFramebuffers(1, &depthPassFramebuffer); - depthPassFramebuffer = 0; - } - - if (depthPassDepthBuffer) - { - glDeleteRenderbuffers(1, &depthPassDepthBuffer); - depthPassDepthBuffer = 0; - } - - if (depthPassTexture) - { - glDeleteTextures(1, &depthPassTexture); - depthPassTexture = 0; - } - - }); -} - -- (void)configureProjection; -{ - [self loadOrthoMatrix:orthographicMatrix left:-1.0 right:1.0 bottom:(-1.0 * (GLfloat)backingHeight / (GLfloat)backingWidth) top:(1.0 * (GLfloat)backingHeight / (GLfloat)backingWidth) near:-1.0 far:1.0]; -} - -- (void)presentRenderBuffer; -{ - [context presentRenderbuffer:GL_RENDERBUFFER]; -} - -- (void)clearScreen; -{ - dispatch_async(openGLESContextQueue, ^{ - [EAGLContext setCurrentContext:context]; - - [self switchToDisplayFramebuffer]; - - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - [self presentRenderBuffer]; - }); -} - -- (void)suspendRenderingDuringRotation; -{ - dispatch_semaphore_wait(frameRenderingSemaphore, DISPATCH_TIME_FOREVER); -} - -- (void)resumeRenderingDuringRotation; -{ - dispatch_semaphore_signal(frameRenderingSemaphore); -} - -#pragma mark - -#pragma mark Actual OpenGL rendering - -- (void)renderFrameForMolecule:(SLSMolecule *)molecule; -{ - if (!isSceneReady) - { - return; - } - - // In order to prevent frames to be rendered from building up indefinitely, we use a dispatch semaphore to keep at most two frames in the queue - - if (dispatch_semaphore_wait(frameRenderingSemaphore, DISPATCH_TIME_NOW) != 0) - { - return; - } - - dispatch_async(openGLESContextQueue, ^{ - - [EAGLContext setCurrentContext:context]; - -// CFTimeInterval previousTimestamp = CFAbsoluteTimeGetCurrent(); - - GLfloat currentModelViewMatrix[9]; - [self convert3DTransform:¤tCalculatedMatrix to3x3Matrix:currentModelViewMatrix]; - - CATransform3D inverseMatrix = CATransform3DInvert(currentCalculatedMatrix); - GLfloat inverseModelViewMatrix[9]; - [self convert3DTransform:&inverseMatrix to3x3Matrix:inverseModelViewMatrix]; - - // Load these once here so that they don't go out of sync between rendering passes during user gestures - GLfloat currentTranslation[3]; - currentTranslation[0] = accumulatedModelTranslation[0]; - currentTranslation[1] = accumulatedModelTranslation[1]; - currentTranslation[2] = accumulatedModelTranslation[2]; - - GLfloat currentScaleFactor = currentModelScaleFactor; - - [self precalculateAOLookupTextureForInverseMatrix:inverseModelViewMatrix]; - [self renderDepthTextureForModelViewMatrix:currentModelViewMatrix translation:currentTranslation scale:currentScaleFactor]; -// [self displayTextureToScreen:sphereAOLookupTexture]; -// [self displayTextureToScreen:depthPassTexture]; -// [self displayTextureToScreen:ambientOcclusionTexture]; - [self renderRaytracedSceneForModelViewMatrix:currentModelViewMatrix inverseMatrix:inverseModelViewMatrix translation:currentTranslation scale:currentScaleFactor]; - - const GLenum discards[] = {GL_DEPTH_ATTACHMENT}; - glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, discards); - - [self presentRenderBuffer]; - -// CFTimeInterval frameDuration = CFAbsoluteTimeGetCurrent() - previousTimestamp; -// -// NSLog(@"Frame duration: %f ms", frameDuration * 1000.0); - - dispatch_semaphore_signal(frameRenderingSemaphore); - }); -} - -#pragma mark - -#pragma mark Molecule 3-D geometry generation - -- (void)configureBasedOnNumberOfAtoms:(NSUInteger)numberOfAtoms numberOfBonds:(NSUInteger)numberOfBonds; -{ - widthOfAtomAOTexturePatch = (GLfloat)ambientOcclusionTextureWidth / (ceil(sqrt((GLfloat)numberOfAtoms + (GLfloat)numberOfBonds))); - normalizedAOTexturePatchWidth = (GLfloat)widthOfAtomAOTexturePatch / (GLfloat)ambientOcclusionTextureWidth; - - previousAmbientOcclusionOffset[0] = normalizedAOTexturePatchWidth / 2.0; - previousAmbientOcclusionOffset[1] = normalizedAOTexturePatchWidth / 2.0; - - shouldDrawBonds = (numberOfBonds > 0); -} - -- (void)addAtomToVertexBuffers:(SLSAtomType)atomType atPoint:(SLS3DPoint)newPoint; -{ - GLushort baseToAddToIndices = numberOfAtomVertices[atomType]; - - GLfloat newVertex[3]; - // newVertex[0] = newPoint.x; - newVertex[0] = -newPoint.x; - newVertex[1] = newPoint.y; - newVertex[2] = newPoint.z; - - /* - // Square coordinate generation - - GLfloat lowerLeftTexture[2] = {-1.0, -1.0}; - GLfloat lowerRightTexture[2] = {1.0, -1.0}; - GLfloat upperLeftTexture[2] = {-1.0, 1.0}; - GLfloat upperRightTexture[2] = {1.0, 1.0}; - - // Add four copies of this vertex, that will be translated in the vertex shader into the billboard - // Interleave texture coordinates in VBO - [self addVertex:newVertex forAtomType:atomType]; - [self addTextureCoordinate:lowerLeftTexture forAtomType:atomType]; - [self addAmbientOcclusionTextureOffset:previousAmbientOcclusionOffset forAtomType:atomType]; - [self addVertex:newVertex forAtomType:atomType]; - [self addTextureCoordinate:lowerRightTexture forAtomType:atomType]; - [self addAmbientOcclusionTextureOffset:previousAmbientOcclusionOffset forAtomType:atomType]; - [self addVertex:newVertex forAtomType:atomType]; - [self addTextureCoordinate:upperLeftTexture forAtomType:atomType]; - [self addAmbientOcclusionTextureOffset:previousAmbientOcclusionOffset forAtomType:atomType]; - [self addVertex:newVertex forAtomType:atomType]; - [self addTextureCoordinate:upperRightTexture forAtomType:atomType]; - [self addAmbientOcclusionTextureOffset:previousAmbientOcclusionOffset forAtomType:atomType]; - - // 123324 - GLushort newIndices[6]; - newIndices[0] = baseToAddToIndices; - newIndices[1] = baseToAddToIndices + 1; - newIndices[2] = baseToAddToIndices + 2; - newIndices[3] = baseToAddToIndices + 2; - newIndices[4] = baseToAddToIndices + 1; - newIndices[5] = baseToAddToIndices + 3; - - - [self addIndices:newIndices size:6 forAtomType:atomType]; -*/ - - - /* - // Hexagonal coordinate generation, using raster-optimized layout - - GLfloat positiveSideComponent = 2.0 / sqrt(3); - GLfloat negativeSideComponent = -2.0 / sqrt(3); - - GLfloat hexagonPoints[6][2] = { - {negativeSideComponent, 1.0}, - {negativeSideComponent, -1.0}, - {1.0, 0.0}, - {positiveSideComponent, 1.0}, - {-1.0, 0.0}, - {positiveSideComponent, -1.0} - }; - - for (unsigned int currentTextureCoordinate = 0; currentTextureCoordinate < 6; currentTextureCoordinate++) - { - [self addVertex:newVertex forAtomType:atomType]; - [self addTextureCoordinate:hexagonPoints[currentTextureCoordinate] forAtomType:atomType]; - [self addAmbientOcclusionTextureOffset:previousAmbientOcclusionOffset forAtomType:atomType]; - } - - // 123,341,152,263 - GLushort newIndices[12]; - newIndices[0] = baseToAddToIndices; - newIndices[1] = baseToAddToIndices + 1; - newIndices[2] = baseToAddToIndices + 2; - - newIndices[3] = baseToAddToIndices + 2; - newIndices[4] = baseToAddToIndices + 3; - newIndices[5] = baseToAddToIndices; - - newIndices[6] = baseToAddToIndices + 0; - newIndices[7] = baseToAddToIndices + 4; - newIndices[8] = baseToAddToIndices + 1; - - newIndices[9] = baseToAddToIndices + 1; - newIndices[10] = baseToAddToIndices + 5; - newIndices[11] = baseToAddToIndices + 2; - - [self addIndices:newIndices size:12 forAtomType:atomType]; - - */ - - - // Octagonal coordinate generation, using raster-optimized layout - GLfloat positiveSideComponent = 1.0 - 2.0 / (sqrt(2) + 2); - GLfloat negativeSideComponent = -1.0 + 2.0 / (sqrt(2) + 2); - - GLfloat octagonPoints[8][2] = { - {negativeSideComponent, 1.0}, - {-1.0, negativeSideComponent}, - {1.0, positiveSideComponent}, - {positiveSideComponent, -1.0}, - {1.0, negativeSideComponent}, - {positiveSideComponent, 1.0}, - {-1.0, positiveSideComponent}, - {negativeSideComponent, -1.0}, - }; - - // Add eight copies of this vertex, that will be translated in the vertex shader into the billboard - // Interleave texture coordinates in VBO - - for (unsigned int currentTextureCoordinate = 0; currentTextureCoordinate < 8; currentTextureCoordinate++) - { - [self addVertex:newVertex forAtomType:atomType]; - [self addTextureCoordinate:octagonPoints[currentTextureCoordinate] forAtomType:atomType]; - [self addAmbientOcclusionTextureOffset:previousAmbientOcclusionOffset forAtomType:atomType]; - } - - // 123, 324, 345, 136, 217, 428 - - GLushort newIndices[18]; - newIndices[0] = baseToAddToIndices; - newIndices[1] = baseToAddToIndices + 1; - newIndices[2] = baseToAddToIndices + 2; - - newIndices[3] = baseToAddToIndices + 2; - newIndices[4] = baseToAddToIndices + 1; - newIndices[5] = baseToAddToIndices + 3; - - newIndices[6] = baseToAddToIndices + 2; - newIndices[7] = baseToAddToIndices + 3; - newIndices[8] = baseToAddToIndices + 4; - - newIndices[9] = baseToAddToIndices; - newIndices[10] = baseToAddToIndices + 2; - newIndices[11] = baseToAddToIndices + 5; - - newIndices[12] = baseToAddToIndices + 1; - newIndices[13] = baseToAddToIndices; - newIndices[14] = baseToAddToIndices + 6; - - newIndices[15] = baseToAddToIndices + 3; - newIndices[16] = baseToAddToIndices + 1; - newIndices[17] = baseToAddToIndices + 7; - - [self addIndices:newIndices size:18 forAtomType:atomType]; - - - previousAmbientOcclusionOffset[0] += normalizedAOTexturePatchWidth; - if (previousAmbientOcclusionOffset[0] > (1.0 - normalizedAOTexturePatchWidth * 0.15)) - { - previousAmbientOcclusionOffset[0] = normalizedAOTexturePatchWidth / 2.0; - previousAmbientOcclusionOffset[1] += normalizedAOTexturePatchWidth; - } -} - -- (void)addBondToVertexBuffersWithStartPoint:(SLS3DPoint)startPoint endPoint:(SLS3DPoint)endPoint bondColor:(GLubyte *)bondColor bondType:(SLSBondType)bondType; -{ - if (currentBondVBO >= MAX_BOND_VBOS) - { - return; - } - - GLushort baseToAddToIndices = numberOfBondVertices[currentBondVBO]; - - // Vertex positions, duplicated for later displacement at each end - // Interleave the directions and texture coordinates for the VBO - GLfloat newVertex[3], cylinderDirection[3]; - -// cylinderDirection[0] = endPoint.x - startPoint.x; - cylinderDirection[0] = startPoint.x - endPoint.x; - cylinderDirection[1] = endPoint.y - startPoint.y; - cylinderDirection[2] = endPoint.z - startPoint.z; - - // Impostor space coordinates - GLfloat lowerLeftTexture[2] = {-1.0, -1.0}; - GLfloat lowerRightTexture[2] = {1.0, -1.0}; - GLfloat upperLeftTexture[2] = {-1.0, 1.0}; - GLfloat upperRightTexture[2] = {1.0, 1.0}; - -// newVertex[0] = startPoint.x; - newVertex[0] = -startPoint.x; - newVertex[1] = startPoint.y; - newVertex[2] = startPoint.z; - - [self addBondVertex:newVertex]; - [self addBondDirection:cylinderDirection]; - [self addBondTextureCoordinate:lowerLeftTexture]; - [self addBondAmbientOcclusionTextureOffset:previousAmbientOcclusionOffset]; - [self addBondVertex:newVertex]; - [self addBondDirection:cylinderDirection]; - [self addBondTextureCoordinate:lowerRightTexture]; - [self addBondAmbientOcclusionTextureOffset:previousAmbientOcclusionOffset]; - -// newVertex[0] = endPoint.x; - newVertex[0] = -endPoint.x; - newVertex[1] = endPoint.y; - newVertex[2] = endPoint.z; - - [self addBondVertex:newVertex]; - [self addBondDirection:cylinderDirection]; - [self addBondTextureCoordinate:upperLeftTexture]; - [self addBondAmbientOcclusionTextureOffset:previousAmbientOcclusionOffset]; - [self addBondVertex:newVertex]; - [self addBondDirection:cylinderDirection]; - [self addBondTextureCoordinate:upperRightTexture]; - [self addBondAmbientOcclusionTextureOffset:previousAmbientOcclusionOffset]; - - // Vertex indices - // 123243 - GLushort newIndices[6]; - newIndices[0] = baseToAddToIndices; - newIndices[1] = baseToAddToIndices + 1; - newIndices[2] = baseToAddToIndices + 2; - newIndices[3] = baseToAddToIndices + 1; - newIndices[4] = baseToAddToIndices + 3; - newIndices[5] = baseToAddToIndices + 2; - - [self addBondIndices:newIndices size:6]; - - previousAmbientOcclusionOffset[0] += normalizedAOTexturePatchWidth; - if (previousAmbientOcclusionOffset[0] > (1.0 - normalizedAOTexturePatchWidth * 0.15)) - { - previousAmbientOcclusionOffset[0] = normalizedAOTexturePatchWidth / 2.0; - previousAmbientOcclusionOffset[1] += normalizedAOTexturePatchWidth; - } -} - -- (void)addVertex:(GLfloat *)newVertex forAtomType:(SLSAtomType)atomType; -{ - if (atomVBOs[atomType] == nil) - { - atomVBOs[atomType] = [[NSMutableData alloc] init]; - } - - [atomVBOs[atomType] appendBytes:newVertex length:(sizeof(GLfloat) * 3)]; - - numberOfAtomVertices[atomType]++; - totalNumberOfVertices++; -} - -- (void)addBondVertex:(GLfloat *)newVertex; -{ - if (bondVBOs[currentBondVBO] == nil) - { - bondVBOs[currentBondVBO] = [[NSMutableData alloc] init]; - } - - [bondVBOs[currentBondVBO] appendBytes:newVertex length:(sizeof(GLfloat) * 3)]; - - numberOfBondVertices[currentBondVBO]++; - totalNumberOfVertices++; -} - -- (void)addTextureCoordinate:(GLfloat *)newTextureCoordinate forAtomType:(SLSAtomType)atomType; -{ - if (atomVBOs[atomType] == nil) - { - atomVBOs[atomType] = [[NSMutableData alloc] init]; - } - - [atomVBOs[atomType] appendBytes:newTextureCoordinate length:(sizeof(GLfloat) * 2)]; -} - -- (void)addAmbientOcclusionTextureOffset:(GLfloat *)ambientOcclusionOffset forAtomType:(SLSAtomType)atomType; -{ - if (atomVBOs[atomType] == nil) - { - atomVBOs[atomType] = [[NSMutableData alloc] init]; - } - - [atomVBOs[atomType] appendBytes:ambientOcclusionOffset length:(sizeof(GLfloat) * 2)]; -} - -- (void)addBondDirection:(GLfloat *)newDirection; -{ - if (bondVBOs[currentBondVBO] == nil) - { - bondVBOs[currentBondVBO] = [[NSMutableData alloc] init]; - } - - [bondVBOs[currentBondVBO] appendBytes:newDirection length:(sizeof(GLfloat) * 3)]; -} - -- (void)addBondTextureCoordinate:(GLfloat *)newTextureCoordinate; -{ - if (bondVBOs[currentBondVBO] == nil) - { - bondVBOs[currentBondVBO] = [[NSMutableData alloc] init]; - } - - [bondVBOs[currentBondVBO] appendBytes:newTextureCoordinate length:(sizeof(GLfloat) * 2)]; -} - -- (void)addBondAmbientOcclusionTextureOffset:(GLfloat *)ambientOcclusionOffset; -{ - if (bondVBOs[currentBondVBO] == nil) - { - bondVBOs[currentBondVBO] = [[NSMutableData alloc] init]; - } - - [bondVBOs[currentBondVBO] appendBytes:ambientOcclusionOffset length:(sizeof(GLfloat) * 2)]; -} - -#pragma mark - -#pragma mark OpenGL drawing routines - -- (void)testPrecisionOfConversionCalculation; -{ - float stepSize = 1.0 / 20.0; - - for (float inputFloat = 0.0; inputFloat < 1.0; inputFloat += stepSize) - { - float ceilInputFloat = ceil(inputFloat * 765.0) / 765.0; - - float blue = MAX(0.0, ceilInputFloat - (2.0 / 3.0)); - float green = MAX(0.0, ceilInputFloat - (1.0 / 3.0) - blue); - float red = ceilInputFloat - blue - green; - - unsigned char blueValue = (unsigned char)(blue * 3.0 * 255.0); - unsigned char greenValue = (unsigned char)(green * 3.0 * 255.0); - unsigned char redValue = (unsigned char)(red * 3.0 * 255.0); - - float result = ((float)blueValue / 255.0 + (float)greenValue / 255.0 + (float)redValue / 255.0) / 3.0; - - NSLog(@"1: Input value: %f, converted value: %f", inputFloat, result); - - - int convertedInput = ceil(inputFloat * 765.0); - int blueInt = MAX(0, convertedInput - 510); - int greenInt = MAX(0, convertedInput - 255 - blueInt); - int redInt = convertedInput - blueInt - greenInt; - - unsigned char blueValue2 = (unsigned char)(blueInt); - unsigned char greenValue2 = (unsigned char)(greenInt); - unsigned char redValue2 = (unsigned char)(redInt); - - float result2 = ((float)blueValue2 / 255.0 + (float)greenValue2 / 255.0 + (float)redValue2 / 255.0) / 3.0; - NSLog(@"2: Input value: %f, converted value: %f", inputFloat, result2); - - } -} - -- (void)bindVertexBuffersForMolecule; -{ -// [super performSelectorOnMainThread:@selector( bindVertexBuffersForMolecule) withObject:nil waitUntilDone:YES]; - [super bindVertexBuffersForMolecule]; - [self prepareAmbientOcclusionMap]; - - isSceneReady = YES; -} - -- (void)renderDepthTextureForModelViewMatrix:(GLfloat *)depthModelViewMatrix translation:(GLfloat *)modelTranslation scale:(GLfloat)scaleFactor; -{ - [self switchToDepthPassFramebuffer]; - - glClearColor(1.0f, 1.0f, 1.0f, 1.0f); - glBlendEquation(GL_MIN_EXT); - glDepthMask(GL_TRUE); - glDepthFunc(GL_LEQUAL); - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - [self writeDepthValuesForOpaqueAreasForModelViewMatrix:depthModelViewMatrix translation:modelTranslation scale:scaleFactor]; - glDepthMask(GL_FALSE); - - // Draw the spheres - [sphereDepthProgram use]; - - glUniformMatrix3fv(sphereDepthModelViewMatrix, 1, 0, depthModelViewMatrix); - glUniform3fv(sphereDepthTranslation, 1, modelTranslation); - glUniform1i(sphereDepthMapTexture, 3); - - float sphereScaleFactor = overallMoleculeScaleFactor * scaleFactor * atomRadiusScaleFactor; - GLsizei atomVBOStride = sizeof(GLfloat) * 3 + sizeof(GLfloat) * 2 + sizeof(GLfloat) * 2; - - for (unsigned int currentAtomType = 0; currentAtomType < NUM_ATOMTYPES; currentAtomType++) - { - if (atomIndexBufferHandle[currentAtomType] != 0) - { - glUniform1f(sphereDepthRadius, atomProperties[currentAtomType].atomRadius * sphereScaleFactor); - - // Bind the VBO and attach it to the program - glBindBuffer(GL_ARRAY_BUFFER, atomVertexBufferHandles[currentAtomType]); - glVertexAttribPointer(sphereDepthPositionAttribute, 3, GL_FLOAT, 0, atomVBOStride, (char *)NULL + 0); - glVertexAttribPointer(sphereDepthImpostorSpaceAttribute, 2, GL_FLOAT, 0, atomVBOStride, (char *)NULL + (sizeof(GLfloat) * 3)); - - // Bind the index buffer and draw to the screen - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, atomIndexBufferHandle[currentAtomType]); - glDrawElements(GL_TRIANGLES, numberOfIndicesInBuffer[currentAtomType], GL_UNSIGNED_SHORT, NULL); - - // Unbind the buffers - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glBindBuffer(GL_ARRAY_BUFFER, 0); - } - } - - if (shouldDrawBonds) - { - // Draw the cylinders - [cylinderDepthProgram use]; - - float cylinderScaleFactor = overallMoleculeScaleFactor * scaleFactor * bondRadiusScaleFactor; - GLsizei bondVBOStride = sizeof(GLfloat) * 3 + sizeof(GLfloat) * 3 + sizeof(GLfloat) * 2 + sizeof(GLfloat) * 2; - GLfloat bondRadius = 1.0; - - glUniform1f(cylinderDepthRadius, bondRadius * cylinderScaleFactor); - glUniformMatrix3fv(cylinderDepthModelViewMatrix, 1, 0, depthModelViewMatrix); - glUniform3fv(cylinderDepthTranslation, 1, modelTranslation); - - for (unsigned int currentBondVBOIndex = 0; currentBondVBOIndex < MAX_BOND_VBOS; currentBondVBOIndex++) - { - // Draw bonds next - if (bondVertexBufferHandle[currentBondVBOIndex] != 0) - { - // Bind the VBO and attach it to the program - glBindBuffer(GL_ARRAY_BUFFER, bondVertexBufferHandle[currentBondVBOIndex]); - glVertexAttribPointer(cylinderDepthPositionAttribute, 3, GL_FLOAT, 0, bondVBOStride, (char *)NULL + 0); - glVertexAttribPointer(cylinderDepthDirectionAttribute, 3, GL_FLOAT, 0, bondVBOStride, (char *)NULL + (sizeof(GLfloat) * 3)); - glVertexAttribPointer(cylinderDepthImpostorSpaceAttribute, 2, GL_FLOAT, 0, bondVBOStride, (char *)NULL + (sizeof(GLfloat) * 3) + (sizeof(GLfloat) * 3)); - - // Bind the index buffer and draw to the screen - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bondIndexBufferHandle[currentBondVBOIndex]); - glDrawElements(GL_TRIANGLES, numberOfBondIndicesInBuffer[currentBondVBOIndex], GL_UNSIGNED_SHORT, NULL); - - // Unbind the buffers - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glBindBuffer(GL_ARRAY_BUFFER, 0); - } - } - } - - const GLenum discards[] = {GL_DEPTH_ATTACHMENT}; - glDiscardFramebufferEXT(GL_FRAMEBUFFER, 1, discards); -} - -- (void)writeDepthValuesForOpaqueAreasForModelViewMatrix:(GLfloat *)depthModelViewMatrix translation:(GLfloat *)modelTranslation scale:(GLfloat)scaleFactor; -{ - glDisable(GL_BLEND); - - // Draw the spheres - [sphereDepthWriteProgram use]; - - glUniformMatrix3fv(sphereDepthWriteModelViewMatrix, 1, 0, depthModelViewMatrix); - glUniform3fv(sphereDepthWriteTranslation, 1, modelTranslation); - - float sphereScaleFactor = overallMoleculeScaleFactor * scaleFactor * atomRadiusScaleFactor; - GLsizei atomVBOStride = sizeof(GLfloat) * 3 + sizeof(GLfloat) * 2 + sizeof(GLfloat) * 2; - - for (unsigned int currentAtomType = 0; currentAtomType < NUM_ATOMTYPES; currentAtomType++) - { - if (atomIndexBufferHandle[currentAtomType] != 0) - { - glUniform1f(sphereDepthWriteRadius, atomProperties[currentAtomType].atomRadius * sphereScaleFactor); - - // Bind the VBO and attach it to the program - glBindBuffer(GL_ARRAY_BUFFER, atomVertexBufferHandles[currentAtomType]); - glVertexAttribPointer(sphereDepthWritePositionAttribute, 3, GL_FLOAT, 0, atomVBOStride, (char *)NULL + 0); - glVertexAttribPointer(sphereDepthWriteImpostorSpaceAttribute, 2, GL_FLOAT, 0, atomVBOStride, (char *)NULL + (sizeof(GLfloat) * 3)); - - // Bind the index buffer and draw to the screen - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, atomIndexBufferHandle[currentAtomType]); - glDrawElements(GL_TRIANGLES, numberOfIndicesInBuffer[currentAtomType], GL_UNSIGNED_SHORT, NULL); - - // Unbind the buffers - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glBindBuffer(GL_ARRAY_BUFFER, 0); - } - } - - glEnable(GL_BLEND); -} - -- (void)renderRaytracedSceneForModelViewMatrix:(GLfloat *)raytracingModelViewMatrix inverseMatrix:(GLfloat *)inverseMatrix translation:(GLfloat *)modelTranslation scale:(GLfloat)scaleFactor; -{ - [self switchToDisplayFramebuffer]; - -#ifdef USEWHITEBACKGROUND - glBlendEquation(GL_MIN_EXT); - - // glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glClearColor(1.0f, 1.0f, 1.0f, 1.0f); - -#else - glBlendEquation(GL_MAX_EXT); - - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); -#endif - - - glDepthMask(GL_TRUE); - - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - glColorMask(0.0, 0.0, 0.0, 0.0); - [self writeDepthValuesForOpaqueAreasForModelViewMatrix:raytracingModelViewMatrix translation:modelTranslation scale:scaleFactor]; - glColorMask(1.0, 1.0, 1.0, 1.0); - glDepthMask(GL_FALSE); - -// glClear(GL_COLOR_BUFFER_BIT); -// - // Draw the spheres - [sphereRaytracingProgram use]; - - glUniform3fv(sphereRaytracingLightPosition, 1, lightDirection); - - // Load in the depth texture from the previous pass - glUniform1i(sphereRaytracingDepthTexture, 0); - glUniform1i(sphereRaytracingAOTexture, 1); - glUniform1i(sphereRaytracingPrecalculatedAOLookupTexture, 2); - - glUniformMatrix3fv(sphereRaytracingModelViewMatrix, 1, 0, raytracingModelViewMatrix); - glUniformMatrix3fv(sphereRaytracingInverseModelViewMatrix, 1, 0, inverseMatrix); - glUniform1f(sphereRaytracingTexturePatchWidth, (normalizedAOTexturePatchWidth - 2.0 / (GLfloat)ambientOcclusionTextureWidth) * 0.5); - glUniform3fv(sphereRaytracingTranslation, 1, modelTranslation); - glUniform1i(sphereRaytracingDepthMapTexture, 3); - - float sphereScaleFactor = overallMoleculeScaleFactor * scaleFactor * atomRadiusScaleFactor; - GLsizei atomVBOStride = sizeof(GLfloat) * 3 + sizeof(GLfloat) * 2 + sizeof(GLfloat) * 2; - - for (unsigned int currentAtomType = 0; currentAtomType < NUM_ATOMTYPES; currentAtomType++) - { - if (atomIndexBufferHandle[currentAtomType] != 0) - { - glUniform1f(sphereRaytracingRadius, atomProperties[currentAtomType].atomRadius * sphereScaleFactor); - glUniform3f(sphereRaytracingColor, (GLfloat)atomProperties[currentAtomType].redComponent / 255.0f , (GLfloat)atomProperties[currentAtomType].greenComponent / 255.0f, (GLfloat)atomProperties[currentAtomType].blueComponent / 255.0f); - - // Bind the VBO and attach it to the program - glBindBuffer(GL_ARRAY_BUFFER, atomVertexBufferHandles[currentAtomType]); - glVertexAttribPointer(sphereRaytracingPositionAttribute, 3, GL_FLOAT, 0, atomVBOStride, (char *)NULL + 0); - glVertexAttribPointer(sphereRaytracingImpostorSpaceAttribute, 2, GL_FLOAT, 0, atomVBOStride, (char *)NULL + (sizeof(GLfloat) * 3)); - glVertexAttribPointer(sphereRaytracingAOOffsetAttribute, 2, GL_FLOAT, 0, atomVBOStride, (char *)NULL + (sizeof(GLfloat) * 3) + (sizeof(GLfloat) * 2)); - - // Bind the index buffer and draw to the screen - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, atomIndexBufferHandle[currentAtomType]); - glDrawElements(GL_TRIANGLES, numberOfIndicesInBuffer[currentAtomType], GL_UNSIGNED_SHORT, NULL); - - // Unbind the buffers - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glBindBuffer(GL_ARRAY_BUFFER, 0); - } - } - - if (shouldDrawBonds) - { - // Draw the cylinders - [cylinderRaytracingProgram use]; - - glUniform3fv(cylinderRaytracingLightPosition, 1, lightDirection); - glUniform1i(cylinderRaytracingDepthTexture, 0); - glUniform1i(cylinderRaytracingAOTexture, 1); - glUniform1f(cylinderRaytracingTexturePatchWidth, normalizedAOTexturePatchWidth - 0.5 / (GLfloat)ambientOcclusionTextureWidth); - - float cylinderScaleFactor = overallMoleculeScaleFactor * scaleFactor * bondRadiusScaleFactor; - GLsizei bondVBOStride = sizeof(GLfloat) * 3 + sizeof(GLfloat) * 3 + sizeof(GLfloat) * 2 + sizeof(GLfloat) * 2; - GLfloat bondRadius = 1.0; - - glUniform1f(cylinderRaytracingRadius, bondRadius * cylinderScaleFactor); - glUniform3f(cylinderRaytracingColor, 0.75, 0.75, 0.75); - glUniformMatrix3fv(cylinderRaytracingModelViewMatrix, 1, 0, raytracingModelViewMatrix); - glUniformMatrix3fv(cylinderRaytracingInverseModelViewMatrix, 1, 0, inverseMatrix); - glUniform3fv(cylinderRaytracingTranslation, 1, modelTranslation); - - - for (unsigned int currentBondVBOIndex = 0; currentBondVBOIndex < MAX_BOND_VBOS; currentBondVBOIndex++) - { - // Draw bonds next - if (bondVertexBufferHandle[currentBondVBOIndex] != 0) - { - - // Bind the VBO and attach it to the program - glBindBuffer(GL_ARRAY_BUFFER, bondVertexBufferHandle[currentBondVBOIndex]); - glVertexAttribPointer(cylinderRaytracingPositionAttribute, 3, GL_FLOAT, 0, bondVBOStride, (char *)NULL + 0); - glVertexAttribPointer(cylinderRaytracingDirectionAttribute, 3, GL_FLOAT, 0, bondVBOStride, (char *)NULL + (sizeof(GLfloat) * 3)); - glVertexAttribPointer(cylinderRaytracingImpostorSpaceAttribute, 2, GL_FLOAT, 0, bondVBOStride, (char *)NULL + (sizeof(GLfloat) * 3) + (sizeof(GLfloat) * 3)); - glVertexAttribPointer(cylinderRaytracingAOOffsetAttribute, 2, GL_FLOAT, 0, bondVBOStride, (char *)NULL + (sizeof(GLfloat) * 3) + (sizeof(GLfloat) * 3) + (sizeof(GLfloat) * 2)); - - // Bind the index buffer and draw to the screen - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bondIndexBufferHandle[currentBondVBOIndex]); - glDrawElements(GL_TRIANGLES, numberOfBondIndicesInBuffer[currentBondVBOIndex], GL_UNSIGNED_SHORT, NULL); - - // Unbind the buffers - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glBindBuffer(GL_ARRAY_BUFFER, 0); - } - } - } - -} - -- (void)renderAmbientOcclusionTextureForModelViewMatrix:(GLfloat *)ambientOcclusionModelViewMatrix inverseMatrix:(GLfloat *)inverseMatrix fractionOfTotal:(GLfloat)fractionOfTotal; -{ - [self switchToAmbientOcclusionFramebuffer]; - - glBlendEquation(GL_FUNC_ADD); - - float sphereScaleFactor = overallMoleculeScaleFactor * currentModelScaleFactor * atomRadiusScaleFactor; - GLsizei atomVBOStride = sizeof(GLfloat) * 3 + sizeof(GLfloat) * 2 + sizeof(GLfloat) * 2; - - // Draw the spheres - [sphereAmbientOcclusionProgram use]; - - glUniformMatrix3fv(sphereAmbientOcclusionInverseModelViewMatrix, 1, 0, inverseMatrix); - - glUniform1i(sphereAmbientOcclusionDepthTexture, 0); - - glUniformMatrix3fv(sphereAmbientOcclusionModelViewMatrix, 1, 0, ambientOcclusionModelViewMatrix); - glUniform1f(sphereAmbientOcclusionTexturePatchWidth, normalizedAOTexturePatchWidth); - glUniform1f(sphereAmbientOcclusionIntensityFactor, fractionOfTotal); - - for (unsigned int currentAtomType = 0; currentAtomType < NUM_ATOMTYPES; currentAtomType++) - { - if (atomIndexBufferHandle[currentAtomType] != 0) - { - glUniform1f(sphereAmbientOcclusionRadius, atomProperties[currentAtomType].atomRadius * sphereScaleFactor); - - // Bind the VBO and attach it to the program - glBindBuffer(GL_ARRAY_BUFFER, atomVertexBufferHandles[currentAtomType]); - glVertexAttribPointer(sphereAmbientOcclusionPositionAttribute, 3, GL_FLOAT, 0, atomVBOStride, (char *)NULL + 0); - glVertexAttribPointer(sphereAmbientOcclusionImpostorSpaceAttribute, 2, GL_FLOAT, 0, atomVBOStride, (char *)NULL + (sizeof(GLfloat) * 3)); - glVertexAttribPointer(sphereAmbientOcclusionAOOffsetAttribute, 2, GL_FLOAT, 0, atomVBOStride, (char *)NULL + (sizeof(GLfloat) * 3) + (sizeof(GLfloat) * 2)); - - // Bind the index buffer and draw to the screen - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, atomIndexBufferHandle[currentAtomType]); - glDrawElements(GL_TRIANGLES, numberOfIndicesInBuffer[currentAtomType], GL_UNSIGNED_SHORT, NULL); - - // Unbind the buffers - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glBindBuffer(GL_ARRAY_BUFFER, 0); - } - } - - - // Draw the cylinders - [cylinderAmbientOcclusionProgram use]; - - glUniformMatrix3fv(cylinderAmbientOcclusionInverseModelViewMatrix, 1, 0, inverseMatrix); - - glUniform1i(cylinderAmbientOcclusionDepthTexture, 0); - - float cylinderScaleFactor = overallMoleculeScaleFactor * currentModelScaleFactor * bondRadiusScaleFactor; - GLsizei bondVBOStride = sizeof(GLfloat) * 3 + sizeof(GLfloat) * 3 + sizeof(GLfloat) * 2 + sizeof(GLfloat) * 2; - GLfloat bondRadius = 1.0; - - glUniform1f(cylinderAmbientOcclusionRadius, bondRadius * cylinderScaleFactor); - glUniformMatrix3fv(cylinderAmbientOcclusionModelViewMatrix, 1, 0, ambientOcclusionModelViewMatrix); - glUniform1f(cylinderAmbientOcclusionTexturePatchWidth, normalizedAOTexturePatchWidth); - glUniform1f(cylinderAmbientOcclusionIntensityFactor, fractionOfTotal); - - for (unsigned int currentBondVBOIndex = 0; currentBondVBOIndex < MAX_BOND_VBOS; currentBondVBOIndex++) - { - // Draw bonds next - if (bondVertexBufferHandle[currentBondVBOIndex] != 0) - { - // Bind the VBO and attach it to the program - glBindBuffer(GL_ARRAY_BUFFER, bondVertexBufferHandle[currentBondVBOIndex]); - glVertexAttribPointer(cylinderAmbientOcclusionPositionAttribute, 3, GL_FLOAT, 0, bondVBOStride, (char *)NULL + 0); - glVertexAttribPointer(cylinderAmbientOcclusionDirectionAttribute, 3, GL_FLOAT, 0, bondVBOStride, (char *)NULL + (sizeof(GLfloat) * 3)); - glVertexAttribPointer(cylinderAmbientOcclusionImpostorSpaceAttribute, 2, GL_FLOAT, 0, bondVBOStride, (char *)NULL + (sizeof(GLfloat) * 3) + (sizeof(GLfloat) * 3)); - glVertexAttribPointer(cylinderAmbientOcclusionAOOffsetAttribute, 2, GL_FLOAT, 0, bondVBOStride, (char *)NULL + (sizeof(GLfloat) * 3) + (sizeof(GLfloat) * 3) + (sizeof(GLfloat) * 2)); - - // Bind the index buffer and draw to the screen - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bondIndexBufferHandle[currentBondVBOIndex]); - glDrawElements(GL_TRIANGLES, numberOfBondIndicesInBuffer[currentBondVBOIndex], GL_UNSIGNED_SHORT, NULL); - - // Unbind the buffers - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); - glBindBuffer(GL_ARRAY_BUFFER, 0); - } - } -} - -/* -#define AMBIENTOCCLUSIONSAMPLINGPOINTS 6 - -static float ambientOcclusionRotationAngles[AMBIENTOCCLUSIONSAMPLINGPOINTS][2] = -{ - {0.0, 0.0}, - {M_PI / 2.0, 0.0}, - {M_PI, 0.0}, - {3.0 * M_PI / 2.0, 0.0}, - {0.0, M_PI / 2.0}, - {0.0, 3.0 * M_PI / 2.0} -}; - - */ - -/* -#define AMBIENTOCCLUSIONSAMPLINGPOINTS 14 - -static float ambientOcclusionRotationAngles[AMBIENTOCCLUSIONSAMPLINGPOINTS][2] = -{ - {0.0, 0.0}, - {M_PI / 2.0, 0.0}, - {M_PI, 0.0}, - {3.0 * M_PI / 2.0, 0.0}, - {0.0, M_PI / 2.0}, - {0.0, 3.0 * M_PI / 2.0}, - - {M_PI / 4.0, M_PI / 4.0}, - {3.0 * M_PI / 4.0, M_PI / 4.0}, - {5.0 * M_PI / 4.0, M_PI / 4.0}, - {7.0 * M_PI / 4.0, M_PI / 4.0}, - - {M_PI / 4.0, 7.0 * M_PI / 4.0}, - {3.0 * M_PI / 4.0, 7.0 * M_PI / 4.0}, - {5.0 * M_PI / 4.0, 7.0 * M_PI / 4.0}, - {7.0 * M_PI / 4.0, 7.0 * M_PI / 4.0}, -}; -*/ - -#define AMBIENTOCCLUSIONSAMPLINGPOINTS 22 - -static float ambientOcclusionRotationAngles[AMBIENTOCCLUSIONSAMPLINGPOINTS][2] = -{ - {0.0, 0.0}, - {M_PI / 2.0, 0.0}, - {M_PI, 0.0}, - {3.0 * M_PI / 2.0, 0.0}, - {0.0, M_PI / 2.0}, - {0.0, 3.0 * M_PI / 2.0}, - - {M_PI / 4.0, M_PI / 4.0}, - {3.0 * M_PI / 4.0, M_PI / 4.0}, - {5.0 * M_PI / 4.0, M_PI / 4.0}, - {7.0 * M_PI / 4.0, M_PI / 4.0}, - - {M_PI / 4.0, 7.0 * M_PI / 4.0}, - {3.0 * M_PI / 4.0, 7.0 * M_PI / 4.0}, - {5.0 * M_PI / 4.0, 7.0 * M_PI / 4.0}, - {7.0 * M_PI / 4.0, 7.0 * M_PI / 4.0}, - - {M_PI / 4.0, 0.0}, - {3.0 * M_PI / 4.0, 0.0}, - {5.0 * M_PI / 4.0, 0.0}, - {7.0 * M_PI / 4.0, 0.0}, - - {0.0, M_PI / 4.0}, - {0.0, 3.0 * M_PI / 4.0}, - {0.0, 5.0 * M_PI / 4.0}, - {0.0, 7.0 * M_PI / 4.0}, -}; - -/* -#define AMBIENTOCCLUSIONSAMPLINGPOINTS 1 - -static float ambientOcclusionRotationAngles[AMBIENTOCCLUSIONSAMPLINGPOINTS][2] = -{ - {0.0, 0.0}, -}; - */ - -- (void)prepareAmbientOcclusionMap; -{ - dispatch_semaphore_wait(frameRenderingSemaphore, DISPATCH_TIME_FOREVER); - - dispatch_sync(openGLESContextQueue, ^{ - [EAGLContext setCurrentContext:context]; - - if (isRenderingCancelled) - { - dispatch_semaphore_signal(frameRenderingSemaphore); - return; - } - - // Use bilinear filtering here to smooth out the ambient occlusion shadowing - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, depthPassTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); - - GLfloat zeroTranslation[3] = {0.0, 0.0, 0.0}; - - -// CFTimeInterval previousTimestamp = CFAbsoluteTimeGetCurrent(); - - // Start fresh on the ambient texture - [self switchToAmbientOcclusionFramebuffer]; - - BOOL disableAOTextureGeneration = NO; - - if (disableAOTextureGeneration) - { - glClearColor(0.5f, 0.5f, 0.5f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); - } - else - { - // glClearColor(0.0f, ambientOcclusionModelViewMatrix[0], 1.0f, 1.0f); - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); - - CATransform3D currentSamplingRotationMatrix; - GLfloat currentModelViewMatrix[9]; - CATransform3D inverseMatrix; - GLfloat inverseModelViewMatrix[9]; - - for (unsigned int currentAOSamplingPoint = 0; currentAOSamplingPoint < AMBIENTOCCLUSIONSAMPLINGPOINTS; currentAOSamplingPoint++) - { - if (isRenderingCancelled) - { - dispatch_semaphore_signal(frameRenderingSemaphore); - return; - } - - float theta = ambientOcclusionRotationAngles[currentAOSamplingPoint][0]; - float phi = ambientOcclusionRotationAngles[currentAOSamplingPoint][1]; - - currentSamplingRotationMatrix = CATransform3DMakeRotation(theta, 1.0, 0.0, 0.0); - currentSamplingRotationMatrix = CATransform3DRotate(currentSamplingRotationMatrix, phi, 0.0, 1.0, 0.0); - - inverseMatrix = CATransform3DInvert(currentSamplingRotationMatrix); - - [self convert3DTransform:&inverseMatrix to3x3Matrix:inverseModelViewMatrix]; - [self convert3DTransform:¤tSamplingRotationMatrix to3x3Matrix:currentModelViewMatrix]; - - [self renderDepthTextureForModelViewMatrix:currentModelViewMatrix translation:zeroTranslation scale:1.0]; - [self renderAmbientOcclusionTextureForModelViewMatrix:currentModelViewMatrix inverseMatrix:inverseModelViewMatrix fractionOfTotal:(0.5 / (GLfloat)AMBIENTOCCLUSIONSAMPLINGPOINTS)]; - // [self renderAmbientOcclusionTextureForModelViewMatrix:currentModelViewMatrix inverseMatrix:inverseModelViewMatrix fractionOfTotal:(1.0 / (GLfloat)AMBIENTOCCLUSIONSAMPLINGPOINTS)]; - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:kSLSMoleculeRenderingUpdateNotification object:[NSNumber numberWithFloat:((float)currentAOSamplingPoint * 2.0) / ((float)AMBIENTOCCLUSIONSAMPLINGPOINTS * 2.0)] ]; - }); - - theta = theta + M_PI / 8.0; - phi = phi + M_PI / 8.0; - - currentSamplingRotationMatrix = CATransform3DMakeRotation(theta, 1.0, 0.0, 0.0); - currentSamplingRotationMatrix = CATransform3DRotate(currentSamplingRotationMatrix, phi, 0.0, 1.0, 0.0); - - inverseMatrix = CATransform3DInvert(currentSamplingRotationMatrix); - - [self convert3DTransform:&inverseMatrix to3x3Matrix:inverseModelViewMatrix]; - [self convert3DTransform:¤tSamplingRotationMatrix to3x3Matrix:currentModelViewMatrix]; - - [self renderDepthTextureForModelViewMatrix:currentModelViewMatrix translation:zeroTranslation scale:1.0]; - [self renderAmbientOcclusionTextureForModelViewMatrix:currentModelViewMatrix inverseMatrix:inverseModelViewMatrix fractionOfTotal:(1.5 / (GLfloat)AMBIENTOCCLUSIONSAMPLINGPOINTS)]; - - dispatch_async(dispatch_get_main_queue(), ^{ - [[NSNotificationCenter defaultCenter] postNotificationName:kSLSMoleculeRenderingUpdateNotification object:[NSNumber numberWithFloat:((float)currentAOSamplingPoint * 2.0 + 1.0) / ((float)AMBIENTOCCLUSIONSAMPLINGPOINTS * 2.0)] ]; - }); - } - -// CFTimeInterval frameDuration = CFAbsoluteTimeGetCurrent() - previousTimestamp; - -// NSLog(@"Ambient occlusion calculation duration: %f s", frameDuration); - } - - // Reset depth texture to nearest filtering to prevent some border transparency artifacts - glActiveTexture(GL_TEXTURE0); - glBindTexture(GL_TEXTURE_2D, depthPassTexture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST ); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); - - dispatch_semaphore_signal(frameRenderingSemaphore); - }); -} - -- (void)precalculateAOLookupTextureForInverseMatrix:(GLfloat *)inverseMatrix; -{ - [self switchToAOLookupFramebuffer]; - - glDisable(GL_DEPTH_TEST); - glDisable(GL_BLEND); - glBlendEquation(GL_MAX_EXT); - -// glBlendEquation(GL_FUNC_ADD); - - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - // Draw the spheres - [sphereAOLookupPrecalculationProgram use]; - - glUniformMatrix3fv(sphereAOLookupInverseModelViewMatrix, 1, 0, inverseMatrix); - - static const GLfloat textureCoordinates[] = { - -1.0f, -1.0f, - 1.0f, -1.0f, - -1.0f, 1.0f, - 1.0f, 1.0f, - }; - - glVertexAttribPointer(sphereAOLookupImpostorSpaceAttribute, 2, GL_FLOAT, 0, 0, textureCoordinates); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - glEnable(GL_BLEND); - glEnable(GL_DEPTH_TEST); -} - -- (void)displayTextureToScreen:(GLuint)textureToDisplay; -{ - [self switchToDisplayFramebuffer]; - glDisable(GL_DEPTH_TEST); - glBlendEquation(GL_MAX_EXT); - glDepthMask(GL_FALSE); - glEnable(GL_BLEND); - - [passthroughProgram use]; - - glClearColor(0.0f, 0.0f, 0.0f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - - static const GLfloat squareVertices[] = { - -1.0f, -1.0f, - 1.0f, -1.0f, - -1.0f, 1.0f, - 1.0f, 1.0f, - }; - - static const GLfloat textureCoordinates[] = { - 0.0f, 0.0f, - 1.0f, 0.0f, - 0.0f, 1.0f, - 1.0f, 1.0f, - }; - - glActiveTexture(GL_TEXTURE4); - glBindTexture(GL_TEXTURE_2D, textureToDisplay); - glUniform1i(passthroughTexture, 4); - - glVertexAttribPointer(passthroughPositionAttribute, 2, GL_FLOAT, 0, 0, squareVertices); - glVertexAttribPointer(passthroughTextureCoordinateAttribute, 2, GL_FLOAT, 0, 0, textureCoordinates); - - glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); - - glEnable(GL_DEPTH_TEST); -} - -@end diff --git a/SLSOpenGLESRenderer.h b/SLSOpenGLESRenderer.h deleted file mode 100644 index c0e532f..0000000 --- a/SLSOpenGLESRenderer.h +++ /dev/null @@ -1,151 +0,0 @@ -// -// SLSOpenGLESRenderer.h -// Molecules -// -// Created by Brad Larson on 4/12/2011. -// Copyright 2011 Sunset Lake Software LLC. All rights reserved. -// - -#import -#import - -#import -#import -#import -#import -#import - -#import "SLSMolecule.h" - -extern NSString *const kSLSMoleculeShadowCalculationStartedNotification; -extern NSString *const kSLSMoleculeShadowCalculationUpdateNotification; -extern NSString *const kSLSMoleculeShadowCalculationEndedNotification; - -#define MAX_BOND_VBOS 2 - -// OpenGL helper functions -void normalize(GLfloat *v); - -typedef struct { - GLubyte redComponent; - GLubyte greenComponent; - GLubyte blueComponent; - GLfloat atomRadius; -} SLSAtomProperties; - -// van der Waals radius used here -// http://www.umass.edu/microbio/rasmol/rasbonds.htm - -static const SLSAtomProperties atomProperties[NUM_ATOMTYPES] = { - {120, 120, 120, 1.55f}, // CARBON - {230, 230, 230, 1.10f}, // HYDROGEN - {240, 40, 40, 1.35f}, // OXYGEN - { 48, 80, 248, 1.40f}, // NITROGEN - {255, 255, 48, 1.81f}, // SULFUR - {255, 128, 0, 1.88f}, // PHOSPHOROUS - {224, 102, 51, 1.95f}, // IRON - { 0, 255, 0, 1.50f}, // UNKNOWN - {200, 200, 90, 1.50f}, // SILICON - {144, 224, 80, 1.47f}, // FLUORINE - { 31, 240, 31, 1.75f}, // CHLORINE - {166, 41, 41, 1.85f}, // BROMINE - {148, 0, 148, 1.75f}, // IODINE - { 61, 255, 0, 1.95f}, // CALCIUM - {125, 128, 176, 1.15f}, // ZINC - {255, 217, 143, 1.75f}, // CADMIUM - {171, 92, 242, 1.02f}, // SODIUM - {138, 255, 0, 0.72f}, // MAGNESIUM -}; - -@interface SLSOpenGLESRenderer : NSObject -{ - GLint backingWidth; - GLint backingHeight; - - CATransform3D currentCalculatedMatrix; - BOOL isFirstDrawingOfMolecule, isFrameRenderingFinished, isSceneReady, isRenderingCancelled; - - float atomRadiusScaleFactor, bondRadiusScaleFactor, overallMoleculeScaleFactor; - float currentModelScaleFactor; - - EAGLContext *context; - - GLuint viewRenderbuffer, viewFramebuffer, viewDepthBuffer; - - - // OpenGL performance tuning statistics - NSInteger totalNumberOfVertices, totalNumberOfTriangles; - - // Binned atom types - // 16384 atoms per indexed VBO per atom type - // 16384 bonds per indexed VBO - NSMutableData *atomVBOs[NUM_ATOMTYPES], *atomIndexBuffers[NUM_ATOMTYPES]; - GLuint atomVertexBufferHandles[NUM_ATOMTYPES], atomIndexBufferHandle[NUM_ATOMTYPES], numberOfIndicesInBuffer[NUM_ATOMTYPES]; - GLuint bondVertexBufferHandle[MAX_BOND_VBOS], bondIndexBufferHandle[MAX_BOND_VBOS], numberOfBondIndicesInBuffer[MAX_BOND_VBOS]; - unsigned int numberOfAtomVertices[NUM_ATOMTYPES], numberOfBondVertices[MAX_BOND_VBOS], numberOfAtomIndices[NUM_ATOMTYPES], numberOfBondIndices[MAX_BOND_VBOS]; - - NSMutableData *bondVBOs[MAX_BOND_VBOS], *bondIndexBuffers[MAX_BOND_VBOS]; - unsigned int currentBondVBO, currentAtomVBO; - - dispatch_queue_t openGLESContextQueue; - dispatch_semaphore_t frameRenderingSemaphore; -} - -@property(readwrite, strong, nonatomic) EAGLContext *context; -@property(readonly) BOOL isFrameRenderingFinished, isSceneReady; -@property(readonly) NSInteger totalNumberOfVertices, totalNumberOfTriangles; -@property(readwrite, nonatomic) float atomRadiusScaleFactor, bondRadiusScaleFactor, overallMoleculeScaleFactor; -@property(readonly) dispatch_queue_t openGLESContextQueue; - -// Initialization and teardown -- (id)initWithContext:(EAGLContext *)newContext; - -// OpenGL matrix helper methods -- (void)convertMatrix:(GLfloat *)matrix to3DTransform:(CATransform3D *)transform3D; -- (void)convert3DTransform:(CATransform3D *)transform3D toMatrix:(GLfloat *)matrix; -- (void)convert3DTransform:(CATransform3D *)transform3D to3x3Matrix:(GLfloat *)matrix; -- (void)print3DTransform:(CATransform3D *)transform3D; -- (void)printMatrix:(GLfloat *)fixedPointMatrix; -- (void)apply3DTransform:(CATransform3D *)transform3D toPoint:(GLfloat *)sourcePoint result:(GLfloat *)resultingPoint; - -// Model manipulation -- (void)rotateModelFromScreenDisplacementInX:(float)xRotation inY:(float)yRotation; -- (void)scaleModelByFactor:(float)scaleFactor; -- (void)translateModelByScreenDisplacementInX:(float)xTranslation inY:(float)yTranslation; -- (void)resetModelViewMatrix; - -// OpenGL drawing support -- (BOOL)createFramebuffersForLayer:(CAEAGLLayer *)glLayer; -- (void)destroyFramebuffers; -- (void)configureLighting; -- (void)clearScreen; -- (void)startDrawingFrame; -- (void)configureProjection; -- (void)presentRenderBuffer; -- (void)suspendRenderingDuringRotation; -- (void)resumeRenderingDuringRotation; - -// Actual OpenGL rendering -- (void)renderFrameForMolecule:(SLSMolecule *)molecule; - -// Molecule 3-D geometry generation -- (void)configureBasedOnNumberOfAtoms:(NSUInteger)numberOfAtoms numberOfBonds:(NSUInteger)numberOfBonds; -- (void)addVertex:(GLfloat *)newVertex forAtomType:(SLSAtomType)atomType; -- (void)addIndex:(GLushort *)newIndex forAtomType:(SLSAtomType)atomType; -- (void)addIndices:(GLushort *)newIndices size:(unsigned int)numIndices forAtomType:(SLSAtomType)atomType; -- (void)addBondVertex:(GLfloat *)newVertex; -- (void)addBondIndex:(GLushort *)newIndex; -- (void)addBondIndices:(GLushort *)newIndices size:(unsigned int)numIndices; -- (void)addAtomToVertexBuffers:(SLSAtomType)atomType atPoint:(SLS3DPoint)newPoint; -- (void)addBondToVertexBuffersWithStartPoint:(SLS3DPoint)startPoint endPoint:(SLS3DPoint)endPoint bondColor:(GLubyte *)bondColor bondType:(SLSBondType)bondType; - -// OpenGL drawing routines -- (void)bindVertexBuffersForMolecule; -- (void)drawMolecule; -- (void)freeVertexBuffers; -- (void)initiateMoleculeRendering; -- (void)terminateMoleculeRendering; -- (void)cancelMoleculeRendering; -- (void)waitForLastFrameToFinishRendering; - -@end diff --git a/SLSOpenGLESRenderer.m b/SLSOpenGLESRenderer.m deleted file mode 100644 index 36ee917..0000000 --- a/SLSOpenGLESRenderer.m +++ /dev/null @@ -1,546 +0,0 @@ -// -// SLSOpenGLESRenderer.m -// Molecules -// -// Created by Brad Larson on 4/12/2011. -// Copyright 2011 Sunset Lake Software LLC. All rights reserved. -// - -#import "SLSOpenGLESRenderer.h" - -NSString *const kSLSMoleculeShadowCalculationStartedNotification = @"MoleculeShadowCalculationStarted"; -NSString *const kSLSMoleculeShadowCalculationUpdateNotification = @"MoleculeShadowCalculationUpdate"; -NSString *const kSLSMoleculeShadowCalculationEndedNotification = @"MoleculeShadowCalculationEnded"; - -@implementation SLSOpenGLESRenderer - -#pragma mark - -#pragma mark Initialization and teardown - -- (id)initWithContext:(EAGLContext *)newContext; -{ - if (!(self = [super init])) - { - return nil; - } - - self.context = newContext; - - isSceneReady = NO; - - // Set up the initial model view matrix for the rendering - isFirstDrawingOfMolecule = YES; - isFrameRenderingFinished = YES; - totalNumberOfVertices = 0; - totalNumberOfTriangles = 0; - currentModelScaleFactor = 1.0; - - GLfloat currentModelViewMatrix[16] = {0.402560,0.094840,0.910469,0.000000, 0.913984,-0.096835,-0.394028,0.000000, 0.050796,0.990772,-0.125664,0.000000, 0.000000,0.000000,0.000000,1.000000}; - - // GLfloat currentModelViewMatrix[16] = {1.0, 0, 0, 0, 0, 1.0, 0, 0, 0, 0, 1.0, 0, 0, 0, 0, 1.0}; - - [self convertMatrix:currentModelViewMatrix to3DTransform:¤tCalculatedMatrix]; - - openGLESContextQueue = dispatch_queue_create("com.sunsetlakesoftware.openGLESContextQueue", NULL);; - frameRenderingSemaphore = dispatch_semaphore_create(1); - -// [self clearScreen]; - - return self; -} - -- (void)dealloc -{ - // // Read the current modelview matrix from OpenGL and save it in the user's preferences for recovery on next startup - // // TODO: save index, vertex, and normal buffers for quick reload later - // float currentModelViewMatrix[16]; - // glMatrixMode(GL_MODELVIEW); - // glGetFloatv(GL_MODELVIEW_MATRIX, currentModelViewMatrix); - // NSData *matrixData = [NSData dataWithBytes:currentModelViewMatrix length:(16 * sizeof(float))]; - // [[NSUserDefaults standardUserDefaults] setObject:matrixData forKey:@"matrixData"]; - // - if ([EAGLContext currentContext] == context) - { - [EAGLContext setCurrentContext:nil]; - } -} - -#pragma mark - -#pragma mark OpenGL matrix helper methods - -- (void)convertMatrix:(GLfloat *)matrix to3DTransform:(CATransform3D *)transform3D; -{ - transform3D->m11 = (CGFloat)matrix[0]; - transform3D->m12 = (CGFloat)matrix[1]; - transform3D->m13 = (CGFloat)matrix[2]; - transform3D->m14 = (CGFloat)matrix[3]; - transform3D->m21 = (CGFloat)matrix[4]; - transform3D->m22 = (CGFloat)matrix[5]; - transform3D->m23 = (CGFloat)matrix[6]; - transform3D->m24 = (CGFloat)matrix[7]; - transform3D->m31 = (CGFloat)matrix[8]; - transform3D->m32 = (CGFloat)matrix[9]; - transform3D->m33 = (CGFloat)matrix[10]; - transform3D->m34 = (CGFloat)matrix[11]; - transform3D->m41 = (CGFloat)matrix[12]; - transform3D->m42 = (CGFloat)matrix[13]; - transform3D->m43 = (CGFloat)matrix[14]; - transform3D->m44 = (CGFloat)matrix[15]; -} - -- (void)convert3DTransform:(CATransform3D *)transform3D toMatrix:(GLfloat *)matrix; -{ - // struct CATransform3D - // { - // CGFloat m11, m12, m13, m14; - // CGFloat m21, m22, m23, m24; - // CGFloat m31, m32, m33, m34; - // CGFloat m41, m42, m43, m44; - // }; - - matrix[0] = (GLfloat)transform3D->m11; - matrix[1] = (GLfloat)transform3D->m12; - matrix[2] = (GLfloat)transform3D->m13; - matrix[3] = (GLfloat)transform3D->m14; - matrix[4] = (GLfloat)transform3D->m21; - matrix[5] = (GLfloat)transform3D->m22; - matrix[6] = (GLfloat)transform3D->m23; - matrix[7] = (GLfloat)transform3D->m24; - matrix[8] = (GLfloat)transform3D->m31; - matrix[9] = (GLfloat)transform3D->m32; - matrix[10] = (GLfloat)transform3D->m33; - matrix[11] = (GLfloat)transform3D->m34; - matrix[12] = (GLfloat)transform3D->m41; - matrix[13] = (GLfloat)transform3D->m42; - matrix[14] = (GLfloat)transform3D->m43; - matrix[15] = (GLfloat)transform3D->m44; -} - -- (void)convert3DTransform:(CATransform3D *)transform3D to3x3Matrix:(GLfloat *)matrix; -{ - matrix[0] = (GLfloat)transform3D->m11; - matrix[1] = (GLfloat)transform3D->m12; - matrix[2] = (GLfloat)transform3D->m13; - matrix[3] = (GLfloat)transform3D->m21; - matrix[4] = (GLfloat)transform3D->m22; - matrix[5] = (GLfloat)transform3D->m23; - matrix[6] = (GLfloat)transform3D->m31; - matrix[7] = (GLfloat)transform3D->m32; - matrix[8] = (GLfloat)transform3D->m33; -} - -- (void)print3DTransform:(CATransform3D *)transform3D; -{ - NSLog(@"___________________________"); - NSLog(@"|%f,%f,%f,%f|", transform3D->m11, transform3D->m12, transform3D->m13, transform3D->m14); - NSLog(@"|%f,%f,%f,%f|", transform3D->m21, transform3D->m22, transform3D->m23, transform3D->m24); - NSLog(@"|%f,%f,%f,%f|", transform3D->m31, transform3D->m32, transform3D->m33, transform3D->m34); - NSLog(@"|%f,%f,%f,%f|", transform3D->m41, transform3D->m42, transform3D->m43, transform3D->m44); - NSLog(@"___________________________"); -} - -- (void)printMatrix:(GLfloat *)matrix; -{ - NSLog(@"___________________________"); - NSLog(@"|%f,%f,%f,%f|", matrix[0], matrix[1], matrix[2], matrix[3]); - NSLog(@"|%f,%f,%f,%f|", matrix[4], matrix[5], matrix[6], matrix[7]); - NSLog(@"|%f,%f,%f,%f|", matrix[8], matrix[9], matrix[10], matrix[11]); - NSLog(@"|%f,%f,%f,%f|", matrix[12], matrix[13], matrix[14], matrix[15]); - NSLog(@"___________________________"); -} - -- (void)apply3DTransform:(CATransform3D *)transform3D toPoint:(GLfloat *)sourcePoint result:(GLfloat *)resultingPoint; -{ -// | A B C D | -// M = | E F G H | -// | I J K L | -// | M N O P | - -// A.x1+B.y1+C.z1+D -// E.x1+F.y1+G.z1+H -// I.x1+J.y1+K.z1+L -// M.x1+N.y1+O.z1+P - - resultingPoint[0] = sourcePoint[0] * transform3D->m11 + sourcePoint[1] * transform3D->m12 + sourcePoint[2] * transform3D->m13 + transform3D->m14; - resultingPoint[1] = sourcePoint[0] * transform3D->m21 + sourcePoint[1] * transform3D->m22 + sourcePoint[2] * transform3D->m23 + transform3D->m24; - resultingPoint[2] = sourcePoint[0] * transform3D->m31 + sourcePoint[1] * transform3D->m32 + sourcePoint[2] * transform3D->m33 + transform3D->m34; -} - -#pragma mark - -#pragma mark Model manipulation - -- (void)rotateModelFromScreenDisplacementInX:(float)xRotation inY:(float)yRotation; -{ - // Perform incremental rotation based on current angles in X and Y - GLfloat totalRotation = sqrt(xRotation*xRotation + yRotation*yRotation); - - CATransform3D temporaryMatrix = CATransform3DRotate(currentCalculatedMatrix, totalRotation * M_PI / 180.0, - ((xRotation/totalRotation) * currentCalculatedMatrix.m12 + (yRotation/totalRotation) * currentCalculatedMatrix.m11), - ((xRotation/totalRotation) * currentCalculatedMatrix.m22 + (yRotation/totalRotation) * currentCalculatedMatrix.m21), - ((xRotation/totalRotation) * currentCalculatedMatrix.m32 + (yRotation/totalRotation) * currentCalculatedMatrix.m31)); - - if ((temporaryMatrix.m11 >= -100.0) && (temporaryMatrix.m11 <= 100.0)) - { - currentCalculatedMatrix = temporaryMatrix; - } -} - -- (void)scaleModelByFactor:(float)scaleFactor; -{ - // Scale the view to fit current multitouch scaling - CATransform3D temporaryMatrix = CATransform3DScale(currentCalculatedMatrix, scaleFactor, scaleFactor, scaleFactor); - - if ((temporaryMatrix.m11 >= -100.0) && (temporaryMatrix.m11 <= 100.0)) - { - currentCalculatedMatrix = temporaryMatrix; - currentModelScaleFactor = currentModelScaleFactor * scaleFactor; - } -} - -- (void)translateModelByScreenDisplacementInX:(float)xTranslation inY:(float)yTranslation; -{ - float scalingForMovement; - if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) - { - scalingForMovement = 85.0f; - } - else - { - scalingForMovement = 200.0f; - } - - - // Translate the model by the accumulated amount - float currentScaleFactor = sqrt(pow(currentCalculatedMatrix.m11, 2.0f) + pow(currentCalculatedMatrix.m12, 2.0f) + pow(currentCalculatedMatrix.m13, 2.0f)); - - xTranslation = xTranslation * scalingForMovement / (currentScaleFactor * currentScaleFactor); - yTranslation = yTranslation * scalingForMovement / (currentScaleFactor * currentScaleFactor); - - // Use the (0,4,8) components to figure the eye's X axis in the model coordinate system, translate along that - CATransform3D temporaryMatrix = CATransform3DTranslate(currentCalculatedMatrix, xTranslation * currentCalculatedMatrix.m11, xTranslation * currentCalculatedMatrix.m21, xTranslation * currentCalculatedMatrix.m31); - // Use the (1,5,9) components to figure the eye's Y axis in the model coordinate system, translate along that - temporaryMatrix = CATransform3DTranslate(temporaryMatrix, yTranslation * currentCalculatedMatrix.m12, yTranslation * currentCalculatedMatrix.m22, yTranslation * currentCalculatedMatrix.m32); - - if ((temporaryMatrix.m11 >= -100.0) && (temporaryMatrix.m11 <= 100.0)) - { - currentCalculatedMatrix = temporaryMatrix; - } -} - -- (void)resetModelViewMatrix; -{ - GLfloat currentModelViewMatrix[16] = {0.402560,0.094840,0.910469,0.000000, 0.913984,-0.096835,-0.394028,0.000000, 0.050796,0.990772,-0.125664,0.000000, 0.000000,0.000000,0.000000,1.000000}; - [self convertMatrix:currentModelViewMatrix to3DTransform:¤tCalculatedMatrix]; - currentModelScaleFactor = 1.0; - - isFirstDrawingOfMolecule = YES; -} - -#pragma mark - -#pragma mark OpenGL drawing support - -- (BOOL)createFramebuffersForLayer:(CAEAGLLayer *)glLayer; -{ - return YES; -} - -- (void)destroyFramebuffers; -{ -} - -- (void)configureLighting; -{ - NSAssert(NO, @"Method not overridden"); -} - -- (void)clearScreen; -{ - NSAssert(NO, @"Method not overridden"); -} - -- (void)startDrawingFrame; -{ - NSAssert(NO, @"Method not overridden"); -} - -- (void)configureProjection; -{ - NSAssert(NO, @"Method not overridden"); -} - -- (void)presentRenderBuffer; -{ - NSAssert(NO, @"Method not overridden"); -} - -- (void)suspendRenderingDuringRotation; -{ - -} - -- (void)resumeRenderingDuringRotation; -{ - -} - -#pragma mark - -#pragma mark Actual OpenGL rendering - -- (void)renderFrameForMolecule:(SLSMolecule *)molecule; -{ - NSAssert(NO, @"Method not overridden"); -} - -#pragma mark - -#pragma mark Molecule 3-D geometry generation - -- (void)configureBasedOnNumberOfAtoms:(NSUInteger)numberOfAtoms numberOfBonds:(NSUInteger)numberOfBonds; -{ - -} - -- (void)addVertex:(GLfloat *)newVertex forAtomType:(SLSAtomType)atomType; -{ -} - -- (void)addIndex:(GLushort *)newIndex forAtomType:(SLSAtomType)atomType; -{ - if (atomIndexBuffers[atomType] == nil) - { - atomIndexBuffers[atomType] = [[NSMutableData alloc] init]; - } - - [atomIndexBuffers[atomType] appendBytes:newIndex length:sizeof(GLushort)]; - numberOfAtomIndices[atomType]++; -} - -- (void)addIndices:(GLushort *)newIndices size:(unsigned int)numIndices forAtomType:(SLSAtomType)atomType; -{ - if (atomIndexBuffers[atomType] == nil) - { - atomIndexBuffers[atomType] = [[NSMutableData alloc] init]; - } - - [atomIndexBuffers[atomType] appendBytes:newIndices length:(sizeof(GLushort) * numIndices)]; - numberOfAtomIndices[atomType] += numIndices; -} - -- (void)addBondVertex:(GLfloat *)newVertex; -{ -} - -- (void)addBondIndex:(GLushort *)newIndex; -{ - if (bondIndexBuffers[currentBondVBO] == nil) - { - bondIndexBuffers[currentBondVBO] = [[NSMutableData alloc] init]; - } - - [bondIndexBuffers[currentBondVBO] appendBytes:newIndex length:sizeof(GLushort)]; - numberOfBondIndices[currentBondVBO]++; -} - -- (void)addBondIndices:(GLushort *)newIndices size:(unsigned int)numIndices; -{ - if (bondIndexBuffers[currentBondVBO] == nil) - { - bondIndexBuffers[currentBondVBO] = [[NSMutableData alloc] init]; - } - - [bondIndexBuffers[currentBondVBO] appendBytes:newIndices length:(sizeof(GLushort) * numIndices)]; - numberOfBondIndices[currentBondVBO] += numIndices; -} - -- (void)addAtomToVertexBuffers:(SLSAtomType)atomType atPoint:(SLS3DPoint)newPoint; -{ - NSAssert(NO, @"Method not overridden"); -} - -- (void)addBondToVertexBuffersWithStartPoint:(SLS3DPoint)startPoint endPoint:(SLS3DPoint)endPoint bondColor:(GLubyte *)bondColor bondType:(SLSBondType)bondType; -{ - NSAssert(NO, @"Method not overridden"); -} - -#pragma mark - -#pragma mark OpenGL drawing routines - -- (void)addVertexBuffer; -{ - NSAssert(NO, @"Method not overridden"); -} - -- (void)bindVertexBuffersForMolecule; -{ - dispatch_async(openGLESContextQueue, ^{ - [EAGLContext setCurrentContext:context]; - - [self resetModelViewMatrix]; - - isRenderingCancelled = NO; - - for (unsigned int currentAtomIndexBufferIndex = 0; currentAtomIndexBufferIndex < NUM_ATOMTYPES; currentAtomIndexBufferIndex++) - { - if (atomIndexBuffers[currentAtomIndexBufferIndex] != nil) - { - glGenBuffers(1, &atomIndexBufferHandle[currentAtomIndexBufferIndex]); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, atomIndexBufferHandle[currentAtomIndexBufferIndex]); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, [atomIndexBuffers[currentAtomIndexBufferIndex] length], (GLushort *)[atomIndexBuffers[currentAtomIndexBufferIndex] bytes], GL_STATIC_DRAW); - - numberOfIndicesInBuffer[currentAtomIndexBufferIndex] = ((GLuint)[atomIndexBuffers[currentAtomIndexBufferIndex] length] / (GLuint)sizeof(GLushort)); - - // Now that the data are in the OpenGL buffer, can release the NSData - atomIndexBuffers[currentAtomIndexBufferIndex] = nil; - } - else - { - atomIndexBufferHandle[currentAtomIndexBufferIndex] = 0; - } - } - - for (unsigned int currentAtomVBOIndex = 0; currentAtomVBOIndex < NUM_ATOMTYPES; currentAtomVBOIndex++) - { - if (atomVBOs[currentAtomVBOIndex] != nil) - { - glGenBuffers(1, &atomVertexBufferHandles[currentAtomVBOIndex]); - glBindBuffer(GL_ARRAY_BUFFER, atomVertexBufferHandles[currentAtomVBOIndex]); - glBufferData(GL_ARRAY_BUFFER, [atomVBOs[currentAtomVBOIndex] length], (void *)[atomVBOs[currentAtomVBOIndex] bytes], GL_STATIC_DRAW); - - atomVBOs[currentAtomVBOIndex] = nil; - } - else - { - atomVertexBufferHandles[currentAtomVBOIndex] = 0; - } - } - - for (unsigned int currentBondVBOIndex = 0; currentBondVBOIndex < MAX_BOND_VBOS; currentBondVBOIndex++) - { - if (bondVBOs[currentBondVBOIndex] != nil) - { - glGenBuffers(1, &bondIndexBufferHandle[currentBondVBOIndex]); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, bondIndexBufferHandle[currentBondVBOIndex]); - glBufferData(GL_ELEMENT_ARRAY_BUFFER, [bondIndexBuffers[currentBondVBOIndex] length], (GLushort *)[bondIndexBuffers[currentBondVBOIndex] bytes], GL_STATIC_DRAW); - - numberOfBondIndicesInBuffer[currentBondVBOIndex] = ((GLuint)[bondIndexBuffers[currentBondVBOIndex] length] / (GLuint)sizeof(GLushort)); - - bondIndexBuffers[currentBondVBOIndex] = nil; - - glGenBuffers(1, &bondVertexBufferHandle[currentBondVBOIndex]); - glBindBuffer(GL_ARRAY_BUFFER, bondVertexBufferHandle[currentBondVBOIndex]); - glBufferData(GL_ARRAY_BUFFER, [bondVBOs[currentBondVBOIndex] length], (void *)[bondVBOs[currentBondVBOIndex] bytes], GL_STATIC_DRAW); - - bondVBOs[currentBondVBOIndex] = nil; - } - } - }); -} - -- (void)drawMolecule; -{ - NSAssert(NO, @"Method not overridden"); -} - -- (void)freeVertexBuffers; -{ - dispatch_async(openGLESContextQueue, ^{ - [EAGLContext setCurrentContext:context]; - - isSceneReady = NO; - - for (unsigned int currentAtomType = 0; currentAtomType < NUM_ATOMTYPES; currentAtomType++) - { - if (atomIndexBufferHandle[currentAtomType] != 0) - { - glDeleteBuffers(1, &atomIndexBufferHandle[currentAtomType]); - glDeleteBuffers(1, &atomVertexBufferHandles[currentAtomType]); - - atomIndexBufferHandle[currentAtomType] = 0; - atomVertexBufferHandles[currentAtomType] = 0; - } - } - if (bondVertexBufferHandle != 0) - { - for (unsigned int currentBondVBOIndex = 0; currentBondVBOIndex < MAX_BOND_VBOS; currentBondVBOIndex++) - { - if (bondIndexBufferHandle[currentBondVBOIndex] != 0) - { - glDeleteBuffers(1, &bondVertexBufferHandle[currentBondVBOIndex]); - glDeleteBuffers(1, &bondIndexBufferHandle[currentBondVBOIndex]); - } - - bondVertexBufferHandle[currentBondVBOIndex] = 0; - bondIndexBufferHandle[currentBondVBOIndex] = 0; - } - } - - totalNumberOfTriangles = 0; - totalNumberOfVertices = 0; - }); -} - -- (void)initiateMoleculeRendering; -{ - for (unsigned int currentAtomTypeIndex = 0; currentAtomTypeIndex < NUM_ATOMTYPES; currentAtomTypeIndex++) - { - numberOfAtomVertices[currentAtomTypeIndex] = 0; - numberOfAtomIndices[currentAtomTypeIndex] = 0; - } - - for (unsigned int currentBondVBOIndex = 0; currentBondVBOIndex < MAX_BOND_VBOS; currentBondVBOIndex++) - { - numberOfBondVertices[currentBondVBOIndex] = 0; - numberOfBondIndices[currentBondVBOIndex] = 0; - } - - currentBondVBO = 0; - currentAtomVBO = 0; -} - -- (void)terminateMoleculeRendering; -{ - // Release all the NSData arrays that were partially generated - for (unsigned int currentVBOIndex = 0; currentVBOIndex < NUM_ATOMTYPES; currentVBOIndex++) - { - if (atomVBOs[currentVBOIndex] != nil) - { - atomVBOs[currentVBOIndex] = nil; - } - } - - for (unsigned int currentIndexBufferIndex = 0; currentIndexBufferIndex < NUM_ATOMTYPES; currentIndexBufferIndex++) - { - if (atomIndexBuffers[currentIndexBufferIndex] != nil) - { - atomIndexBuffers[currentIndexBufferIndex] = nil; - } - } - - for (unsigned int currentBondVBOIndex = 0; currentBondVBOIndex < MAX_BOND_VBOS; currentBondVBOIndex++) - { - bondVBOs[currentBondVBOIndex] = nil; - - bondIndexBuffers[currentBondVBOIndex] = nil; - } -} - -- (void)cancelMoleculeRendering; -{ - isRenderingCancelled = YES; -} - -- (void)waitForLastFrameToFinishRendering; -{ - dispatch_semaphore_wait(frameRenderingSemaphore, DISPATCH_TIME_FOREVER); - dispatch_semaphore_signal(frameRenderingSemaphore); -} - -#pragma mark - -#pragma mark Accessors - -@synthesize context; -@synthesize isFrameRenderingFinished, isSceneReady; -@synthesize totalNumberOfVertices, totalNumberOfTriangles; -@synthesize atomRadiusScaleFactor, bondRadiusScaleFactor, overallMoleculeScaleFactor; -@synthesize openGLESContextQueue; - -@end diff --git a/SLSTextViewController.h b/SLSTextViewController.h deleted file mode 100755 index 2a06b83..0000000 --- a/SLSTextViewController.h +++ /dev/null @@ -1,21 +0,0 @@ -// -// SLSTextViewController.h -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 6/30/2008. -// -// This class is based on Apple's example from the Recipes sample application, with only minor modifications - -#import - -@interface SLSTextViewController : UITableViewController -{ - NSString *content; -} - -- (id)initWithTitle:(NSString *)newTitle andContent:(NSString *)newContent; - -@end - diff --git a/SLSTextViewController.m b/SLSTextViewController.m deleted file mode 100755 index d12a5b0..0000000 --- a/SLSTextViewController.m +++ /dev/null @@ -1,106 +0,0 @@ -// -// SLSTextViewController.m -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 6/30/2008. -// -// This class is based on Apple's example from the Recipes sample application, with only minor modifications - -#import "SLSTextViewController.h" -#import "SLSCellTextView.h" - -@implementation SLSTextViewController - -#define kUITextViewCellRowHeight 390.0f - -- (id)initWithTitle:(NSString *)newTitle andContent:(NSString *)newContent; -{ - if ((self = [super initWithStyle:UITableViewStyleGrouped])) - { - // this title will appear in the navigation bar - self.title = newTitle; - content = newContent; - } - - return self; -} - - -- (UITextView *)create_UITextView -{ - CGRect frame = CGRectMake(0.0f, 0.0f, 100.0f, 390.0f); - - UITextView *textView = [[UITextView alloc] initWithFrame:frame]; - textView.textColor = [UIColor blackColor]; - textView.font = [UIFont fontWithName:@"Arial" size:18.0]; - textView.backgroundColor = [UIColor whiteColor]; - textView.showsVerticalScrollIndicator = YES; - - textView.editable = NO; - textView.text = content; - - // note: for UITextView, if you don't like autocompletion while typing use: - // myTextView.autocorrectionType = UITextAutocorrectionTypeNo; - - return textView; -} - -#pragma mark - UITableView delegates - -// if you want the entire table to just be re-orderable then just return UITableViewCellEditingStyleNone -// -- (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSIndexPath *)indexPath -{ - return UITableViewCellEditingStyleNone; -} - -- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView -{ - return 1; -} - -//- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section -//{ -// return @"UITextView"; -//} - -- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section -{ - return 1; -} - -// to determine specific row height for each cell, override this. In this example, each row is determined -// buy the its subviews that are embedded. -// -- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath -{ - return kUITextViewCellRowHeight; -} - -// to determine which UITableViewCell to be used on a given row. -// -- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath -{ - UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kSLSCellTextView_ID]; - - if (cell == nil) - { -// cell = [[[SLSCellTextView alloc] initWithFrame:CGRectZero reuseIdentifier:kSLSCellTextView_ID] autorelease]; - cell = [[SLSCellTextView alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:kSLSCellTextView_ID]; - } - - // this cell hosts the UISwitch control - ((SLSCellTextView *)cell).view = [self create_UITextView]; - - return cell; -} - -- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation -{ - return YES; -} - -@end - diff --git a/Shaders/CylinderAmbientOcclusion.fsh b/Shaders/CylinderAmbientOcclusion.fsh deleted file mode 100644 index bd13cce..0000000 --- a/Shaders/CylinderAmbientOcclusion.fsh +++ /dev/null @@ -1,71 +0,0 @@ -precision mediump float; - -uniform sampler2D depthTexture; -uniform mediump mat3 modelViewProjMatrix; -uniform mediump mat3 inverseModelViewProjMatrix; -uniform mediump float intensityFactor; - -varying mediump vec2 impostorSpaceCoordinate; -varying mediump vec3 normalizedStartingCoordinate; -varying mediump vec3 normalizedEndingCoordinate; -varying mediump float halfCylinderRadius; -varying mediump vec3 adjustmentForOrthographicProjection; - -const mediump float oneThird = 1.0 / 3.0; - -mediump float depthFromEncodedColor(mediump vec4 encodedColor) -{ - return oneThird * (encodedColor.r + encodedColor.g + encodedColor.b); - // return encodedColor.r; -} - - -// X and Y are from -0.5 .. 0.5, Z is from -1.0 .. 1.0 - -mediump vec3 coordinateFromTexturePosition(mediump vec2 texturePosition) -{ - float halfS = texturePosition.s / 2.0; - - if (texturePosition.s >= 0.0) - { - return vec3(1.0 - abs(2.0 * texturePosition.s - 1.0), 2.0 * texturePosition.s - 1.0, texturePosition.t); - } - else - { - return vec3(abs(2.0 * texturePosition.s - 1.0) - 1.0, 2.0 * texturePosition.s - 1.0, texturePosition.t); - } - -} - -void main() -{ - vec3 currentCylinderSurfaceCoordinate = coordinateFromTexturePosition(impostorSpaceCoordinate); - currentCylinderSurfaceCoordinate.xy = normalize(currentCylinderSurfaceCoordinate.xy); - float fractionalZPosition = (currentCylinderSurfaceCoordinate.z + 1.0) / 2.0; - - vec3 currentBaseCoordinate = (normalizedEndingCoordinate * fractionalZPosition) + (normalizedStartingCoordinate * (1.0 - fractionalZPosition)); - vec2 offsetXYCoordinates = normalize(currentCylinderSurfaceCoordinate.xy); - - vec3 currentPositionCoordinate = currentBaseCoordinate - halfCylinderRadius * (vec3(0.0, 0.0, 1.0) * adjustmentForOrthographicProjection); -// vec3 currentPositionCoordinate = currentBaseCoordinate; - - float previousDepthValue = depthFromEncodedColor(texture2D(depthTexture, currentPositionCoordinate.xy)); - - if ( floor(currentPositionCoordinate.z * 765.0) <= (ceil(previousDepthValue * 765.0)) ) - { -// gl_FragColor = vec4(currentPositionCoordinate, 1.0); -// gl_FragColor = vec4(texture2D(depthTexture, currentPositionCoordinate.xy).rgb, 1.0); -// gl_FragColor = vec4(vec3(intensityFactor * 0.75), 1.0); - gl_FragColor = vec4(vec3(intensityFactor), 1.0); - } - else - { - gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); - } - -// gl_FragColor = vec4((1.0 + currentCylinderSurfaceCoordinate) / 2.0, 1.0); -// gl_FragColor = vec4((impostorSpaceCoordinate + 1.0) / 2.0, 0.0, 1.0); -// gl_FragColor = vec4(texture2D(depthTexture, currentPositionCoordinate.xy).rgb, 1.0); -// gl_FragColor = vec4(currentPositionCoordinate, 1.0); -// gl_FragColor = vec4(vec3(fractionalZPosition), 1.0); -} \ No newline at end of file diff --git a/Shaders/CylinderAmbientOcclusion.vsh b/Shaders/CylinderAmbientOcclusion.vsh deleted file mode 100644 index 3e31ced..0000000 --- a/Shaders/CylinderAmbientOcclusion.vsh +++ /dev/null @@ -1,57 +0,0 @@ -// -// Shader.vsh -// CubeExample -// -// Created by Brad Larson on 4/20/2010. -// - -attribute vec3 position; -attribute vec2 inputImpostorSpaceCoordinate; -attribute vec3 direction; -attribute vec2 ambientOcclusionTextureOffset; - -varying mediump vec2 impostorSpaceCoordinate; -varying mediump vec3 normalizedStartingCoordinate; -varying mediump vec3 normalizedEndingCoordinate; -varying mediump float halfCylinderRadius; -varying mediump vec3 adjustmentForOrthographicProjection; -varying mediump float depthAdjustmentForOrthographicProjection; - -uniform mediump mat3 modelViewProjMatrix; -uniform mediump float cylinderRadius; -uniform mediump mat3 orthographicMatrix; -uniform mediump float ambientOcclusionTexturePatchWidth; - -void main() -{ - vec3 transformedStartingCoordinate, transformedEndingCoordinate; - - if (inputImpostorSpaceCoordinate.t < 0.0) - { - transformedStartingCoordinate = modelViewProjMatrix * position; - transformedEndingCoordinate = modelViewProjMatrix * (position + direction); - } - else - { - transformedStartingCoordinate = modelViewProjMatrix * (position - direction); - transformedEndingCoordinate = modelViewProjMatrix * position; - } - - transformedStartingCoordinate *= orthographicMatrix; - transformedEndingCoordinate *= orthographicMatrix; - - adjustmentForOrthographicProjection = (vec3(0.5, 0.5, 0.5) * orthographicMatrix).xyz; - -// normalizedStartingCoordinate = (transformedStartingCoordinate.xyz + 1.0) * adjustmentForOrthographicProjection; -// normalizedEndingCoordinate = (transformedEndingCoordinate.xyz + 1.0) * adjustmentForOrthographicProjection; - - normalizedStartingCoordinate = (transformedStartingCoordinate / 2.0) + 0.5; - - normalizedEndingCoordinate = (transformedEndingCoordinate / 2.0) + 0.5; - - impostorSpaceCoordinate = inputImpostorSpaceCoordinate.xy; - - halfCylinderRadius = cylinderRadius; - - gl_Position = vec4(ambientOcclusionTextureOffset * 2.0 - vec2(1.0) + (ambientOcclusionTexturePatchWidth * impostorSpaceCoordinate), 0.0, 1.0); -} diff --git a/Shaders/CylinderDepth.fsh b/Shaders/CylinderDepth.fsh deleted file mode 100644 index 14cda7f..0000000 --- a/Shaders/CylinderDepth.fsh +++ /dev/null @@ -1,37 +0,0 @@ -precision mediump float; - -varying mediump vec2 impostorSpaceCoordinate; -varying mediump float depthOffsetAlongCenterAxis; -varying mediump float normalizedDisplacementAtEndCaps; -varying mediump float normalizedDepth; -varying mediump float depthAdjustmentForOrthographicProjection; - -const lowp vec3 stepValues = vec3(2.0, 1.0, 0.0); -const float scaleDownFactor = 1.0 / 255.0; - -void main() -{ - float adjustmentFromCenterAxis = sqrt(1.0 - impostorSpaceCoordinate.s * impostorSpaceCoordinate.s); - float displacementFromCurvature = normalizedDisplacementAtEndCaps * adjustmentFromCenterAxis; - float depthOffset = depthOffsetAlongCenterAxis * adjustmentFromCenterAxis * depthAdjustmentForOrthographicProjection; - - if ( (impostorSpaceCoordinate.t <= (-1.0 + displacementFromCurvature)) || (impostorSpaceCoordinate.t >= (1.0 + displacementFromCurvature))) - { - gl_FragColor = vec4(1.0); - } - -// if ( impostorSpaceCoordinate.t <= (-1.0 + displacementFromCurvature)) - // { - // discard; - // } - - // Use a little fudge factor to account for rounding errors when zoomed out on the ball and stick mode - float calculatedDepth = normalizedDepth - depthOffset + 0.0025; - - calculatedDepth = calculatedDepth * 3.0; - - lowp vec3 intDepthValue = vec3(calculatedDepth) - stepValues; - lowp vec4 outputColor = vec4(intDepthValue, 1.0); - - gl_FragColor = outputColor; -} diff --git a/Shaders/CylinderDepth.vsh b/Shaders/CylinderDepth.vsh deleted file mode 100644 index e3226c0..0000000 --- a/Shaders/CylinderDepth.vsh +++ /dev/null @@ -1,65 +0,0 @@ -attribute vec3 position; -attribute vec3 direction; -attribute vec3 inputImpostorSpaceCoordinate; - -uniform mat3 modelViewProjMatrix; -uniform mediump float cylinderRadius; -uniform mediump mat3 orthographicMatrix; -uniform mediump vec3 translation; - -varying mediump vec2 impostorSpaceCoordinate; -varying mediump float depthOffsetAlongCenterAxis; -varying mediump float normalizedDisplacementAtEndCaps; -varying mediump float normalizedDepth; -varying mediump float depthAdjustmentForOrthographicProjection; - -void main() -{ - vec2 rotationFactor; - vec3 transformedDirection, transformedPosition, transformedOtherPosition; - vec3 viewDisplacementForVertex, displacementDirectionAtEndCap; - float displacementAtEndCaps, lengthOfCylinder, lengthOfCylinderInView; - - depthAdjustmentForOrthographicProjection = (vec3(0.0, 0.0, 0.5) * orthographicMatrix).z; - - transformedPosition = modelViewProjMatrix * (position + translation); - transformedOtherPosition = modelViewProjMatrix * (position + direction + translation); - transformedDirection = transformedOtherPosition - transformedPosition; - - lengthOfCylinder = length(transformedDirection.xyz); - lengthOfCylinderInView = length(transformedDirection.xy); - rotationFactor = transformedDirection.xy / lengthOfCylinderInView; - - displacementAtEndCaps = cylinderRadius * (transformedOtherPosition.z - transformedPosition.z) / lengthOfCylinder; - - normalizedDisplacementAtEndCaps = displacementAtEndCaps / lengthOfCylinderInView; - - depthOffsetAlongCenterAxis = cylinderRadius * lengthOfCylinder * inversesqrt(lengthOfCylinder * lengthOfCylinder - (transformedOtherPosition.z - transformedPosition.z) * (transformedOtherPosition.z - transformedPosition.z)); - depthOffsetAlongCenterAxis = clamp(depthOffsetAlongCenterAxis, 0.0, cylinderRadius * 2.0); - - displacementDirectionAtEndCap.xy = displacementAtEndCaps * rotationFactor; - displacementDirectionAtEndCap.z = transformedDirection.z * displacementAtEndCaps / lengthOfCylinder; - - transformedDirection.xy = normalize(transformedDirection.xy); - - if ((displacementAtEndCaps * inputImpostorSpaceCoordinate.t) > 0.0) - { - viewDisplacementForVertex.x = inputImpostorSpaceCoordinate.x * transformedDirection.y * cylinderRadius + displacementDirectionAtEndCap.x; - viewDisplacementForVertex.y = -inputImpostorSpaceCoordinate.x * transformedDirection.x * cylinderRadius + displacementDirectionAtEndCap.y; - viewDisplacementForVertex.z = displacementDirectionAtEndCap.z; - impostorSpaceCoordinate = vec2(inputImpostorSpaceCoordinate.s, inputImpostorSpaceCoordinate.t + 1.0 * normalizedDisplacementAtEndCaps); - } - else - { - viewDisplacementForVertex.x = inputImpostorSpaceCoordinate.x * transformedDirection.y * cylinderRadius; - viewDisplacementForVertex.y = -inputImpostorSpaceCoordinate.x * transformedDirection.x * cylinderRadius; - viewDisplacementForVertex.z = 0.0; - impostorSpaceCoordinate = vec2(inputImpostorSpaceCoordinate.s, inputImpostorSpaceCoordinate.t); - } - - transformedPosition.xyz = (transformedPosition.xyz + viewDisplacementForVertex); - transformedPosition *= orthographicMatrix; - normalizedDepth = transformedPosition.z / 2.0 + 0.5; - - gl_Position = vec4(transformedPosition, 1.0); -} diff --git a/Shaders/CylinderRaytracing.fsh b/Shaders/CylinderRaytracing.fsh deleted file mode 100644 index dd16dfa..0000000 --- a/Shaders/CylinderRaytracing.fsh +++ /dev/null @@ -1,111 +0,0 @@ -precision mediump float; - -uniform vec3 cylinderColor; -uniform sampler2D depthTexture; -uniform sampler2D ambientOcclusionTexture; -uniform mat3 inverseModelViewProjMatrix; -uniform mediump float ambientOcclusionTexturePatchWidth; - -varying mediump vec2 impostorSpaceCoordinate; -varying mediump vec3 normalAlongCenterAxis; -varying mediump float depthOffsetAlongCenterAxis; -varying mediump float normalizedDepthOffsetAlongCenterAxis; -varying mediump float normalizedDisplacementAtEndCaps; -varying mediump float normalizedRadialDisplacementAtEndCaps; -varying mediump vec2 rotationFactor; -varying mediump vec3 normalizedViewCoordinate; -varying mediump vec2 ambientOcclusionTextureBase; -varying mediump float depthAdjustmentForOrthographicProjection; -varying mediump float normalizedDistanceAlongZAxis; - -const mediump float oneThird = 1.0 / 3.0; -const vec3 lightPosition = vec3(0.312757, 0.248372, 0.916785); - - -mediump float depthFromEncodedColor(mediump vec4 encodedColor) -{ - return oneThird * (encodedColor.r + encodedColor.g + encodedColor.b); - // return encodedColor.r; -} - -mediump vec2 textureCoordinateForCylinderSurfacePosition(mediump vec3 cylinderSurfacePosition) -{ - vec2 halfAbsoluteXY = abs(cylinderSurfacePosition.xy / 2.0); - - if (cylinderSurfacePosition.x >= 0.0) - { - return vec2(cylinderSurfacePosition.y / (4.0 * (halfAbsoluteXY.x + halfAbsoluteXY.y)) - 0.5, cylinderSurfacePosition.z); - } - else - { - return vec2(-cylinderSurfacePosition.y / (4.0 * (halfAbsoluteXY.x + halfAbsoluteXY.y)) + 0.5, cylinderSurfacePosition.z); - } -} - -void main() -{ - float adjustmentFromCenterAxis = sqrt(1.0 - impostorSpaceCoordinate.s * impostorSpaceCoordinate.s); - float displacementFromCurvature = normalizedDisplacementAtEndCaps * adjustmentFromCenterAxis; - float depthOffset = depthOffsetAlongCenterAxis * adjustmentFromCenterAxis * depthAdjustmentForOrthographicProjection; - - vec3 normal = vec3(normalizedRadialDisplacementAtEndCaps * rotationFactor.x * adjustmentFromCenterAxis + impostorSpaceCoordinate.s * rotationFactor.y, - -(normalizedRadialDisplacementAtEndCaps * rotationFactor.y * adjustmentFromCenterAxis + impostorSpaceCoordinate.s * rotationFactor.x), - normalizedDepthOffsetAlongCenterAxis * adjustmentFromCenterAxis); - - normal = normalize(normal); - - if ( (impostorSpaceCoordinate.t <= (-1.0 + displacementFromCurvature)) || (impostorSpaceCoordinate.t >= (1.0 + displacementFromCurvature))) - { - gl_FragColor = vec4(0.0); // Black background -// gl_FragColor = vec4(1.0); // White background - } - else - { - float currentDepthValue = normalizedViewCoordinate.z - depthOffset + 0.0025; - float previousDepthValue = depthFromEncodedColor(texture2D(depthTexture, normalizedViewCoordinate.xy)); - - //if ( (floor(currentDepthValue * 765.0)) > (ceil(previousDepthValue * 765.0)) ) - if ( (currentDepthValue - 0.006) > (previousDepthValue) ) - { - gl_FragColor = vec4(0.0); // Black background -// gl_FragColor = vec4(1.0); // White background - } - else - { - vec3 finalCylinderColor = cylinderColor; - - // ambient - vec3 aoNormal = vec3(0.5, 0.5, normalizedDistanceAlongZAxis); - // vec3 aoNormal = normal; - // aoNormal.z = -aoNormal.z; - // aoNormal = (inverseModelViewProjMatrix * vec4(aoNormal, 0.0)).xyz; - // aoNormal.z = -aoNormal.z; - vec2 textureCoordinateForAOLookup = ambientOcclusionTextureBase + ambientOcclusionTexturePatchWidth * 0.5 * textureCoordinateForCylinderSurfacePosition(aoNormal); - vec3 ambientOcclusionIntensity = texture2D(ambientOcclusionTexture, textureCoordinateForAOLookup).rgb; - - float lightingIntensity = 0.1 + clamp(dot(lightPosition, normal), 0.0, 1.0) * ambientOcclusionIntensity.r; - finalCylinderColor *= lightingIntensity; - - // Per fragment specular lighting - lightingIntensity = clamp(dot(lightPosition, normal), 0.0, 1.0); - lightingIntensity = pow(lightingIntensity, 60.0) * ambientOcclusionIntensity.r * 1.2; - finalCylinderColor += 0.4 * lightingIntensity; - - // gl_FragColor = texture2D(depthTexture, normalizedViewCoordinate.xy); - - // normal.z = -normal.z; - // normal = (inverseModelViewProjMatrix * vec4(normal, 0.0)).xyz; - // normal.z = -normal.z; - // - // gl_FragColor = vec4(normal, 1.0); - - // gl_FragColor = vec4(textureCoordinateForCylinderSurfacePosition(aoNormal), 0.0, 1.0); - // gl_FragColor = vec4(ambientOcclusionTextureBase, 0.0, 1.0); - -// gl_FragColor = vec4(ambientOcclusionIntensity, 1.0); - - // gl_FragColor = vec4(vec3((1.0 + normalizedDistanceAlongZAxis) / 2.0), 1.0); - gl_FragColor = vec4(finalCylinderColor, 1.0); - } - } -} diff --git a/Shaders/CylinderRaytracing.vsh b/Shaders/CylinderRaytracing.vsh deleted file mode 100644 index f59370c..0000000 --- a/Shaders/CylinderRaytracing.vsh +++ /dev/null @@ -1,80 +0,0 @@ -attribute vec3 position; -attribute vec3 direction; -attribute vec3 inputImpostorSpaceCoordinate; -attribute mediump vec2 ambientOcclusionTextureOffset; - -uniform mat3 modelViewProjMatrix; -uniform mediump float cylinderRadius; -uniform mediump mat3 orthographicMatrix; -uniform mediump vec3 translation; - -varying mediump vec2 impostorSpaceCoordinate; -varying mediump float depthOffsetAlongCenterAxis; -varying mediump float normalizedDepthOffsetAlongCenterAxis; -varying mediump float normalizedDisplacementAtEndCaps; -varying mediump float normalizedRadialDisplacementAtEndCaps; -varying mediump vec2 rotationFactor; -varying mediump vec3 normalizedViewCoordinate; -varying mediump vec2 ambientOcclusionTextureBase; -varying mediump float depthAdjustmentForOrthographicProjection; -varying mediump float normalizedDistanceAlongZAxis; - -void main() -{ - ambientOcclusionTextureBase = (ambientOcclusionTextureOffset + 1.0 / 1024.0); - normalizedDistanceAlongZAxis = inputImpostorSpaceCoordinate.y; - - vec3 transformedDirection, transformedPosition, transformedOtherPosition; - vec3 viewDisplacementForVertex, displacementDirectionAtEndCap; - float displacementAtEndCaps, lengthOfCylinder, lengthOfCylinderInView; - - depthAdjustmentForOrthographicProjection = (vec3(0.0, 0.0, 0.5) * orthographicMatrix).z; - - transformedPosition = modelViewProjMatrix * (position + translation); - transformedOtherPosition = modelViewProjMatrix * (position + direction + translation); - transformedDirection = transformedOtherPosition - transformedPosition; - - lengthOfCylinder = length(transformedDirection.xyz); - lengthOfCylinderInView = length(transformedDirection.xy); - rotationFactor = transformedDirection.xy / lengthOfCylinderInView; - - displacementAtEndCaps = cylinderRadius * (transformedOtherPosition.z - transformedPosition.z) / lengthOfCylinder; - normalizedDisplacementAtEndCaps = displacementAtEndCaps / lengthOfCylinderInView; - normalizedRadialDisplacementAtEndCaps = displacementAtEndCaps / cylinderRadius; - - depthOffsetAlongCenterAxis = cylinderRadius * lengthOfCylinder * inversesqrt(lengthOfCylinder * lengthOfCylinder - (transformedOtherPosition.z - transformedPosition.z) * (transformedOtherPosition.z - transformedPosition.z)); - depthOffsetAlongCenterAxis = clamp(depthOffsetAlongCenterAxis, 0.0, cylinderRadius * 2.0); - normalizedDepthOffsetAlongCenterAxis = depthOffsetAlongCenterAxis / (cylinderRadius); - - displacementDirectionAtEndCap.xy = displacementAtEndCaps * rotationFactor; - displacementDirectionAtEndCap.z = transformedDirection.z * displacementAtEndCaps / lengthOfCylinder; - - transformedDirection.xy = normalize(transformedDirection.xy); - - if ((displacementAtEndCaps * inputImpostorSpaceCoordinate.t) > 0.0) - { - viewDisplacementForVertex.x = inputImpostorSpaceCoordinate.x * transformedDirection.y * cylinderRadius + displacementDirectionAtEndCap.x; - viewDisplacementForVertex.y = -inputImpostorSpaceCoordinate.x * transformedDirection.x * cylinderRadius + displacementDirectionAtEndCap.y; - viewDisplacementForVertex.z = displacementDirectionAtEndCap.z; - impostorSpaceCoordinate = vec2(inputImpostorSpaceCoordinate.s, inputImpostorSpaceCoordinate.t + 1.0 * normalizedDisplacementAtEndCaps); - } - else - { - viewDisplacementForVertex.x = inputImpostorSpaceCoordinate.x * transformedDirection.y * cylinderRadius; - viewDisplacementForVertex.y = -inputImpostorSpaceCoordinate.x * transformedDirection.x * cylinderRadius; - viewDisplacementForVertex.z = 0.0; -// impostorSpaceCoordinate = inputImpostorSpaceCoordinate.st; - impostorSpaceCoordinate = vec2(inputImpostorSpaceCoordinate.s, inputImpostorSpaceCoordinate.t); - } - - transformedPosition.xyz = transformedPosition.xyz + viewDisplacementForVertex; - // transformedPosition.z = 0.0; - - transformedPosition *= orthographicMatrix; - - normalizedViewCoordinate = (transformedPosition / 2.0) + 0.5; - - gl_Position = vec4(transformedPosition, 1.0); -// gl_Position = transformedPosition; -// impostorSpaceCoordinate = displacementDirectionAtEndCap / cylinderRadius; -} diff --git a/Shaders/PlainDisplay.fsh b/Shaders/PlainDisplay.fsh deleted file mode 100644 index 70ec547..0000000 --- a/Shaders/PlainDisplay.fsh +++ /dev/null @@ -1,11 +0,0 @@ -precision mediump float; - -varying highp vec2 textureCoordinate; - -uniform sampler2D texture; - -void main() -{ - gl_FragColor = texture2D(texture, textureCoordinate); -// gl_FragColor = vec4(1.0); -} diff --git a/Shaders/PlainDisplay.vsh b/Shaders/PlainDisplay.vsh deleted file mode 100644 index 3cfb6f7..0000000 --- a/Shaders/PlainDisplay.vsh +++ /dev/null @@ -1,10 +0,0 @@ -attribute vec4 position; -attribute vec4 inputTextureCoordinate; - -varying vec2 textureCoordinate; - -void main() -{ - gl_Position = position; - textureCoordinate = inputTextureCoordinate.xy; -} \ No newline at end of file diff --git a/Shaders/SphereAOLookup.fsh b/Shaders/SphereAOLookup.fsh deleted file mode 100644 index 4ea6091..0000000 --- a/Shaders/SphereAOLookup.fsh +++ /dev/null @@ -1,45 +0,0 @@ -precision mediump float; - -uniform mediump mat3 inverseModelViewProjMatrix; - -varying mediump vec2 impostorSpaceCoordinate; -varying mediump vec2 depthLookupCoordinate; - -void main() -{ - float distanceFromCenter = length(impostorSpaceCoordinate); - - vec3 aoNormal; - - if (distanceFromCenter > 1.0) - { - distanceFromCenter = 1.0; - aoNormal = vec3(normalize(impostorSpaceCoordinate), 0.0); - } - else - { - float precalculatedDepth = sqrt(1.0 - distanceFromCenter * distanceFromCenter); - aoNormal = vec3(impostorSpaceCoordinate, -precalculatedDepth); - } - - // Ambient occlusion factor - aoNormal = inverseModelViewProjMatrix * aoNormal; - aoNormal.z = -aoNormal.z; - - vec3 absoluteSphereSurfacePosition = abs(aoNormal); - float d = absoluteSphereSurfacePosition.x + absoluteSphereSurfacePosition.y + absoluteSphereSurfacePosition.z; - - vec2 lookupTextureCoordinate; - if (aoNormal.z <= 0.0) - { - lookupTextureCoordinate = aoNormal.xy / d; - } - else - { - vec2 theSign = aoNormal.xy / absoluteSphereSurfacePosition.xy; - //vec2 aSign = sign(aoNormal.xy); - lookupTextureCoordinate = theSign - absoluteSphereSurfacePosition.yx * (theSign / d); - } - - gl_FragColor = vec4((lookupTextureCoordinate / 2.0) + 0.5, 0.0, 1.0); -} \ No newline at end of file diff --git a/Shaders/SphereAOLookup.vsh b/Shaders/SphereAOLookup.vsh deleted file mode 100644 index 2437293..0000000 --- a/Shaders/SphereAOLookup.vsh +++ /dev/null @@ -1,19 +0,0 @@ -// -// Shader.vsh -// CubeExample -// -// Created by Brad Larson on 4/20/2010. -// - -attribute mediump vec2 inputImpostorSpaceCoordinate; - -varying mediump vec2 impostorSpaceCoordinate; -varying mediump vec2 depthLookupCoordinate; - -void main() -{ - impostorSpaceCoordinate = inputImpostorSpaceCoordinate; - depthLookupCoordinate = (inputImpostorSpaceCoordinate / 2.0) + 0.5; - - gl_Position = vec4(impostorSpaceCoordinate, 0.0, 1.0); -} diff --git a/Shaders/SphereAmbientOcclusion.fsh b/Shaders/SphereAmbientOcclusion.fsh deleted file mode 100644 index 895ed11..0000000 --- a/Shaders/SphereAmbientOcclusion.fsh +++ /dev/null @@ -1,62 +0,0 @@ -precision mediump float; - -uniform sampler2D depthTexture; -uniform highp mat3 modelViewProjMatrix; -uniform mediump mat3 inverseModelViewProjMatrix; -uniform mediump float intensityFactor; - -varying highp vec2 impostorSpaceCoordinate; -varying mediump vec3 normalizedViewCoordinate; -varying mediump float adjustedSphereRadius; -varying mediump vec3 adjustmentForOrthographicProjection; - -const mediump float oneThird = 1.0 / 3.0; - -mediump float depthFromEncodedColor(mediump vec4 encodedColor) -{ - return oneThird * (encodedColor.r + encodedColor.g + encodedColor.b); - // return encodedColor.r; -} - -highp vec3 coordinateFromTexturePosition(highp vec2 texturePosition) -{ - highp vec2 absoluteTexturePosition = abs(texturePosition); - highp float h = 1.0 - absoluteTexturePosition.s - absoluteTexturePosition.t; - - if (h >= 0.0) - { - return vec3(texturePosition.s, texturePosition.t, h); - } - else - { - return vec3(sign(texturePosition.s) * (1.0 - absoluteTexturePosition.t), sign(texturePosition.t) * (1.0 - absoluteTexturePosition.s), h); - } -} - -void main() -{ - highp vec3 currentSphereSurfaceCoordinate = coordinateFromTexturePosition(clamp(impostorSpaceCoordinate, -1.0, 1.0)); - - currentSphereSurfaceCoordinate = normalize(modelViewProjMatrix * currentSphereSurfaceCoordinate); - - vec3 currentPositionCoordinate = normalizedViewCoordinate + adjustedSphereRadius * currentSphereSurfaceCoordinate * adjustmentForOrthographicProjection; - - - float previousDepthValue = depthFromEncodedColor(texture2D(depthTexture, currentPositionCoordinate.xy)); - - if ( (floor(currentPositionCoordinate.z * 765.0 - 5.0)) <= (ceil(previousDepthValue * 765.0)) ) - { -// gl_FragColor = vec4(texture2D(depthTexture, currentPositionCoordinate.xy).rgb, 1.0); - gl_FragColor = vec4(vec3(intensityFactor), 1.0); -// gl_FragColor = vec4(currentPositionCoordinate, 1.0); - } - else - { - gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); - } - -// gl_FragColor = vec4(currentPositionCoordinate, 1.0); -// gl_FragColor = vec4(currentSphereSurfaceCoordinate, 1.0); -// gl_FragColor = vec4(textureOffset, 0.0 , 1.0); - -} \ No newline at end of file diff --git a/Shaders/SphereAmbientOcclusion.vsh b/Shaders/SphereAmbientOcclusion.vsh deleted file mode 100644 index 045fb42..0000000 --- a/Shaders/SphereAmbientOcclusion.vsh +++ /dev/null @@ -1,53 +0,0 @@ -// -// Shader.vsh -// CubeExample -// -// Created by Brad Larson on 4/20/2010. -// - -#define AMBIENTOCCLUSIONTEXTUREWIDTH 512.0 -//#define AMBIENTOCCLUSIONTEXTUREWIDTH 2048.0 - -attribute vec3 position; -attribute vec2 inputImpostorSpaceCoordinate; -attribute vec2 ambientOcclusionTextureOffset; - -varying highp vec2 impostorSpaceCoordinate; -varying mediump vec2 depthLookupCoordinate; -varying mediump vec3 normalizedViewCoordinate; -varying mediump float adjustedSphereRadius; -varying mediump vec3 adjustmentForOrthographicProjection; -varying mediump float depthAdjustmentForOrthographicProjection; - -uniform highp mat3 modelViewProjMatrix; -uniform mediump float sphereRadius; -uniform mediump mat3 orthographicMatrix; -uniform mediump float ambientOcclusionTexturePatchWidth; - -void main() -{ - vec3 transformedPosition = modelViewProjMatrix * position; -// impostorSpaceCoordinate = inputImpostorSpaceCoordinate; - vec2 adjustedImpostorSpaceCoordinate; - if (inputImpostorSpaceCoordinate.x != 0.0) - { - adjustedImpostorSpaceCoordinate = sign(inputImpostorSpaceCoordinate); - } - else - { - adjustedImpostorSpaceCoordinate = vec2(0.0, 0.0); - } - - - impostorSpaceCoordinate = adjustedImpostorSpaceCoordinate * (1.0 + 2.0 / (AMBIENTOCCLUSIONTEXTUREWIDTH * ambientOcclusionTexturePatchWidth)); - - adjustedSphereRadius = sphereRadius; - - transformedPosition = transformedPosition * orthographicMatrix; - - adjustmentForOrthographicProjection = (vec3(0.5, 0.5, 0.5) * orthographicMatrix).xyz; - - normalizedViewCoordinate = (transformedPosition / 2.0) + 0.5; - - gl_Position = vec4(ambientOcclusionTextureOffset * 2.0 - vec2(1.0) + (ambientOcclusionTexturePatchWidth * adjustedImpostorSpaceCoordinate), 0.0, 1.0); -} diff --git a/Shaders/SphereDepth.fsh b/Shaders/SphereDepth.fsh deleted file mode 100644 index a7b9189..0000000 --- a/Shaders/SphereDepth.fsh +++ /dev/null @@ -1,27 +0,0 @@ -precision mediump float; - -varying mediump vec2 impostorSpaceCoordinate; -varying mediump float normalizedDepth; -varying mediump float adjustedSphereRadius; -varying mediump vec2 depthLookupCoordinate; - -uniform lowp sampler2D sphereDepthMap; - -const lowp vec3 stepValues = vec3(2.0, 1.0, 0.0); - -void main() -{ - lowp vec2 precalculatedDepthAndAlpha = texture2D(sphereDepthMap, depthLookupCoordinate).ra; - - float outOfCircleMultiplier = step(precalculatedDepthAndAlpha.g, 0.5); - - float currentDepthValue = normalizedDepth - adjustedSphereRadius * precalculatedDepthAndAlpha.r; - - // Inlined color encoding for the depth values - currentDepthValue = currentDepthValue * 3.0; - - lowp vec3 intDepthValue = vec3(currentDepthValue) - stepValues; - - vec3 temporaryColor = vec3(outOfCircleMultiplier) + vec3(1.0 - outOfCircleMultiplier) * intDepthValue; - gl_FragColor = vec4(temporaryColor, 1.0); -} diff --git a/Shaders/SphereDepth.vsh b/Shaders/SphereDepth.vsh deleted file mode 100644 index 6c515fe..0000000 --- a/Shaders/SphereDepth.vsh +++ /dev/null @@ -1,37 +0,0 @@ -// -// Shader.vsh -// CubeExample -// -// Created by Brad Larson on 4/20/2010. -// - -attribute vec3 position; -attribute vec2 inputImpostorSpaceCoordinate; - -varying mediump vec2 impostorSpaceCoordinate; -varying mediump float normalizedDepth; -varying mediump float adjustedSphereRadius; -varying mediump vec2 depthLookupCoordinate; - -uniform mediump mat3 modelViewProjMatrix; -uniform mediump float sphereRadius; -uniform mediump mat3 orthographicMatrix; -uniform mediump vec3 translation; - -void main() -{ - vec3 transformedPosition; - transformedPosition = modelViewProjMatrix * (position + translation); - impostorSpaceCoordinate = inputImpostorSpaceCoordinate.xy; - - depthLookupCoordinate = (impostorSpaceCoordinate / 2.0) + 0.5; - - transformedPosition.xy = transformedPosition.xy + inputImpostorSpaceCoordinate.xy * vec2(sphereRadius); - transformedPosition = transformedPosition * orthographicMatrix; - - float depthAdjustmentForOrthographicProjection = (vec3(0.0, 0.0, 0.5) * orthographicMatrix).z; - adjustedSphereRadius = sphereRadius * depthAdjustmentForOrthographicProjection; - - normalizedDepth = transformedPosition.z / 2.0 + 0.5; - gl_Position = vec4(transformedPosition, 1.0); -} diff --git a/Shaders/SphereDepthWrite.fsh b/Shaders/SphereDepthWrite.fsh deleted file mode 100644 index e2c609b..0000000 --- a/Shaders/SphereDepthWrite.fsh +++ /dev/null @@ -1,7 +0,0 @@ -precision mediump float; - -void main() -{ - gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); -// gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0); -} diff --git a/Shaders/SphereDepthWrite.vsh b/Shaders/SphereDepthWrite.vsh deleted file mode 100644 index 8c185c6..0000000 --- a/Shaders/SphereDepthWrite.vsh +++ /dev/null @@ -1,20 +0,0 @@ -attribute mediump vec3 position; -attribute mediump vec2 inputImpostorSpaceCoordinate; - -uniform mediump mat3 modelViewProjMatrix; -uniform mediump float sphereRadius; -uniform mediump mat3 orthographicMatrix; -uniform mediump vec3 translation; - -void main() -{ - mediump vec3 transformedPosition = modelViewProjMatrix * (position + translation); -// mediump vec2 insetCoordinate = inputImpostorSpaceCoordinate * 0.707107; // Square - mediump vec2 insetCoordinate = inputImpostorSpaceCoordinate * 0.91017; // Octagon - - transformedPosition.xy = transformedPosition.xy + insetCoordinate * vec2(sphereRadius); - transformedPosition.z = transformedPosition.z + sphereRadius + 0.006; - transformedPosition = transformedPosition * orthographicMatrix; - - gl_Position = vec4(transformedPosition, 1.0); -} diff --git a/Shaders/SphereRaytracing.fsh b/Shaders/SphereRaytracing.fsh deleted file mode 100644 index 204f93e..0000000 --- a/Shaders/SphereRaytracing.fsh +++ /dev/null @@ -1,64 +0,0 @@ -precision mediump float; - -uniform lowp vec3 sphereColor; -uniform lowp sampler2D depthTexture; -uniform lowp sampler2D ambientOcclusionTexture; -uniform lowp sampler2D precalculatedAOLookupTexture; -uniform mediump mat3 inverseModelViewProjMatrix; -uniform mediump float ambientOcclusionTexturePatchWidth; -uniform lowp sampler2D sphereDepthMap; - -varying mediump vec2 impostorSpaceCoordinate; -varying mediump vec2 depthLookupCoordinate; -varying mediump vec3 normalizedViewCoordinate; -varying mediump vec2 ambientOcclusionTextureBase; -varying mediump float adjustedSphereRadius; - -const mediump float oneThird = 1.0 / 3.0; -const vec3 lightPosition = vec3(0.312757, 0.248372, 0.916785); - -mediump float depthFromEncodedColor(mediump vec3 encodedColor) -{ - return (encodedColor.r + encodedColor.g + encodedColor.b) * oneThird; -} - -void main() -{ - lowp vec4 precalculatedDepthAndLighting = texture2D(sphereDepthMap, depthLookupCoordinate); - lowp float alphaComponent = 1.0; - -// gl_FragColor = vec4(1.0); - - alphaComponent = step(0.5, precalculatedDepthAndLighting.a); - - float currentDepthValue = normalizedViewCoordinate.z - adjustedSphereRadius * precalculatedDepthAndLighting.r; - vec3 encodedColor = texture2D(depthTexture, normalizedViewCoordinate.xy).rgb; - float previousDepthValue = depthFromEncodedColor(encodedColor); - - // Check to see that this fragment is the frontmost one for this area - alphaComponent = alphaComponent * step((currentDepthValue - 0.004), previousDepthValue); -// alphaComponent = alphaComponent * smoothstep((currentDepthValue - 0.024), (currentDepthValue - 0.006), previousDepthValue); -// alphaComponent = alphaComponent * smoothstep((currentDepthValue - 0.006), (currentDepthValue - 0.024), previousDepthValue); - - lowp vec2 lookupTextureCoordinate = texture2D(precalculatedAOLookupTexture, depthLookupCoordinate).st; - lookupTextureCoordinate = (lookupTextureCoordinate * 2.0) - 1.0; - - vec2 textureCoordinateForAOLookup = ambientOcclusionTextureBase + ambientOcclusionTexturePatchWidth * lookupTextureCoordinate; - lowp float ambientOcclusionIntensity = texture2D(ambientOcclusionTexture, textureCoordinateForAOLookup).r; - - // Ambient lighting -// lowp float lightingIntensity = 0.2 + 1.7 * precalculatedDepthAndLighting.g * ambientOcclusionIntensity; - lowp float lightingIntensity = 0.1 + precalculatedDepthAndLighting.g * ambientOcclusionIntensity; -// lowp float lightingIntensity = precalculatedDepthAndLighting.g; - lowp vec3 finalSphereColor = sphereColor * lightingIntensity; - - // Specular lighting - finalSphereColor = finalSphereColor + ( (precalculatedDepthAndLighting.b * ambientOcclusionIntensity) * (vec3(1.0) - finalSphereColor)); - - gl_FragColor = vec4(finalSphereColor * alphaComponent, alphaComponent); // Black background -// gl_FragColor = vec4(finalSphereColor * alphaComponent + (1.0 - 1.0 * alphaComponent), alphaComponent); // White background -// gl_FragColor = vec4(texture2D(ambientOcclusionTexture, textureCoordinateForAOLookup).rgb * alphaComponent + (1.0 - 1.0 * alphaComponent), alphaComponent); -// gl_FragColor = vec4(textureCoordinateForAOLookup * alphaComponent, 0.0, alphaComponent); - // gl_FragColor = vec4(normalizedViewCoordinate, 1.0); - // gl_FragColor = vec4(precalculatedDepthAndLighting, 1.0); -} \ No newline at end of file diff --git a/Shaders/SphereRaytracing.vsh b/Shaders/SphereRaytracing.vsh deleted file mode 100644 index 03d2681..0000000 --- a/Shaders/SphereRaytracing.vsh +++ /dev/null @@ -1,40 +0,0 @@ -// -// Shader.vsh -// CubeExample -// -// Created by Brad Larson on 4/20/2010. -// - -attribute mediump vec3 position; -attribute mediump vec2 inputImpostorSpaceCoordinate; -attribute mediump vec2 ambientOcclusionTextureOffset; - -varying mediump vec2 impostorSpaceCoordinate; -varying mediump vec3 normalizedViewCoordinate; -varying mediump vec2 depthLookupCoordinate; -varying mediump vec2 ambientOcclusionTextureBase; -varying mediump float adjustedSphereRadius; - -uniform mediump mat3 modelViewProjMatrix; -uniform mediump mat3 orthographicMatrix; -uniform mediump float sphereRadius; -uniform mediump vec3 translation; - -void main() -{ - ambientOcclusionTextureBase = ambientOcclusionTextureOffset; - - vec3 transformedPosition = modelViewProjMatrix * (position + translation); - impostorSpaceCoordinate = inputImpostorSpaceCoordinate; - depthLookupCoordinate = (inputImpostorSpaceCoordinate / 2.0) + 0.5; - - transformedPosition.xy = transformedPosition.xy + inputImpostorSpaceCoordinate.xy * vec2(sphereRadius); - transformedPosition = transformedPosition * orthographicMatrix; - - float depthAdjustmentForOrthographicProjection = (vec3(0.0, 0.0, 0.5) * orthographicMatrix).z; -// adjustedSphereRadius = sphereRadius * 0.5 * depthAdjustmentForOrthographicProjection; - adjustedSphereRadius = sphereRadius * depthAdjustmentForOrthographicProjection; - - normalizedViewCoordinate = (transformedPosition / 2.0) + 0.5; - gl_Position = vec4(transformedPosition, 1.0); -} diff --git a/VCTitleCase.h b/VCTitleCase.h deleted file mode 100644 index 185cba0..0000000 --- a/VCTitleCase.h +++ /dev/null @@ -1,36 +0,0 @@ -// -// VCTitleCase.m -// Title Case extension for NSString -// -// Based on titlecase.pl by: -// John Gruber -// http://daringfireball.net/ -// 10 May 2008 -// -// Cocoa Foundation version by: -// Marshall Elfstrand -// http://vengefulcow.com/ -// 24 May 2008 -// -// License: http://www.opensource.org/licenses/mit-license.php -// - -#import - -/*! - This category adds a "titlecaseString" method to NSString objects. -*/ -@interface NSString (VCTitleCase) - -/*! - Returns a title-cased version of the string. - - This changes all words to Title Caps, and attempts to be clever - about *un*capitalizing small words like a/an/the in the input. - - The list of "small words" which are not capped comes from - the New York Times Manual of Style, plus "vs" ans "v". -*/ -- (NSString *)titlecaseString; - -@end diff --git a/VCTitleCase.m b/VCTitleCase.m deleted file mode 100644 index 562bf3c..0000000 --- a/VCTitleCase.m +++ /dev/null @@ -1,169 +0,0 @@ -// -// VCTitleCase.m -// Title Case extension for NSString -// -// Based on titlecase.pl by: -// John Gruber -// http://daringfireball.net/ -// 10 May 2008 -// -// Cocoa Foundation version by: -// Marshall Elfstrand -// http://vengefulcow.com/ -// 24 May 2008 -// -// License: http://www.opensource.org/licenses/mit-license.php -// -// Modified by Brad Larson to contain all-caps acronyms - - -#import "VCTitleCase.h" - -@implementation NSString (VCTitleCase) - -- (NSString *)titlecaseString -{ - static NSArray *shortWords = nil, *shortAcronyms; - static NSMutableCharacterSet *wordStartCharacterSet; - static NSMutableCharacterSet *wordMiddleCharacterSet; - static NSMutableCharacterSet *wordEndCharacterSet; - static NSMutableCharacterSet *wordIgnoreCharacterSet; - - // Initialize the list of "short" words that remain lowercase. - if (!shortWords) { - shortWords = [[NSArray alloc] initWithObjects: - @"a", @"an", @"and", @"as", @"at", @"but", @"by", @"en", @"for", - @"if", @"in", @"of", @"on", @"or", @"the", @"to", @"v", @"via", - @"vs", nil]; - } - - if (!shortAcronyms) { - shortAcronyms = [[NSArray alloc] initWithObjects: - @"dna", @"rna", @"trna", @"trnaphe", @"hiv", @"nmr", nil]; - } - - // Initialize the set of characters allowed at the start of words. - if (!wordStartCharacterSet) { - wordStartCharacterSet = [[NSCharacterSet uppercaseLetterCharacterSet] mutableCopy]; - [wordStartCharacterSet formUnionWithCharacterSet:[NSCharacterSet lowercaseLetterCharacterSet]]; - } - - // Initialize the set of characters allowed in the middle of words. - if (!wordMiddleCharacterSet) { - wordMiddleCharacterSet = [[NSCharacterSet uppercaseLetterCharacterSet] mutableCopy]; - [wordMiddleCharacterSet formUnionWithCharacterSet:[NSCharacterSet lowercaseLetterCharacterSet]]; - [wordMiddleCharacterSet addCharactersInString:@".&'’"]; - } - - // Initialize the set of characters allowed at the end of words. - if (!wordEndCharacterSet) wordEndCharacterSet = wordStartCharacterSet; - - // Initialize the set of characters that cause a word to be ignored - // when they appear in the middle. - if (!wordIgnoreCharacterSet) { - wordIgnoreCharacterSet = [[NSCharacterSet uppercaseLetterCharacterSet] mutableCopy]; - [wordIgnoreCharacterSet addCharactersInString:@"."]; - } - - // Create a mutable copy of the string that we can modify in-place. - NSMutableString *newString = [self mutableCopy]; - - // Create a local autorelease pool for the temporary objects we're making. - @autoreleasepool{ - - // Create a scanner that we can use to locate words in the string. - NSScanner *scanner = [NSScanner scannerWithString:self]; - [scanner setCaseSensitive:YES]; - - // Begin scanning for words. - NSRange currentRange; // Range of word located by scanner - NSString *word = nil; // Extracted word - NSString *lowercaseWord = nil; // Lowercase version of extracted word - NSRange ignoreTriggerRange; // Range of character causing word to be ignored - BOOL isFirstWord = YES; // To determine whether to capitalize small word - while (![scanner isAtEnd]) { - - // Locate the beginning of the next word. - [scanner scanUpToCharactersFromSet:wordStartCharacterSet - intoString:NULL]; - if ([scanner scanLocation] >= [self length]) continue; // No more words - currentRange = NSMakeRange([scanner scanLocation], 1); - - // Check to see if we stopped on whitespace and advance to the - // actual beginning of the word. - if (![wordStartCharacterSet characterIsMember:[self characterAtIndex:[scanner scanLocation]]]) { - [scanner setScanLocation:[scanner scanLocation] + 1]; - currentRange = NSMakeRange([scanner scanLocation], 1); - } - - // Advance to the next character in the word. - [scanner scanString:[self substringWithRange:currentRange] - intoString:NULL]; - if ([scanner scanLocation] >= [self length]) continue; // No more words - - // See if the next character is a valid word character, and if so, - // scan through the end of the word. - if ([wordMiddleCharacterSet characterIsMember:[self characterAtIndex:[scanner scanLocation]]]) { - [scanner scanCharactersFromSet:wordMiddleCharacterSet - intoString:NULL]; - currentRange.length = [scanner scanLocation] - currentRange.location; - } - - // Back off the word until it ends with a valid character. - unichar lastCharacter = [self characterAtIndex:(NSMaxRange(currentRange) - 1)]; - while (![wordEndCharacterSet characterIsMember:lastCharacter]) { - [scanner setScanLocation:[scanner scanLocation] - 1]; - currentRange.length -= 1; - lastCharacter = [self characterAtIndex:(NSMaxRange(currentRange) - 1)]; - } - - // We have now located a word. - word = [self substringWithRange:currentRange]; - lowercaseWord = [word lowercaseString]; - - // Check to see if the word needs to be capitalized. - // Words that have dots in the middle or that already contain - // capitalized letters in the middle (e.g. "iTunes") are ignored. - ignoreTriggerRange = [self - rangeOfCharacterFromSet:wordIgnoreCharacterSet - options:NSLiteralSearch - range:NSMakeRange(currentRange.location + 1, currentRange.length - 1) - ]; - if (ignoreTriggerRange.location == NSNotFound) { - if ([word rangeOfString:@"&"].location != NSNotFound) { - // Uppercase words that contain ampersands. - [newString replaceCharactersInRange:currentRange - withString:[word uppercaseString]]; - } else { - if ((!isFirstWord) && [shortWords containsObject:lowercaseWord]) { - // Lowercase small words. - [newString replaceCharactersInRange:currentRange - withString:lowercaseWord]; - } else if ([shortAcronyms containsObject:lowercaseWord]) { - [newString replaceCharactersInRange:currentRange - withString:[word uppercaseString]]; - } else { - // Capitalize word. - [newString replaceCharactersInRange:currentRange - withString:[word capitalizedString]]; - } - } - } - - isFirstWord = NO; - } - - // Make sure the last word is capitalized, even if it is a small word. - if (lowercaseWord && [shortWords containsObject:lowercaseWord]) { - [newString replaceCharactersInRange:currentRange - withString:[lowercaseWord capitalizedString]]; - } - - }; - - NSString *resultString = [newString copy]; - - return resultString; -} - -@end diff --git a/VisualizationIcon.png b/VisualizationIcon.png deleted file mode 100644 index 40a61b1dece0bc3f5173cfa76f6b6f0c24dcb088..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1827 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 diff --git a/main.m b/main.m deleted file mode 100644 index 7c9c346..0000000 --- a/main.m +++ /dev/null @@ -1,17 +0,0 @@ -// -// main.m -// Molecules -// -// The source code for Molecules is available under a BSD license. See License.txt for details. -// -// Created by Brad Larson on 6/26/2008. - -#import - -int main(int argc, char *argv[]) { - - @autoreleasepool { - int retVal = UIApplicationMain(argc, argv, nil, @"SLSMoleculeAppDelegate"); - return retVal; - } -} diff --git a/molecules.sql b/molecules.sql deleted file mode 100644 index d92806fd28957e45245c8afbf3d5a9d9d5cb864e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7168 zcmeHMzi-qq6tSJg($~HB+`ISZf9B#7o&@tb{-Wqr z#!{+pRI;^XO7Z-&+ylI~t7s8NBwoB(Mr3dK_=c5|7dbgOi=%hZYWCy6n+8Wm_~+Vg zAc~A7RmNm%_?>_)e9Y4(2Klf$dB0d)lGEri-0+O$R4@`p7jdY#%AA=L;<~8vv|ezZ zKwK6fRfSk5dRa9=!>b}sdyrIRm8hy@J)9&gSF9{Hf=ao*C7OX{*um}|z`oX);5iqS z&P{TqiD@41!!AM}Zi3L;2ZQwBCP)vigUqBq4Gs_S_EoI}Q`E>O+>)g<+s%4pNpN*zL5a91m?XE@$X{T{#KMVc!W+jt6)jKruw!*at%b3ujwZ0S0 z5#$Kjo{j6=zS-PNM@TzOI=NZsALeE|ufvyAv{X%J)Q-!vc3c-Q-Lb9^W4gZ>(-rg^ DM&nQH diff --git a/redButton.png b/redButton.png deleted file mode 100644 index c7b572826ad5a166d94d0a411bd5243467c1aeb6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1472 zcmV;x1wZF$~7o$X25BA)>MnkeCOFPr(O2Kru1kM+Aipn{jt$Hp}iT zJ-f4=?yjn-uBv;F58XXGGdnYGcDwyI=b^g0>R0!kbN}~L3Hz%GJP3RmXaWxd!7l$R zfPVlj;7`E6fn=|X${ycWfG-124KH*B$bj{IZm90LvmUk6t9gMJEl4tNVl0e|8npE&-+vDvRabn1AeJQd3P{JhyoyLRoG zPm@&NUR+9EIoEl4Y4LAMfB~j}PXa#x{<0hN1n_O(UqEr-;61Z1e*V$tA3AlsTtq$f z8yh&6L7v|{+AtJrO9YiFGBYcGedpcu`QQHi`|n@B(q&Ks9|yh%oZAYm0zYL?0R5vU zA3b*Z3lDyyl6EBTMf*+Zt_o+SB`($VYp=ik*mesoiVw=$lnG00%rtoHSAYCYd;M|;r~#LOS7mtcBCrU!pMU9dKRT)X&==Wl z14pu@3X2-#PyhON&oU^k`+;v-28o^m`VV~UGar5OZXR(_q&Ux=hR-tYs+8=TS#zm< z{n8o`0VWvIGeEyG(|NqVmVV$I?x!M+^^0@ClRt!S4LK(QUqA8M<dEa$RY@u3f0aR7lR!HpM?cJ%|Fm%+}d1R2}32Jjt+p1S4JJL;a*z92~H?^~q zfa+H0WEaa0=r-@2@L0Ww_)+x^yw?nR#Q%dN01y$6FzG@XdGC#N>YXq4dTtyuR0C_R zs;Y5$oadnBy`NccTWxUGTI0PJYpt3v3{${cYu);=gPH)eVCY6+7^XoG1W}e{25@JP z=ny(LZs-}LGlWVQhFw#wR$JEEvWWQKA3XWWs4E^<2_CVDwV57M3z;x z2IP5f`SKgk8z=P7(960jSAbkqt0J;&qbN!njmC$vEXx7&&eVbBNAm39S*$VK*=Lm? z|5>g65wI(8oX5DMwJ#g>Kmk5f1UEE$=A}C&%oj>if z-nxjysybaRm(K%z3()O$qlJZqdau_5@IH&&8&lP@HZM+{q%O?B3s4ee<1DtWVZ3@IX2!>pI;Y|tHzjav)Syo+wHSynkKNr2%632-Sv9? z$i<5nYpOaUB6F%b3rwl%Xzpakd82*Oi%0>aBGOaU>mt%SeE4wE>2%&}wOVVV6SjGL z9LKS9E;)MiXtgMcEKSo~RX2dXF{bamPl3!B(-)CcMAG5!v9&f*)eeKVYVCTx{{E38 zN0Qam)ur|I^=)t3_OiTiVPWCkTCH|h6h%QC$6=ObVV>t@=bW%*^Jm~@8irxNR4V1Q zS}mWRo-X1zzPhrqvdS(l_rH8LY&M&7wOXxStyY6lsZ`zTk6D%_Ns<(C9Cuo+R(m(t aNdE=>Vyb5Ys(5Yy0000 Date: Sat, 15 Jul 2023 19:35:05 -0500 Subject: [PATCH 2/9] Added metadata parsing for PDB files. Started initial geometry construction for Metal. --- Molecules.xcodeproj/project.pbxproj | 20 +++-- .../FileProcessing/MolecularStructure.swift | 15 ++++ Molecules/FileProcessing/PDBFile.swift | 70 +++++++++++++-- Molecules/FileProcessing/SDFFile.swift | 2 + Molecules/FileProcessing/XYZFile.swift | 3 +- .../MoleculeDisplayView.swift | 2 +- .../Interface/MoleculeMetadataView.swift | 66 ++++++++++++++ Molecules/Rendering/MetalRenderView.swift | 90 +++++++++++++++---- .../Rendering/MoleculeMetadataView.swift | 17 ---- MoleculesTests/PDBFileTests.swift | 9 +- MoleculesTests/RenderingTests.swift | 42 +++++++++ 11 files changed, 282 insertions(+), 54 deletions(-) rename Molecules/{Rendering => Interface}/MoleculeDisplayView.swift (96%) create mode 100644 Molecules/Interface/MoleculeMetadataView.swift delete mode 100644 Molecules/Rendering/MoleculeMetadataView.swift create mode 100644 MoleculesTests/RenderingTests.swift diff --git a/Molecules.xcodeproj/project.pbxproj b/Molecules.xcodeproj/project.pbxproj index 86af458..c726623 100644 --- a/Molecules.xcodeproj/project.pbxproj +++ b/Molecules.xcodeproj/project.pbxproj @@ -38,9 +38,7 @@ BC5F1FB92A5A0D4300416000 /* TransferRNA.pdb in Resources */ = {isa = PBXBuildFile; fileRef = BC5F1FAC2A5A0D4300416000 /* TransferRNA.pdb */; }; BC5F1FBA2A5A0D4300416000 /* TransferRNA.pdb in Resources */ = {isa = PBXBuildFile; fileRef = BC5F1FAC2A5A0D4300416000 /* TransferRNA.pdb */; }; BC5F1FCC2A5A29D400416000 /* MetalRenderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1FCB2A5A29D400416000 /* MetalRenderView.swift */; }; - BC5F1FCD2A5A29D400416000 /* MetalRenderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1FCB2A5A29D400416000 /* MetalRenderView.swift */; }; BC5F1FCF2A5A34FD00416000 /* MoleculeMetadataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1FCE2A5A34FD00416000 /* MoleculeMetadataView.swift */; }; - BC5F1FD02A5A34FD00416000 /* MoleculeMetadataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1FCE2A5A34FD00416000 /* MoleculeMetadataView.swift */; }; BC5F1FD22A5A4B5300416000 /* SDFFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1FD12A5A4B5300416000 /* SDFFile.swift */; }; BC5F1FD52A5A4C5800416000 /* XYZFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1FD42A5A4C5800416000 /* XYZFile.swift */; }; BC5F1FD82A5A4CAB00416000 /* SDFFileTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1FD72A5A4CAB00416000 /* SDFFileTests.swift */; }; @@ -57,6 +55,7 @@ BC5F1FEC2A5B099400416000 /* DNA.pdb.gz in Resources */ = {isa = PBXBuildFile; fileRef = BC5F1FE82A5B099400416000 /* DNA.pdb.gz */; }; BC5F1FEF2A5B51AF00416000 /* SphereRaytracing.metal in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1FEE2A5B51AF00416000 /* SphereRaytracing.metal */; }; BC5F1FF12A5B63B800416000 /* MetalRenderingDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1FF02A5B63B800416000 /* MetalRenderingDevice.swift */; }; + BC5F1FF32A5CD81400416000 /* RenderingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1FF22A5CD81400416000 /* RenderingTests.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -117,6 +116,7 @@ BC5F1FE82A5B099400416000 /* DNA.pdb.gz */ = {isa = PBXFileReference; lastKnownFileType = archive.gzip; name = DNA.pdb.gz; path = TestMolecules/DNA.pdb.gz; sourceTree = ""; }; BC5F1FEE2A5B51AF00416000 /* SphereRaytracing.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = SphereRaytracing.metal; sourceTree = ""; }; BC5F1FF02A5B63B800416000 /* MetalRenderingDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetalRenderingDevice.swift; sourceTree = ""; }; + BC5F1FF22A5CD81400416000 /* RenderingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenderingTests.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -170,6 +170,7 @@ isa = PBXGroup; children = ( BC5807042A56395400313289 /* MoleculesApp.swift */, + BC5F1FF42A62F52800416000 /* Interface */, BC5F1F722A579BCE00416000 /* FileProcessing */, BC5F1FA62A59FB1F00416000 /* Rendering */, BC5F1F6E2A578BA000416000 /* Info.plist */, @@ -194,6 +195,7 @@ BC5807152A56395900313289 /* PDBFileTests.swift */, BC5F1FD72A5A4CAB00416000 /* SDFFileTests.swift */, BC5F1FD92A5A4EA900416000 /* XYZFileTests.swift */, + BC5F1FF22A5CD81400416000 /* RenderingTests.swift */, ); path = MoleculesTests; sourceTree = ""; @@ -243,9 +245,7 @@ BC5F1FA62A59FB1F00416000 /* Rendering */ = { isa = PBXGroup; children = ( - BC5807062A56395400313289 /* MoleculeDisplayView.swift */, BC5F1FCB2A5A29D400416000 /* MetalRenderView.swift */, - BC5F1FCE2A5A34FD00416000 /* MoleculeMetadataView.swift */, BC5F1FF02A5B63B800416000 /* MetalRenderingDevice.swift */, BC5F1FED2A5B421600416000 /* Shaders */, ); @@ -271,6 +271,15 @@ path = Shaders; sourceTree = ""; }; + BC5F1FF42A62F52800416000 /* Interface */ = { + isa = PBXGroup; + children = ( + BC5807062A56395400313289 /* MoleculeDisplayView.swift */, + BC5F1FCE2A5A34FD00416000 /* MoleculeMetadataView.swift */, + ); + path = Interface; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -454,10 +463,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - BC5F1FD02A5A34FD00416000 /* MoleculeMetadataView.swift in Sources */, BC5F1FD82A5A4CAB00416000 /* SDFFileTests.swift in Sources */, BC5F1FDA2A5A4EA900416000 /* XYZFileTests.swift in Sources */, - BC5F1FCD2A5A29D400416000 /* MetalRenderView.swift in Sources */, + BC5F1FF32A5CD81400416000 /* RenderingTests.swift in Sources */, BC5807162A56395900313289 /* PDBFileTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Molecules/FileProcessing/MolecularStructure.swift b/Molecules/FileProcessing/MolecularStructure.swift index 297ee09..c2bcb31 100644 --- a/Molecules/FileProcessing/MolecularStructure.swift +++ b/Molecules/FileProcessing/MolecularStructure.swift @@ -2,6 +2,7 @@ import Foundation /// All molecular file types need to conform to this protocol in order to be rendered. protocol MolecularStructure { + // TODO: Support multiple structures. var atoms: [Atom] { get } var bonds: [Bond] { get } var centerOfMass: Coordinate { get } @@ -9,9 +10,23 @@ protocol MolecularStructure { var maximumLimits: Coordinate { get } var suggestedScaleFactor: Coordinate { get } + var structureCount: Int { get } + var metadata: MolecularMetadata? { get } + init(data: Data) throws } + +/// Protein Data Bank files contain a lot of structure and publication metadata. +struct MolecularMetadata { + let title: String + let compound: String + let authors: String + let source: String + let journal: String + let sequence: String +} + /// To aid in scaling and other rendering tasks, statistics of a molecule are captured during /// parsing. This helps aggregate commonly-used operations in a shared data structure. struct MoleculeStatistics { diff --git a/Molecules/FileProcessing/PDBFile.swift b/Molecules/FileProcessing/PDBFile.swift index 027bf2e..2399171 100644 --- a/Molecules/FileProcessing/PDBFile.swift +++ b/Molecules/FileProcessing/PDBFile.swift @@ -14,6 +14,8 @@ struct PDBFile: MolecularStructure { let minimumLimits: Coordinate let maximumLimits: Coordinate let suggestedScaleFactor: Coordinate + let metadata: MolecularMetadata? + let structureCount: Int init(data: Data) throws { guard let fileContents = String(data: data, encoding: .utf8) else { @@ -24,6 +26,14 @@ struct PDBFile: MolecularStructure { var parsedBonds: [Bond] = [] var globalAtomLookup: [Int: Atom] = [:] var statistics = MoleculeStatistics() + var stillCountingAtomsInFirstStructure = true + var numberOfStructures = 1 + var title = "" + var source = "" + var compound = "" + var authors = "" + var journalReference = "" + var sequence = "" let lines = fileContents.components(separatedBy: "\n") for line in lines { @@ -34,6 +44,7 @@ struct PDBFile: MolecularStructure { print("Identifier: |\(lineIdentifier)|") switch lineIdentifier { case "ATOM", "HETATM": + guard stillCountingAtomsInFirstStructure else { continue } // TODO: If ATOM, insert bonds for previous residue when switching residues. // Grab the X, Y, Z coordinates of the atom. guard line.count >= 54 else { continue } @@ -63,6 +74,7 @@ struct PDBFile: MolecularStructure { print("Atom detected: \(newAtom)") case "TER": break case "CONECT": + guard stillCountingAtomsInFirstStructure else { continue } // TODO: Properly handle bidirectional bonds. guard line.count >= 11 else { continue } guard let firstAtomSerial = Int(line.whitespaceTrimmedString(from: 6, to: 11)), @@ -107,26 +119,68 @@ struct PDBFile: MolecularStructure { parsedBonds.append(fourthBond) print("Bond detected: \(fourthBond)") - case "MODEL": break - case "ENDMDL": break - case "TITLE": break - case "COMPND": break - case "SOURCE": break - case "AUTHOR": break - case "JRNL": break - case "SEQRES": break + case "MODEL": + guard line.count >= 16 else { continue } + guard let currentStructureNumber = Int(line.whitespaceTrimmedString(from: 12, to: 16)) else { + continue + } + numberOfStructures = max(numberOfStructures, currentStructureNumber) + case "ENDMDL": + stillCountingAtomsInFirstStructure = false + case "TITLE": + guard line.count >= 10 else { continue } + let titleLine = line.dropFirst(10) + title += titleLine + case "COMPND": + guard line.count >= 20 else { continue } + let compoundIdentifier = line.whitespaceTrimmedString(from: 10, to: 20) + if compoundIdentifier == "MOLECULE:" { + compound = String(line.dropFirst(20)) + } + case "SOURCE": + guard line.count >= 10 else { continue } + let sourceLine = line.dropFirst(10) + source += sourceLine + case "AUTHOR": + guard line.count >= 10 else { continue } + let authorLine = line.dropFirst(10) + authors += authorLine + case "JRNL": + guard line.count >= 18 else { continue } + let journalIdentifier = line.whitespaceTrimmedString(from: 12, to: 16) + switch journalIdentifier { + case "REF", "REFN": + let journalReferenceLine = line.dropFirst(18) + journalReference += journalReferenceLine + // TODO: Do something else with these journal fields. + case "AUTH": break + case "TITL": break + default: break + } + case "SEQRES": + guard line.count >= 14 else { continue } + let sequenceLine = line.dropFirst(14) + sequence += sequenceLine default: break } } guard parsedAtoms.count > 0 else { throw PDBFileError.emptyFile } + self.structureCount = numberOfStructures self.atoms = parsedAtoms self.bonds = parsedBonds self.centerOfMass = statistics.calculatedCenterOfMass(atomCount: parsedAtoms.count) self.minimumLimits = statistics.minimumLimits self.maximumLimits = statistics.maximumLimits self.suggestedScaleFactor = statistics.calculatedScaleFactor() + self.metadata = MolecularMetadata( + title: title, + compound: compound, + authors: authors, + source: source, + journal: journalReference, + sequence: sequence) } } diff --git a/Molecules/FileProcessing/SDFFile.swift b/Molecules/FileProcessing/SDFFile.swift index c866570..6858c37 100644 --- a/Molecules/FileProcessing/SDFFile.swift +++ b/Molecules/FileProcessing/SDFFile.swift @@ -9,6 +9,8 @@ struct SDFFile: MolecularStructure { let minimumLimits: Coordinate let maximumLimits: Coordinate let suggestedScaleFactor: Coordinate + let metadata: MolecularMetadata? = nil + let structureCount: Int = 1 init(data: Data) throws { guard let fileContents = String(data: data, encoding: .utf8) else { diff --git a/Molecules/FileProcessing/XYZFile.swift b/Molecules/FileProcessing/XYZFile.swift index 2730568..ef1dceb 100644 --- a/Molecules/FileProcessing/XYZFile.swift +++ b/Molecules/FileProcessing/XYZFile.swift @@ -8,6 +8,8 @@ struct XYZFile: MolecularStructure { let minimumLimits: Coordinate let maximumLimits: Coordinate let suggestedScaleFactor: Coordinate + let metadata: MolecularMetadata? = nil + let structureCount: Int = 1 init(data: Data) throws { guard let fileContents = String(data: data, encoding: .utf8) else { @@ -44,7 +46,6 @@ struct XYZFile: MolecularStructure { throw PDBFileError.emptyFile } self.atoms = parsedAtoms - self.bonds = parsedBonds self.centerOfMass = statistics.calculatedCenterOfMass(atomCount: parsedAtoms.count) self.minimumLimits = statistics.minimumLimits self.maximumLimits = statistics.maximumLimits diff --git a/Molecules/Rendering/MoleculeDisplayView.swift b/Molecules/Interface/MoleculeDisplayView.swift similarity index 96% rename from Molecules/Rendering/MoleculeDisplayView.swift rename to Molecules/Interface/MoleculeDisplayView.swift index 49f1887..5193adf 100644 --- a/Molecules/Rendering/MoleculeDisplayView.swift +++ b/Molecules/Interface/MoleculeDisplayView.swift @@ -36,7 +36,7 @@ struct MoleculeDisplayView: View { } ToolbarItem(placement: .navigationBarTrailing) { NavigationLink { - MoleculeMetadataView() + MoleculeMetadataView(molecule: document.molecule) } label: { // TODO: Popover on iPad? Image(systemName: "info.circle") diff --git a/Molecules/Interface/MoleculeMetadataView.swift b/Molecules/Interface/MoleculeMetadataView.swift new file mode 100644 index 0000000..8c6818a --- /dev/null +++ b/Molecules/Interface/MoleculeMetadataView.swift @@ -0,0 +1,66 @@ +import SwiftUI + +struct MoleculeMetadataView: View { + let molecule: MolecularStructure + + var body: some View { + List { + // TODO: Titlecase these strings. + if let metadata = molecule.metadata { + Section(header: Text("Name")) { + Text(metadata.compound) + } + Section(header: Text("Description")) { + Text(metadata.title) + } + } + Section(header: Text("Statistics")) { + MoleculeStatisticsRow(title: "Number of atoms", value: molecule.atoms.count) + MoleculeStatisticsRow(title: "Number of bonds", value: molecule.bonds.count) + MoleculeStatisticsRow(title: "Number of structures", value: molecule.structureCount) + MoleculeStatisticsRow(title: "Current structure", value: 1) + } + if let metadata = molecule.metadata { + Section(header: Text("Author(s)")) { + Text(metadata.authors) + } + Section(header: Text("Journal")) { + Text(metadata.journal) + } + Section(header: Text("Source")) { + Text(metadata.source) + } + Section(header: Text("Sequence")) { + Text(metadata.sequence) + } + } + } + .padding() + } +} + +struct MoleculeStatisticsRow: View { + let title: String + let value: Int + + var body: some View { + HStack { + Text(title) + Spacer() + Text("\(value)").foregroundColor(.secondary) + } + } +} + +struct MoleculeMetadataView_Previews: PreviewProvider { + static var previews: some View { + let data = try! Bundle.main.loadData(forResource: "DNA", withExtension: "pdb") + let document = try! MoleculeDocument(data: data, contentType: .pdb, filename: "DNA.pdb") + NavigationStack { + MoleculeMetadataView(molecule: document.molecule) + .navigationTitle("DNA") + .toolbarRole(.automatic) + .navigationBarTitleDisplayMode(.inline) + } + } +} diff --git a/Molecules/Rendering/MetalRenderView.swift b/Molecules/Rendering/MetalRenderView.swift index a067868..3ee5782 100644 --- a/Molecules/Rendering/MetalRenderView.swift +++ b/Molecules/Rendering/MetalRenderView.swift @@ -6,7 +6,9 @@ public class MetalRenderView: MTKView { var molecule: MolecularStructure! var moleculeVertexBuffer: MTLBuffer! - + var moleculeImpostorSpaceCoordinateBuffer: MTLBuffer! + var moleculeIndexBuffer: MTLBuffer! + public override init(frame frameRect: CGRect, device: MTLDevice?) { super.init(frame: frameRect, device: sharedMetalRenderingDevice.device) @@ -73,27 +75,37 @@ struct MetalView: UIViewRepresentable { extension MetalRenderView { func initializeMoleculeBuffers() { - let moleculeVertices: [Float] = [ - 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0 - ] - + var moleculeIndices: [UInt16] = [] + var moleculeVertices: [Float] = [] + var moleculeImpostorSpaceCoordinates: [Float] = [] + + var currentIndex: UInt16 = 0 + // TODO: Loop through all atoms. + appendOctagonVertices( + position: Coordinate(x: 0.0, y: 0.0, z: 0.0), + currentIndex: ¤tIndex, + indices: &moleculeIndices, + vertices: &moleculeVertices, + impostorSpaceCoordinates: &moleculeImpostorSpaceCoordinates) + + moleculeIndexBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: moleculeIndices, + length: moleculeIndices.count * MemoryLayout.size, + options: [])! + // const device packed_float3 *position [[buffer(0)]], - let vertexBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: moleculeVertices, - length: moleculeVertices.count * MemoryLayout.size, - options: [])! + moleculeVertexBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: moleculeVertices, + length: moleculeVertices.count * MemoryLayout.size, + options: [])! + // const device packed_float2 *inputImpostorSpaceCoordinate [[buffer(1)]], - let moleculeVertices: [Float] = [ - 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0, - 0.0, 0.0, 0.0 - ] - -// constant SphereRaytracingVertexUniform& uniform [[buffer(3)]], + moleculeImpostorSpaceCoordinateBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: moleculeImpostorSpaceCoordinates, + length: moleculeImpostorSpaceCoordinates.count * MemoryLayout.size, + options: [])! + + + // TODO: Complete cylinder impostors. + print("Number of atoms to initialize: \(molecule.atoms.count)") } @@ -101,3 +113,43 @@ extension MetalRenderView { } } + +func appendOctagonVertices( + position: Coordinate, + currentIndex: inout UInt16, + indices: inout [UInt16], + vertices: inout [Float], + impostorSpaceCoordinates: inout [Float] +) { + let vertex = [-position.x, position.y, position.z] + for _ in 0..<8 { + vertices.append(contentsOf: vertex) + } + + let positiveSideComponent: Float = 1.0 - 2.0 / (sqrt(2.0) + 2.0) + let negativeSideComponent: Float = -1.0 + 2.0 / (sqrt(2.0) + 2.0); + let octagonPoints: [Float] = [ + negativeSideComponent, 1.0, + -1.0, negativeSideComponent, + 1.0, positiveSideComponent, + positiveSideComponent, -1.0, + 1.0, negativeSideComponent, + positiveSideComponent, 1.0, + -1.0, positiveSideComponent, + negativeSideComponent, -1.0 + ] + impostorSpaceCoordinates += octagonPoints + + // 123, 324, 345, 136, 217, 428 + let octagonIndices: [UInt16] = [ + currentIndex, currentIndex + 1, currentIndex + 2, + currentIndex + 2, currentIndex, currentIndex + 3, + currentIndex + 2, currentIndex + 3, currentIndex + 4, + currentIndex, currentIndex + 2, currentIndex + 5, + currentIndex + 1, currentIndex, currentIndex + 6, + currentIndex + 3, currentIndex + 1, currentIndex + 7 + ] + indices += octagonIndices + + currentIndex += 8 +} diff --git a/Molecules/Rendering/MoleculeMetadataView.swift b/Molecules/Rendering/MoleculeMetadataView.swift deleted file mode 100644 index 4967d6d..0000000 --- a/Molecules/Rendering/MoleculeMetadataView.swift +++ /dev/null @@ -1,17 +0,0 @@ -import SwiftUI - -struct MoleculeMetadataView: View { - - var body: some View { - VStack { - Text("Hello world") - } - .padding() - } -} - -struct MoleculeMetadataView_Previews: PreviewProvider { - static var previews: some View { - return MoleculeMetadataView() - } -} diff --git a/MoleculesTests/PDBFileTests.swift b/MoleculesTests/PDBFileTests.swift index fabc9a7..f3054ee 100644 --- a/MoleculesTests/PDBFileTests.swift +++ b/MoleculesTests/PDBFileTests.swift @@ -1,8 +1,6 @@ import XCTest @testable import Molecules -// TODO: Add a suite of test molecules that are only used in unit tests. - final class PDBFileTests: XCTestCase { func testLoadCaffeine() throws { @@ -48,5 +46,12 @@ final class PDBFileTests: XCTestCase { for atom in dna.atoms { XCTAssert(atom.element != .unknown) } + let metadata = try XCTUnwrap(dna.metadata) + XCTAssertGreaterThan(metadata.title.count, 0) + XCTAssertGreaterThan(metadata.compound.count, 0) + XCTAssertGreaterThan(metadata.authors.count, 0) + XCTAssertGreaterThan(metadata.source.count, 0) + XCTAssertGreaterThan(metadata.journal.count, 0) + XCTAssertGreaterThan(metadata.sequence.count, 0) } } diff --git a/MoleculesTests/RenderingTests.swift b/MoleculesTests/RenderingTests.swift new file mode 100644 index 0000000..cc66aa7 --- /dev/null +++ b/MoleculesTests/RenderingTests.swift @@ -0,0 +1,42 @@ +import XCTest +@testable import Molecules + +final class RenderingTests: XCTestCase { + + func testOctagonCoordinates() throws { + var currentIndex: UInt16 = 0 + var indices: [UInt16] = [] + var vertices: [Float] = [] + var impostorSpaceCoordinates: [Float] = [] + + let position = Coordinate(x: -1.0, y: 2.0, z: 3.0) + + appendOctagonVertices( + position: position, + currentIndex: ¤tIndex, + indices: &indices, + vertices: &vertices, + impostorSpaceCoordinates: &impostorSpaceCoordinates) + + XCTAssertEqual(currentIndex, 8) + XCTAssertEqual(indices.count, 18) + XCTAssertEqual(vertices.count, 24) + XCTAssertEqual(impostorSpaceCoordinates.count, 16) + + appendOctagonVertices( + position: position, + currentIndex: ¤tIndex, + indices: &indices, + vertices: &vertices, + impostorSpaceCoordinates: &impostorSpaceCoordinates) + + XCTAssertEqual(currentIndex, 16) + XCTAssertEqual(indices.count, 36) + XCTAssertEqual(vertices.count, 48) + XCTAssertEqual(impostorSpaceCoordinates.count, 32) + + XCTAssertEqual(vertices[3], 1.0) + XCTAssertEqual(vertices[4], 2.0) + XCTAssertEqual(vertices[5], 3.0) + } +} From f12b3dec53693adaa9a31b7837086a9d41938978 Mon Sep 17 00:00:00 2001 From: Brad Larson Date: Sun, 16 Jul 2023 21:07:31 -0500 Subject: [PATCH 3/9] Completed parsing of PDB files. --- Molecules.xcodeproj/project.pbxproj | 4 + .../FileProcessing/AminoAcidResidue.swift | 266 ++++++++++++++++++ Molecules/FileProcessing/PDBFile.swift | 58 +++- .../Interface/MoleculeMetadataView.swift | 9 + MoleculesTests/PDBFileTests.swift | 56 +++- 5 files changed, 381 insertions(+), 12 deletions(-) create mode 100644 Molecules/FileProcessing/AminoAcidResidue.swift diff --git a/Molecules.xcodeproj/project.pbxproj b/Molecules.xcodeproj/project.pbxproj index c726623..7551d47 100644 --- a/Molecules.xcodeproj/project.pbxproj +++ b/Molecules.xcodeproj/project.pbxproj @@ -56,6 +56,7 @@ BC5F1FEF2A5B51AF00416000 /* SphereRaytracing.metal in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1FEE2A5B51AF00416000 /* SphereRaytracing.metal */; }; BC5F1FF12A5B63B800416000 /* MetalRenderingDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1FF02A5B63B800416000 /* MetalRenderingDevice.swift */; }; BC5F1FF32A5CD81400416000 /* RenderingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1FF22A5CD81400416000 /* RenderingTests.swift */; }; + BCF265E32A6377D300374DB0 /* AminoAcidResidue.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCF265E22A6377D300374DB0 /* AminoAcidResidue.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -117,6 +118,7 @@ BC5F1FEE2A5B51AF00416000 /* SphereRaytracing.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = SphereRaytracing.metal; sourceTree = ""; }; BC5F1FF02A5B63B800416000 /* MetalRenderingDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetalRenderingDevice.swift; sourceTree = ""; }; BC5F1FF22A5CD81400416000 /* RenderingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenderingTests.swift; sourceTree = ""; }; + BCF265E22A6377D300374DB0 /* AminoAcidResidue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AminoAcidResidue.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -238,6 +240,7 @@ BC5F1F792A58EB5500416000 /* Atom.swift */, BC5F1F7C2A58EB6300416000 /* Bond.swift */, BC5F1F7F2A58EC5F00416000 /* Coordinate.swift */, + BCF265E22A6377D300374DB0 /* AminoAcidResidue.swift */, ); path = FileProcessing; sourceTree = ""; @@ -455,6 +458,7 @@ BC5F1FA42A59E96E00416000 /* MoleculeDocument.swift in Sources */, BC5F1FEF2A5B51AF00416000 /* SphereRaytracing.metal in Sources */, BC5F1FCC2A5A29D400416000 /* MetalRenderView.swift in Sources */, + BCF265E32A6377D300374DB0 /* AminoAcidResidue.swift in Sources */, BC5807052A56395400313289 /* MoleculesApp.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Molecules/FileProcessing/AminoAcidResidue.swift b/Molecules/FileProcessing/AminoAcidResidue.swift new file mode 100644 index 0000000..01be915 --- /dev/null +++ b/Molecules/FileProcessing/AminoAcidResidue.swift @@ -0,0 +1,266 @@ +/// Despite the name, this handles both amino acid residues and nucleic acid residues as present +/// in PDB files. +enum AminoAcidResidue: String { + // Amino acids + case glycine = "GLY" + case alanine = "ALA" + case valine = "VAL" + case leucine = "LEU" + case isoleucine = "ILE" + case serine = "SER" + case cysteine = "CYS" + case threonine = "THR" + case methionine = "MET" + case proline = "PRO" + case phenylalanine = "PHE" + case tyrosine = "TYR" + case tryptophan = "TRP" + case histidine = "HIS" + case lysine = "LYS" + case arginine = "ARG" + case asparticAcid = "ASP" + case glutamicAcid = "GLU" + case asparagine = "ASN" + case glutamine = "GLN" + + // RNA + case adenine = "A" + case cytosine = "C" + case guanine = "G" + case uracil = "U" + + // DNA + case deoxyadenine = "DA" + case deoxycytosine = "DC" + case deoxyguanine = "DG" + case deoxythymine = "DT" +} + +extension AminoAcidResidue { + func bonds(residueAtoms: inout [String: Atom], previousTerminalAtom: inout Atom?) -> [Bond] { + var residueBonds: [Bond] = [] + + func addBond(from: String, to: String) { + if let firstAtom = residueAtoms[from], + let secondAtom = residueAtoms[to] { + residueBonds.append(Bond(strength: .single, start: firstAtom.location, end: secondAtom.location)) + } + } + + // First, common bonds for groupings of residues. + switch self { + // Amino acids + case .glycine, .alanine, .valine, .leucine, .isoleucine, .serine, .cysteine, .threonine, + .methionine, .proline, .phenylalanine, .tyrosine, .tryptophan, .histidine, .lysine, + .arginine, .asparticAcid, .glutamicAcid, .asparagine, .glutamine: + addBond(from: "N", to: "CA") + addBond(from: "CA", to: "C") + addBond(from: "C", to: "O") + + // Add the peptide bond. + if let terminalAtom = previousTerminalAtom, + let secondAtom = residueAtoms["N"] { + residueBonds.append(Bond(strength: .single, start: terminalAtom.location, end: secondAtom.location)) + } + previousTerminalAtom = residueAtoms["C"] + + // RNA + case .adenine, .cytosine, .guanine, .uracil: + addBond(from: "C2'", to: "O2'") + + // DNA + case .deoxyadenine, .deoxycytosine, .deoxyguanine, .deoxythymine: + // P -> O3' (Starts from 3' end, so no P in first nucleotide). + addBond(from: "P", to: "OP1") + addBond(from: "P", to: "OP2") + addBond(from: "P", to: "O5'") + addBond(from: "O5'", to: "C5'") + addBond(from: "C5'", to: "C4'") + addBond(from: "C4'", to: "O4'") + addBond(from: "C4'", to: "C3'") + addBond(from: "C3'", to: "O3'") + addBond(from: "O4'", to: "C1'") + addBond(from: "C3'", to: "C2'") + addBond(from: "C2'", to: "C1'") + + // Link the nucleotides together. + if let terminalAtom = previousTerminalAtom, + let secondAtom = residueAtoms["P"] { + residueBonds.append(Bond(strength: .single, start: terminalAtom.location, end: secondAtom.location)) + } + previousTerminalAtom = residueAtoms["O3'"] + } + + // Then, all residue-specific bonds. + switch self { + // Amino acids + case .alanine: + addBond(from: "CA", to: "CB") + case .valine: + addBond(from: "CA", to: "CB") + addBond(from: "CB", to: "CG1") + addBond(from: "CB", to: "CG2") + case .leucine: + addBond(from: "CA", to: "CB") + addBond(from: "CB", to: "CG") + addBond(from: "CG", to: "CD1") + addBond(from: "CG", to: "CD2") + case .isoleucine: + addBond(from: "CA", to: "CB") + addBond(from: "CB", to: "CG1") + addBond(from: "CB", to: "CG2") + addBond(from: "CG1", to: "CD1") + case .serine: + addBond(from: "CA", to: "CB") + addBond(from: "CB", to: "OG") + case .cysteine: + addBond(from: "CA", to: "CB") + addBond(from: "CB", to: "SG") + case .threonine: + addBond(from: "CA", to: "CB") + addBond(from: "CB", to: "OG1") + addBond(from: "CB", to: "CG2") + case .methionine: + addBond(from: "CA", to: "CB") + addBond(from: "CB", to: "CG") + addBond(from: "CG", to: "SD") + addBond(from: "SD", to: "CE") + case .proline: + addBond(from: "CA", to: "CB") + addBond(from: "CB", to: "CG") + addBond(from: "CG", to: "CD") + addBond(from: "CD", to: "N") + case .phenylalanine: + addBond(from: "CA", to: "CB") + addBond(from: "CB", to: "CG") + addBond(from: "CG", to: "CD1") + addBond(from: "CG", to: "CD2") + addBond(from: "CD1", to: "CE1") + addBond(from: "CD2", to: "CE2") + addBond(from: "CE1", to: "CZ") + addBond(from: "CE2", to: "CZ") + case .tyrosine: + addBond(from: "CA", to: "CB") + addBond(from: "CB", to: "CG") + addBond(from: "CG", to: "CD1") + addBond(from: "CG", to: "CD2") + addBond(from: "CD1", to: "CE1") + addBond(from: "CD2", to: "CE2") + addBond(from: "CE1", to: "CZ") + addBond(from: "CE2", to: "CZ") + addBond(from: "CZ", to: "OH") + case .tryptophan: + addBond(from: "CA", to: "CB") + addBond(from: "CB", to: "CG") + addBond(from: "CG", to: "CD1") + addBond(from: "CG", to: "CD2") + addBond(from: "CD1", to: "NE1") + addBond(from: "CD2", to: "CE2") + addBond(from: "NE1", to: "CE2") + addBond(from: "CE2", to: "CZ2") + addBond(from: "CZ2", to: "CH2") + addBond(from: "CH2", to: "CZ3") + addBond(from: "CZ3", to: "CE3") + addBond(from: "CE3", to: "CD2") + case .histidine: + addBond(from: "CA", to: "CB") + addBond(from: "CB", to: "CG") + addBond(from: "CG", to: "ND1") + addBond(from: "CG", to: "CD2") + addBond(from: "ND1", to: "CE1") + addBond(from: "CD2", to: "NE2") + addBond(from: "CE1", to: "NE2") + case .lysine: + addBond(from: "CA", to: "CB") + addBond(from: "CB", to: "CG") + addBond(from: "CG", to: "CD") + addBond(from: "CD", to: "CE") + addBond(from: "CE", to: "NZ") + case .arginine: + addBond(from: "CA", to: "CB") + addBond(from: "CB", to: "CG") + addBond(from: "CG", to: "CD") + addBond(from: "CD", to: "NE") + addBond(from: "NE", to: "CZ") + addBond(from: "CZ", to: "NH1") + addBond(from: "CZ", to: "NH2") + case .asparticAcid: + addBond(from: "CA", to: "CB") + addBond(from: "CB", to: "CG") + addBond(from: "CG", to: "OD1") + addBond(from: "CG", to: "OD2") + case .glutamicAcid: + addBond(from: "CA", to: "CB") + addBond(from: "CB", to: "CG") + addBond(from: "CG", to: "CD") + addBond(from: "CD", to: "OE1") + addBond(from: "CD", to: "OE2") + case .asparagine: + addBond(from: "CA", to: "CB") + addBond(from: "CB", to: "CG") + addBond(from: "CG", to: "OD1") + addBond(from: "CG", to: "ND2") + case .glutamine: + addBond(from: "CA", to: "CB") + addBond(from: "CB", to: "CG") + addBond(from: "CG", to: "CD") + addBond(from: "CD", to: "OE1") + addBond(from: "CD", to: "NE2") + case .glycine: + break + + // Nucleic acids + case .adenine, .deoxyadenine: + addBond(from: "C1'", to: "N9") + addBond(from: "N9", to: "C4") + addBond(from: "C4", to: "N3") + addBond(from: "N3", to: "C2") + addBond(from: "C2", to: "N1") + addBond(from: "N1", to: "C6") + addBond(from: "C6", to: "N6") + addBond(from: "C6", to: "C5") + addBond(from: "C5", to: "C4") + addBond(from: "C5", to: "N7") + addBond(from: "N7", to: "C8") + addBond(from: "C8", to: "N9") + case .cytosine, .deoxycytosine: + addBond(from: "C1'", to: "N1") + addBond(from: "N1", to: "C2") + addBond(from: "C2", to: "O2") + addBond(from: "C2", to: "N3") + addBond(from: "N3", to: "C4") + addBond(from: "C4", to: "N4") + addBond(from: "C4", to: "C5") + addBond(from: "C5", to: "C6") + addBond(from: "C6", to: "N1") + case .guanine, .deoxyguanine: + addBond(from: "C1'", to: "N9") + addBond(from: "N9", to: "C4") + addBond(from: "C4", to: "N3") + addBond(from: "N3", to: "C2") + addBond(from: "C2", to: "N2") + addBond(from: "C2", to: "N1") + addBond(from: "N1", to: "C6") + addBond(from: "C6", to: "O6") + addBond(from: "C6", to: "C5") + addBond(from: "C5", to: "C4") + addBond(from: "C5", to: "N7") + addBond(from: "N7", to: "C8") + addBond(from: "C8", to: "N9") + case .deoxythymine: + addBond(from: "C5", to: "C7") + case .uracil: + addBond(from: "C1'", to: "N1") + addBond(from: "N1", to: "C2") + addBond(from: "C2", to: "O2") + addBond(from: "C2", to: "N3") + addBond(from: "N3", to: "C4") + addBond(from: "C4", to: "O4") + addBond(from: "C4", to: "C5") + addBond(from: "C5", to: "C6") + addBond(from: "C6", to: "N1") + } + residueAtoms.removeAll(keepingCapacity: true) + return residueBonds + } +} diff --git a/Molecules/FileProcessing/PDBFile.swift b/Molecules/FileProcessing/PDBFile.swift index 2399171..98d7550 100644 --- a/Molecules/FileProcessing/PDBFile.swift +++ b/Molecules/FileProcessing/PDBFile.swift @@ -26,6 +26,10 @@ struct PDBFile: MolecularStructure { var parsedBonds: [Bond] = [] var globalAtomLookup: [Int: Atom] = [:] var statistics = MoleculeStatistics() + var currentResidue = 1 + var currentResidueType: AminoAcidResidue? + var currentResidueAtoms: [String: Atom] = [:] + var previousTerminalAtom: Atom? var stillCountingAtomsInFirstStructure = true var numberOfStructures = 1 var title = "" @@ -41,11 +45,37 @@ struct PDBFile: MolecularStructure { guard line.count >= 6 else { continue } let lineIdentifier = line.prefix(6).trimmingCharacters(in: .whitespacesAndNewlines) - print("Identifier: |\(lineIdentifier)|") switch lineIdentifier { case "ATOM", "HETATM": guard stillCountingAtomsInFirstStructure else { continue } - // TODO: If ATOM, insert bonds for previous residue when switching residues. + // At the start of a new residue, generate all bonds for the previous one. + if lineIdentifier == "ATOM" { + guard line.count >= 27 else { continue } + if let residueIdentifier = Int(line.whitespaceTrimmedString(from: 22, to: 27)), + currentResidue != residueIdentifier { + if let residueBonds = currentResidueType?.bonds( + residueAtoms: ¤tResidueAtoms, + previousTerminalAtom: &previousTerminalAtom) { + parsedBonds.append(contentsOf: residueBonds) + } + currentResidue = residueIdentifier + currentResidueType = AminoAcidResidue(rawValue: line.whitespaceTrimmedString(from: 17, to: 20)) + if currentResidueType == nil { + print("Unknown residue: \(line.whitespaceTrimmedString(from: 17, to: 20))") + } + } + } else { + // Throw out water oxygens from biomolecule structures. + if line.whitespaceTrimmedString(from: 17, to: 20) == "HOH" { + continue + } + if let residueBonds = currentResidueType?.bonds( + residueAtoms: ¤tResidueAtoms, + previousTerminalAtom: &previousTerminalAtom) { + parsedBonds.append(contentsOf: residueBonds) + } + currentResidueType = nil + } // Grab the X, Y, Z coordinates of the atom. guard line.count >= 54 else { continue } guard let xCoordinate = Float(line.whitespaceTrimmedString(from: 30, to: 38)), @@ -68,11 +98,19 @@ struct PDBFile: MolecularStructure { } let element = Atom.Element(code: elementIdentifier) let newAtom = Atom(element: element, location: atomCoordinate) + if lineIdentifier == "ATOM" { + currentResidueAtoms[line.whitespaceTrimmedString(from: 12, to: 16)] = newAtom + } parsedAtoms.append(newAtom) globalAtomLookup[atomSerialNumber] = newAtom - - print("Atom detected: \(newAtom)") - case "TER": break + case "TER": + if let residueBonds = currentResidueType?.bonds( + residueAtoms: ¤tResidueAtoms, + previousTerminalAtom: &previousTerminalAtom) { + parsedBonds.append(contentsOf: residueBonds) + } + currentResidueType = nil + break case "CONECT": guard stillCountingAtomsInFirstStructure else { continue } // TODO: Properly handle bidirectional bonds. @@ -90,7 +128,6 @@ struct PDBFile: MolecularStructure { } let firstBond = Bond(strength: .single, start: firstAtom.location, end: secondAtom.location) parsedBonds.append(firstBond) - print("Bond detected: \(firstBond)") guard line.count >= 21 else { continue } guard let thirdAtomSerial = Int(line.whitespaceTrimmedString(from: 16, to: 21)), thirdAtomSerial > 0, @@ -99,7 +136,6 @@ struct PDBFile: MolecularStructure { } let secondBond = Bond(strength: .single, start: firstAtom.location, end: thirdAtom.location) parsedBonds.append(secondBond) - print("Bond detected: \(secondBond)") guard line.count >= 26 else { continue } guard let fourthAtomSerial = Int(line.whitespaceTrimmedString(from: 21, to: 26)), fourthAtomSerial > 0, @@ -108,7 +144,6 @@ struct PDBFile: MolecularStructure { } let thirdBond = Bond(strength: .single, start: firstAtom.location, end: fourthAtom.location) parsedBonds.append(thirdBond) - print("Bond detected: \(thirdBond)") guard line.count >= 31 else { continue } guard let fifthAtomSerial = Int(line.whitespaceTrimmedString(from: 26, to: 31)), fifthAtomSerial > 0, @@ -117,7 +152,6 @@ struct PDBFile: MolecularStructure { } let fourthBond = Bond(strength: .single, start: firstAtom.location, end: fifthAtom.location) parsedBonds.append(fourthBond) - print("Bond detected: \(fourthBond)") case "MODEL": guard line.count >= 16 else { continue } @@ -167,6 +201,12 @@ struct PDBFile: MolecularStructure { guard parsedAtoms.count > 0 else { throw PDBFileError.emptyFile } + if let residueBonds = currentResidueType?.bonds( + residueAtoms: ¤tResidueAtoms, + previousTerminalAtom: &previousTerminalAtom) { + parsedBonds.append(contentsOf: residueBonds) + } + currentResidueType = nil self.structureCount = numberOfStructures self.atoms = parsedAtoms self.bonds = parsedBonds diff --git a/Molecules/Interface/MoleculeMetadataView.swift b/Molecules/Interface/MoleculeMetadataView.swift index 8c6818a..5cc71ff 100644 --- a/Molecules/Interface/MoleculeMetadataView.swift +++ b/Molecules/Interface/MoleculeMetadataView.swift @@ -62,5 +62,14 @@ struct MoleculeMetadataView_Previews: PreviewProvider { .toolbarRole(.automatic) .navigationBarTitleDisplayMode(.inline) } + + let buckyData = try! Bundle.main.loadData(forResource: "Buckminsterfullerene", withExtension: "sdf") + let buckyDocument = try! MoleculeDocument(data: buckyData, contentType: .sdf, filename: "Buckminsterfullerene.sdf") + NavigationStack { + MoleculeMetadataView(molecule: buckyDocument.molecule) + .navigationTitle("Buckminsterfullerene") + .toolbarRole(.automatic) + .navigationBarTitleDisplayMode(.inline) + } } } diff --git a/MoleculesTests/PDBFileTests.swift b/MoleculesTests/PDBFileTests.swift index f3054ee..0403964 100644 --- a/MoleculesTests/PDBFileTests.swift +++ b/MoleculesTests/PDBFileTests.swift @@ -40,9 +40,8 @@ final class PDBFileTests: XCTestCase { let fileData = try Bundle.main.loadData(forResource: "DNA", withExtension: ".pdb") let dna = try PDBFile(data: fileData) - XCTAssertEqual(dna.atoms.count, 206) - // TODO: Reduce this to the actual number of bonds, once bidirectional bonds are corrected. - XCTAssertEqual(dna.bonds.count, 556) + XCTAssertEqual(dna.atoms.count, 486) + XCTAssertEqual(dna.bonds.count, 490) for atom in dna.atoms { XCTAssert(atom.element != .unknown) } @@ -54,4 +53,55 @@ final class PDBFileTests: XCTestCase { XCTAssertGreaterThan(metadata.journal.count, 0) XCTAssertGreaterThan(metadata.sequence.count, 0) } + + func testLoadRNA() throws { + let fileData = try Bundle.main.loadData(forResource: "TransferRNA", withExtension: ".pdb") + + let rna = try PDBFile(data: fileData) + XCTAssertEqual(rna.atoms.count, 1656) + XCTAssertEqual(rna.bonds.count, 1467) + for atom in rna.atoms { + XCTAssert(atom.element != .unknown) + } + let metadata = try XCTUnwrap(rna.metadata) + XCTAssertGreaterThan(metadata.title.count, 0) + XCTAssertGreaterThan(metadata.compound.count, 0) + XCTAssertGreaterThan(metadata.authors.count, 0) + XCTAssertGreaterThan(metadata.source.count, 0) + XCTAssertGreaterThan(metadata.journal.count, 0) + XCTAssertGreaterThan(metadata.sequence.count, 0) + } + + func testLoadInsulin() throws { + let fileData = try Bundle.main.loadData(forResource: "Insulin", withExtension: ".pdb") + + let insulin = try PDBFile(data: fileData) + XCTAssertEqual(insulin.atoms.count, 803) + XCTAssertEqual(insulin.bonds.count, 815) + for atom in insulin.atoms { + XCTAssert(atom.element != .unknown) + } + let metadata = try XCTUnwrap(insulin.metadata) + XCTAssertGreaterThan(metadata.title.count, 0) + XCTAssertGreaterThan(metadata.compound.count, 0) + XCTAssertGreaterThan(metadata.authors.count, 0) + XCTAssertGreaterThan(metadata.source.count, 0) + XCTAssertGreaterThan(metadata.journal.count, 0) + XCTAssertGreaterThan(metadata.sequence.count, 0) + } + + func testAminoAcidBonds() { + let glycine = AminoAcidResidue.glycine + var glycineAtoms = [ + "N": Atom(element: .nitrogen, location: Coordinate(x: 1.0, y: 2.0, z: 3.0)), + "CA": Atom(element: .carbon, location: Coordinate(x: 2.0, y: 3.0, z: 4.0)), + "C": Atom(element: .carbon, location: Coordinate(x: 3.0, y: 4.0, z: 5.0)), + "O": Atom(element: .oxygen, location: Coordinate(x: 4.0, y: 5.0, z: 6.0)), + ] + var previousTerminalAtom: Atom? + let bonds = glycine.bonds(residueAtoms: &glycineAtoms, previousTerminalAtom: &previousTerminalAtom) + XCTAssertEqual(bonds.count, 3) + XCTAssertEqual(glycineAtoms.count, 0) + XCTAssertNotNil(previousTerminalAtom) + } } From 2e04edf06d006cd177e063a91dfb266b6546fec7 Mon Sep 17 00:00:00 2001 From: Brad Larson Date: Tue, 25 Jul 2023 21:14:08 -0500 Subject: [PATCH 4/9] Initial crude sphere rendering, completed PDB metadata processing. --- Molecules.xcodeproj/project.pbxproj | 8 + Molecules/FileProcessing/PDBFile.swift | 23 ++- Molecules/Interface/MoleculeDisplayView.swift | 5 +- Molecules/Rendering/Matrix.swift | 142 ++++++++++++++ Molecules/Rendering/MetalRenderView.swift | 177 ++++++++---------- .../Rendering/MetalRenderingDevice.swift | 41 +++- Molecules/Rendering/MoleculeRenderer.swift | 150 +++++++++++++++ 7 files changed, 428 insertions(+), 118 deletions(-) create mode 100644 Molecules/Rendering/Matrix.swift create mode 100644 Molecules/Rendering/MoleculeRenderer.swift diff --git a/Molecules.xcodeproj/project.pbxproj b/Molecules.xcodeproj/project.pbxproj index 7551d47..5f91210 100644 --- a/Molecules.xcodeproj/project.pbxproj +++ b/Molecules.xcodeproj/project.pbxproj @@ -56,6 +56,8 @@ BC5F1FEF2A5B51AF00416000 /* SphereRaytracing.metal in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1FEE2A5B51AF00416000 /* SphereRaytracing.metal */; }; BC5F1FF12A5B63B800416000 /* MetalRenderingDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1FF02A5B63B800416000 /* MetalRenderingDevice.swift */; }; BC5F1FF32A5CD81400416000 /* RenderingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1FF22A5CD81400416000 /* RenderingTests.swift */; }; + BCD090CD2A7098850034650F /* MoleculeRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCD090CC2A7098850034650F /* MoleculeRenderer.swift */; }; + BCD090CF2A70A7960034650F /* Matrix.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCD090CE2A70A7960034650F /* Matrix.swift */; }; BCF265E32A6377D300374DB0 /* AminoAcidResidue.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCF265E22A6377D300374DB0 /* AminoAcidResidue.swift */; }; /* End PBXBuildFile section */ @@ -118,6 +120,8 @@ BC5F1FEE2A5B51AF00416000 /* SphereRaytracing.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = SphereRaytracing.metal; sourceTree = ""; }; BC5F1FF02A5B63B800416000 /* MetalRenderingDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetalRenderingDevice.swift; sourceTree = ""; }; BC5F1FF22A5CD81400416000 /* RenderingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenderingTests.swift; sourceTree = ""; }; + BCD090CC2A7098850034650F /* MoleculeRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeRenderer.swift; sourceTree = ""; }; + BCD090CE2A70A7960034650F /* Matrix.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Matrix.swift; sourceTree = ""; }; BCF265E22A6377D300374DB0 /* AminoAcidResidue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AminoAcidResidue.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -250,6 +254,8 @@ children = ( BC5F1FCB2A5A29D400416000 /* MetalRenderView.swift */, BC5F1FF02A5B63B800416000 /* MetalRenderingDevice.swift */, + BCD090CC2A7098850034650F /* MoleculeRenderer.swift */, + BCD090CE2A70A7960034650F /* Matrix.swift */, BC5F1FED2A5B421600416000 /* Shaders */, ); path = Rendering; @@ -449,6 +455,7 @@ BC5F1FCF2A5A34FD00416000 /* MoleculeMetadataView.swift in Sources */, BC5F1F772A58D40500416000 /* MolecularStructure.swift in Sources */, BC5F1FD52A5A4C5800416000 /* XYZFile.swift in Sources */, + BCD090CF2A70A7960034650F /* Matrix.swift in Sources */, BC5F1FD22A5A4B5300416000 /* SDFFile.swift in Sources */, BC5807072A56395400313289 /* MoleculeDisplayView.swift in Sources */, BC5F1F7D2A58EB6300416000 /* Bond.swift in Sources */, @@ -459,6 +466,7 @@ BC5F1FEF2A5B51AF00416000 /* SphereRaytracing.metal in Sources */, BC5F1FCC2A5A29D400416000 /* MetalRenderView.swift in Sources */, BCF265E32A6377D300374DB0 /* AminoAcidResidue.swift in Sources */, + BCD090CD2A7098850034650F /* MoleculeRenderer.swift in Sources */, BC5807052A56395400313289 /* MoleculesApp.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/Molecules/FileProcessing/PDBFile.swift b/Molecules/FileProcessing/PDBFile.swift index 98d7550..5c05ec4 100644 --- a/Molecules/FileProcessing/PDBFile.swift +++ b/Molecules/FileProcessing/PDBFile.swift @@ -163,8 +163,7 @@ struct PDBFile: MolecularStructure { stillCountingAtomsInFirstStructure = false case "TITLE": guard line.count >= 10 else { continue } - let titleLine = line.dropFirst(10) - title += titleLine + line.cutAndAppend(to: &title, startingFrom: 10) case "COMPND": guard line.count >= 20 else { continue } let compoundIdentifier = line.whitespaceTrimmedString(from: 10, to: 20) @@ -173,19 +172,16 @@ struct PDBFile: MolecularStructure { } case "SOURCE": guard line.count >= 10 else { continue } - let sourceLine = line.dropFirst(10) - source += sourceLine + line.cutAndAppend(to: &source, startingFrom: 10) case "AUTHOR": guard line.count >= 10 else { continue } - let authorLine = line.dropFirst(10) - authors += authorLine + line.cutAndAppend(to: &authors, startingFrom: 10) case "JRNL": guard line.count >= 18 else { continue } let journalIdentifier = line.whitespaceTrimmedString(from: 12, to: 16) switch journalIdentifier { case "REF", "REFN": - let journalReferenceLine = line.dropFirst(18) - journalReference += journalReferenceLine + line.cutAndAppend(to: &journalReference, startingFrom: 18) // TODO: Do something else with these journal fields. case "AUTH": break case "TITL": break @@ -193,8 +189,7 @@ struct PDBFile: MolecularStructure { } case "SEQRES": guard line.count >= 14 else { continue } - let sequenceLine = line.dropFirst(14) - sequence += sequenceLine + line.cutAndAppend(to: &sequence, startingFrom: 14) default: break } } @@ -230,4 +225,12 @@ extension String { let endIndex = self.index(self.startIndex, offsetBy: end) return self[startIndex.. 15 else { + fatalError("Tried to initialize a 4x4 matrix with fewer than 16 values") + } + + self.m11 = rowMajorValues[0] + self.m12 = rowMajorValues[1] + self.m13 = rowMajorValues[2] + self.m14 = rowMajorValues[3] + + self.m21 = rowMajorValues[4] + self.m22 = rowMajorValues[5] + self.m23 = rowMajorValues[6] + self.m24 = rowMajorValues[7] + + self.m31 = rowMajorValues[8] + self.m32 = rowMajorValues[9] + self.m33 = rowMajorValues[10] + self.m34 = rowMajorValues[11] + + self.m41 = rowMajorValues[12] + self.m42 = rowMajorValues[13] + self.m43 = rowMajorValues[14] + self.m44 = rowMajorValues[15] + } + + public static let identity = Matrix4x4(rowMajorValues: [1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0]) +} + +public struct Matrix3x3 { + public let m11: Float, m12: Float, m13: Float + public let m21: Float, m22: Float, m23: Float + public let m31: Float, m32: Float, m33: Float + + public init(rowMajorValues: [Float]) { + guard rowMajorValues.count > 8 else { + fatalError("Tried to initialize a 3x3 matrix with fewer than 9 values") + } + + self.m11 = rowMajorValues[0] + self.m12 = rowMajorValues[1] + self.m13 = rowMajorValues[2] + + self.m21 = rowMajorValues[3] + self.m22 = rowMajorValues[4] + self.m23 = rowMajorValues[5] + + self.m31 = rowMajorValues[6] + self.m32 = rowMajorValues[7] + self.m33 = rowMajorValues[8] + } + + public static let identity = Matrix3x3(rowMajorValues: [1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0]) + + public static let centerOnly = Matrix3x3(rowMajorValues: [0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0]) +} + +func orthographicMatrix(_ left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float, anchorTopLeft: Bool = false) -> Matrix4x4 { + let r_l = right - left + let t_b = top - bottom + let f_n = far - near + var tx = -(right + left) / (right - left) + var ty = -(top + bottom) / (top - bottom) + let tz = -(far + near) / (far - near) + + let scale: Float + if (anchorTopLeft) { + scale = 4.0 + tx = -1.0 + ty = -1.0 + } else { + scale = 2.0 + } + + return Matrix4x4(rowMajorValues: [ + scale / r_l, 0.0, 0.0, tx, + 0.0, scale / t_b, 0.0, ty, + 0.0, 0.0, scale / f_n, tz, + 0.0, 0.0, 0.0, 1.0]) +} + + +public extension Matrix4x4 { + init (_ transform3D: CATransform3D) { + self.m11 = Float(transform3D.m11) + self.m12 = Float(transform3D.m12) + self.m13 = Float(transform3D.m13) + self.m14 = Float(transform3D.m14) + + self.m21 = Float(transform3D.m21) + self.m22 = Float(transform3D.m22) + self.m23 = Float(transform3D.m23) + self.m24 = Float(transform3D.m24) + + self.m31 = Float(transform3D.m31) + self.m32 = Float(transform3D.m32) + self.m33 = Float(transform3D.m33) + self.m34 = Float(transform3D.m34) + + self.m41 = Float(transform3D.m41) + self.m42 = Float(transform3D.m42) + self.m43 = Float(transform3D.m43) + self.m44 = Float(transform3D.m44) + } + + init (_ transform: CGAffineTransform) { + self.init(CATransform3DMakeAffineTransform(transform)) + } +} + +extension Matrix3x3 { + public func toFloatArray() -> [Float] { + // Row major, with zero-padding + return [m11, m12, m13, 0.0, m21, m22, m23, 0.0, m31, m32, m33, 0.0] + // return [m11, m12, m13, m21, m22, m23, m31, m32, m33] + } +} + +extension Matrix4x4 { + public func toFloatArray() -> [Float] { + // Row major + return [m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44] + // return [m11, m21, m31, m41, m12, m22, m32, m42, m13, m23, m33, m43, m14, m24, m34, m44] + } +} diff --git a/Molecules/Rendering/MetalRenderView.swift b/Molecules/Rendering/MetalRenderView.swift index 3ee5782..62e5528 100644 --- a/Molecules/Rendering/MetalRenderView.swift +++ b/Molecules/Rendering/MetalRenderView.swift @@ -2,68 +2,106 @@ import Foundation import MetalKit import SwiftUI -public class MetalRenderView: MTKView { +class MetalRenderView: MTKView { - var molecule: MolecularStructure! - var moleculeVertexBuffer: MTLBuffer! - var moleculeImpostorSpaceCoordinateBuffer: MTLBuffer! - var moleculeIndexBuffer: MTLBuffer! + var moleculeRenderer: MoleculeRenderer! - public override init(frame frameRect: CGRect, device: MTLDevice?) { + var rotationGestureRecognizer: UIPanGestureRecognizer! + var scaleGestureRecognizer: UIPinchGestureRecognizer! + var panGestureRecognizer: UIPanGestureRecognizer! + + override init(frame frameRect: CGRect, device: MTLDevice?) { super.init(frame: frameRect, device: sharedMetalRenderingDevice.device) commonInit() } - public required init(coder: NSCoder) { + required init(coder: NSCoder) { super.init(coder: coder) commonInit() } private func commonInit() { - framebufferOnly = false - autoResizeDrawable = true - + // Configure all touch interactions for the molecule rendering. + self.rotationGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(rotated)) + self.addGestureRecognizer(self.rotationGestureRecognizer) + self.scaleGestureRecognizer = UIPinchGestureRecognizer(target: self, action: #selector(scaled)) + self.addGestureRecognizer(self.scaleGestureRecognizer) + self.panGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(panned)) + self.panGestureRecognizer.minimumNumberOfTouches = 2 + self.addGestureRecognizer(self.panGestureRecognizer) + + self.framebufferOnly = true + self.autoResizeDrawable = true + self.depthStencilPixelFormat = .depth16Unorm self.device = sharedMetalRenderingDevice.device - enableSetNeedsDisplay = true - isPaused = true + self.enableSetNeedsDisplay = true + self.isPaused = true } - public override func draw(_ rect:CGRect) { + override func draw(_ rect:CGRect) { print("Draw") guard let drawable = self.currentDrawable else { print("No drawable") return } - let commandBuffer = sharedMetalRenderingDevice.commandQueue.makeCommandBuffer() - let rpd = self.currentRenderPassDescriptor - rpd?.colorAttachments[0].clearColor = MTLClearColorMake(0.0, 1.0, 0.0, 1.0) - rpd?.colorAttachments[0].loadAction = .clear - rpd?.colorAttachments[0].storeAction = .store - let re = commandBuffer?.makeRenderCommandEncoder(descriptor: rpd!) - re?.endEncoding() - commandBuffer?.present(drawable) - commandBuffer?.commit() + guard let moleculeRenderer = moleculeRenderer else { + print("No molecule renderer") + return + } + if let commandBuffer = sharedMetalRenderingDevice.commandQueue.makeCommandBuffer(), + let renderPass = self.currentRenderPassDescriptor { + moleculeRenderer.renderMoleculeFrame(buffer: commandBuffer, renderPass: renderPass) +// commandBuffer?.clear(texture: texture, with: MTLClearColorMake(0.0, 1.0, 0.0, 1.0)) + commandBuffer.present(drawable) + commandBuffer.commit() + } + } +} + +// MARK: - +// MARK: Gesture recognition + +extension MetalRenderView { + @objc func rotated(_ sender: UIPanGestureRecognizer) { + // TODO: Translate displacement into rotation matrix. + print("Rotation: \(sender.translation(in: self))") + } + + @objc func scaled(_ sender: UIPinchGestureRecognizer) { + // TODO: Translate into scale factor. + print("Scale: \(sender.scale)") + } + + @objc func panned(_ sender: UIPanGestureRecognizer) { + // TODO: Translate into pan matrix. + print("Pan: \(sender.translation(in: self))") } } +// MARK: - +// MARK: SwiftUI bridging. + struct MetalView: UIViewRepresentable { @Binding var molecule: MolecularStructure + @Binding var autorotate: Bool - init(molecule: Binding) { + init(molecule: Binding, autorotate: Binding) { self._molecule = molecule + self._autorotate = autorotate } func makeUIView(context: Context) -> MetalRenderView { let view = MetalRenderView(frame: .zero, device: nil) - view.molecule = molecule - view.initializeMoleculeBuffers() + view.moleculeRenderer = MoleculeRenderer(molecule: molecule) return view } func updateUIView(_ uiView: MetalRenderView, context: Context) { + // TODO: Enable or disable the autorotation loop. + print("Autorotate: \(autorotate)") print("updating view") } @@ -73,83 +111,20 @@ struct MetalView: UIViewRepresentable { } } -extension MetalRenderView { - func initializeMoleculeBuffers() { - var moleculeIndices: [UInt16] = [] - var moleculeVertices: [Float] = [] - var moleculeImpostorSpaceCoordinates: [Float] = [] - - var currentIndex: UInt16 = 0 - // TODO: Loop through all atoms. - appendOctagonVertices( - position: Coordinate(x: 0.0, y: 0.0, z: 0.0), - currentIndex: ¤tIndex, - indices: &moleculeIndices, - vertices: &moleculeVertices, - impostorSpaceCoordinates: &moleculeImpostorSpaceCoordinates) - - moleculeIndexBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: moleculeIndices, - length: moleculeIndices.count * MemoryLayout.size, - options: [])! - - // const device packed_float3 *position [[buffer(0)]], - moleculeVertexBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: moleculeVertices, - length: moleculeVertices.count * MemoryLayout.size, - options: [])! - +// MARK: - +// MARK: Metal rendering. - // const device packed_float2 *inputImpostorSpaceCoordinate [[buffer(1)]], - moleculeImpostorSpaceCoordinateBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: moleculeImpostorSpaceCoordinates, - length: moleculeImpostorSpaceCoordinates.count * MemoryLayout.size, - options: [])! - - - // TODO: Complete cylinder impostors. - - print("Number of atoms to initialize: \(molecule.atoms.count)") - } - - func renderMoleculeFrame() { - - } -} +extension MTLCommandBuffer { + func clear(texture: MTLTexture, with color: MTLClearColor) { + let renderPass = MTLRenderPassDescriptor() + renderPass.colorAttachments[0].texture = texture + renderPass.colorAttachments[0].clearColor = color + renderPass.colorAttachments[0].storeAction = .store + renderPass.colorAttachments[0].loadAction = .clear -func appendOctagonVertices( - position: Coordinate, - currentIndex: inout UInt16, - indices: inout [UInt16], - vertices: inout [Float], - impostorSpaceCoordinates: inout [Float] -) { - let vertex = [-position.x, position.y, position.z] - for _ in 0..<8 { - vertices.append(contentsOf: vertex) + guard let renderEncoder = self.makeRenderCommandEncoder(descriptor: renderPass) else { + fatalError("Could not create render encoder") + } + renderEncoder.endEncoding() } - - let positiveSideComponent: Float = 1.0 - 2.0 / (sqrt(2.0) + 2.0) - let negativeSideComponent: Float = -1.0 + 2.0 / (sqrt(2.0) + 2.0); - let octagonPoints: [Float] = [ - negativeSideComponent, 1.0, - -1.0, negativeSideComponent, - 1.0, positiveSideComponent, - positiveSideComponent, -1.0, - 1.0, negativeSideComponent, - positiveSideComponent, 1.0, - -1.0, positiveSideComponent, - negativeSideComponent, -1.0 - ] - impostorSpaceCoordinates += octagonPoints - - // 123, 324, 345, 136, 217, 428 - let octagonIndices: [UInt16] = [ - currentIndex, currentIndex + 1, currentIndex + 2, - currentIndex + 2, currentIndex, currentIndex + 3, - currentIndex + 2, currentIndex + 3, currentIndex + 4, - currentIndex, currentIndex + 2, currentIndex + 5, - currentIndex + 1, currentIndex, currentIndex + 6, - currentIndex + 3, currentIndex + 1, currentIndex + 7 - ] - indices += octagonIndices - - currentIndex += 8 } diff --git a/Molecules/Rendering/MetalRenderingDevice.swift b/Molecules/Rendering/MetalRenderingDevice.swift index 9737a70..b520a3d 100644 --- a/Molecules/Rendering/MetalRenderingDevice.swift +++ b/Molecules/Rendering/MetalRenderingDevice.swift @@ -1,19 +1,25 @@ import MetalKit -public let sharedMetalRenderingDevice = MetalRenderingDevice() +let sharedMetalRenderingDevice = MetalRenderingDevice() + +/// The MetalRenderingDevice is shared across all Metal rendering, setting up the Metal device +/// and all shaders once for the lifetime of the application. +class MetalRenderingDevice { + let device: MTLDevice + let commandQueue: MTLCommandQueue + let shaderLibrary: MTLLibrary + let sphereRaytracingDescriptor: MTLRenderPipelineDescriptor + let sphereRaytracingPipelineState: MTLRenderPipelineState -public class MetalRenderingDevice { - public let device: MTLDevice - public let commandQueue: MTLCommandQueue - public let shaderLibrary: MTLLibrary - init() { + // Configure the Metal device and command queue. guard let device = MTLCreateSystemDefaultDevice() else {fatalError("Could not create Metal Device")} self.device = device guard let queue = self.device.makeCommandQueue() else {fatalError("Could not create command queue")} self.commandQueue = queue + // Set up the shader library. do { let frameworkBundle = Bundle(for: MetalRenderingDevice.self) let metalLibraryPath = frameworkBundle.url(forResource: "default", withExtension: "metallib")! @@ -22,5 +28,28 @@ public class MetalRenderingDevice { } catch { fatalError("Could not load library") } + + // Create the render pipeline state for the sphere raytracing shader. + guard let vertexFunction = self.shaderLibrary.makeFunction(name: "sphereRaytracingVertex") else { + fatalError("Sphere raytracing: could not load vertex function sphereRaytracingVertex") + } + + guard let fragmentFunction = self.shaderLibrary.makeFunction(name: "sphereRaytracingFragment") else { + fatalError("Sphere raytracing: could not load fragment function sphereRaytracingFragment") + } + + self.sphereRaytracingDescriptor = MTLRenderPipelineDescriptor() + self.sphereRaytracingDescriptor.depthAttachmentPixelFormat = .depth16Unorm + self.sphereRaytracingDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm + self.sphereRaytracingDescriptor.rasterSampleCount = 1 + self.sphereRaytracingDescriptor.vertexFunction = vertexFunction + self.sphereRaytracingDescriptor.fragmentFunction = fragmentFunction + + do { + self.sphereRaytracingPipelineState = try self.device.makeRenderPipelineState(descriptor: self.sphereRaytracingDescriptor) + } catch { + // TODO: Examine the potential error cases here. + fatalError("Unable to create sphere raytracing render pipeline state with error: \(error)") + } } } diff --git a/Molecules/Rendering/MoleculeRenderer.swift b/Molecules/Rendering/MoleculeRenderer.swift new file mode 100644 index 0000000..6c68417 --- /dev/null +++ b/Molecules/Rendering/MoleculeRenderer.swift @@ -0,0 +1,150 @@ +import MetalKit + +final class MoleculeRenderer { + let molecule: MolecularStructure + + var moleculeVertexBuffer: MTLBuffer! + var moleculeImpostorSpaceCoordinateBuffer: MTLBuffer! + var moleculeIndexBuffer: MTLBuffer! + var moleculeIndexBufferCount: Int! + + init(molecule: MolecularStructure) { + self.molecule = molecule + initializeMoleculeBuffers() + } + + func initializeMoleculeBuffers() { + var moleculeIndices: [UInt16] = [] + var moleculeVertices: [Float] = [] + var moleculeImpostorSpaceCoordinates: [Float] = [] + + var currentIndex: UInt16 = 0 + // TODO: Loop through all atoms. + appendOctagonVertices( + position: Coordinate(x: 0.0, y: 0.0, z: 0.0), + currentIndex: ¤tIndex, + indices: &moleculeIndices, + vertices: &moleculeVertices, + impostorSpaceCoordinates: &moleculeImpostorSpaceCoordinates) + + moleculeIndexBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: moleculeIndices, + length: moleculeIndices.count * MemoryLayout.size, + options: [])! + moleculeIndexBuffer.label = "Sphere index buffer" + moleculeIndexBufferCount = moleculeIndices.count + + // const device packed_float3 *position [[buffer(0)]], + moleculeVertexBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: moleculeVertices, + length: moleculeVertices.count * MemoryLayout.size, + options: [])! + moleculeVertexBuffer.label = "Sphere vertex buffer" + + + // const device packed_float2 *inputImpostorSpaceCoordinate [[buffer(1)]], + moleculeImpostorSpaceCoordinateBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: moleculeImpostorSpaceCoordinates, + length: moleculeImpostorSpaceCoordinates.count * MemoryLayout.size, + options: [])! + moleculeImpostorSpaceCoordinateBuffer.label = "Sphere impostor space buffer" + + + // TODO: Complete cylinder impostors. + + print("Number of atoms to initialize: \(molecule.atoms.count)") + } + + func renderMoleculeFrame(buffer: MTLCommandBuffer, renderPass: MTLRenderPassDescriptor) { +// let renderPass = MTLRenderPassDescriptor() +// renderPass.colorAttachments[0].texture = output + renderPass.colorAttachments[0].clearColor = MTLClearColorMake(1, 0, 0, 1) + renderPass.colorAttachments[0].storeAction = .store + renderPass.colorAttachments[0].loadAction = .clear + + guard let renderEncoder = buffer.makeRenderCommandEncoder(descriptor: renderPass) else { + fatalError("Could not create render encoder") + } + renderEncoder.setFrontFacing(.counterClockwise) + renderEncoder.setRenderPipelineState(sharedMetalRenderingDevice.sphereRaytracingPipelineState) + + renderEncoder.setVertexBuffer(moleculeVertexBuffer, offset: 0, index: 0) + renderEncoder.setVertexBuffer(moleculeImpostorSpaceCoordinateBuffer, offset: 0, index: 1) + + let vertexUniforms = sphereVertexUniforms() + let vertexUniformBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: vertexUniforms, + length: vertexUniforms.count * MemoryLayout.size, + options: [])! + renderEncoder.setVertexBuffer(vertexUniformBuffer, offset: 0, index: 3) + + let fragmentUniforms = sphereFragmentUniforms() + let fragmentUniformBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: fragmentUniforms, + length: fragmentUniforms.count * MemoryLayout.size, + options: [])! + renderEncoder.setFragmentBuffer(fragmentUniformBuffer, offset: 0, index: 1) + + + renderEncoder.drawIndexedPrimitives(type: .triangle, indexCount: moleculeIndexBufferCount, indexType: .uint16, indexBuffer: moleculeIndexBuffer, indexBufferOffset: 0) + + renderEncoder.endEncoding() + } + + func sphereVertexUniforms() -> [Float] { +// float3x3 modelViewProjMatrix; +// float4x4 orthographicMatrix; +// float sphereRadius; +// float3 translation; + let modelViewProjMatrix = Matrix3x3.identity + let orthographicMatrix = Matrix4x4.identity + let sphereRadius: Float = 1.0 + let translation: (Float, Float, Float) = (0.0, 0.0, 0.0) + + return modelViewProjMatrix.toFloatArray() + orthographicMatrix.toFloatArray() + [sphereRadius, 0.0, 0.0, 0.0, translation.0, translation.1, translation.2, 0.0] + } + + func sphereFragmentUniforms() -> [Float] { +// float3 sphereColor; +// float3x3 inverseModelViewProjMatrix; + let sphereColor: (Float, Float, Float) = (0.0, 1.0, 0.0) + let inverseModelViewProjMatrix = Matrix3x3.identity + + return [sphereColor.0, sphereColor.1, sphereColor.2, 0.0] + inverseModelViewProjMatrix.toFloatArray() + } +} + +func appendOctagonVertices( + position: Coordinate, + currentIndex: inout UInt16, + indices: inout [UInt16], + vertices: inout [Float], + impostorSpaceCoordinates: inout [Float] +) { + let vertex = [-position.x, position.y, position.z] + for _ in 0..<8 { + vertices.append(contentsOf: vertex) + } + + let positiveSideComponent: Float = 1.0 - 2.0 / (sqrt(2.0) + 2.0) + let negativeSideComponent: Float = -1.0 + 2.0 / (sqrt(2.0) + 2.0); + let octagonPoints: [Float] = [ + negativeSideComponent, 1.0, + -1.0, negativeSideComponent, + 1.0, positiveSideComponent, + positiveSideComponent, -1.0, + 1.0, negativeSideComponent, + positiveSideComponent, 1.0, + -1.0, positiveSideComponent, + negativeSideComponent, -1.0 + ] + impostorSpaceCoordinates += octagonPoints + + // 123, 324, 345, 136, 217, 428 + let octagonIndices: [UInt16] = [ + currentIndex, currentIndex + 1, currentIndex + 2, + currentIndex + 2, currentIndex + 1, currentIndex + 3, + currentIndex + 2, currentIndex + 3, currentIndex + 4, + currentIndex, currentIndex + 2, currentIndex + 5, + currentIndex + 1, currentIndex, currentIndex + 6, + currentIndex + 3, currentIndex + 1, currentIndex + 7 + ] + indices += octagonIndices + + currentIndex += 8 +} From de6612a8f61f98b7a7fd08bf2be9b8bd4f3a1d6b Mon Sep 17 00:00:00 2001 From: Brad Larson Date: Sat, 29 Jul 2023 19:53:03 -0500 Subject: [PATCH 5/9] All atoms now render. Touch rotation, scaling, panning works. --- Molecules.xcodeproj/project.pbxproj | 4 + Molecules/FileProcessing/Atom.swift | 64 +++++- Molecules/FileProcessing/Coordinate.swift | 22 ++ .../FileProcessing/MolecularStructure.swift | 6 + Molecules/Info.plist | 6 +- Molecules/Interface/MoleculeDisplayView.swift | 60 ++++- .../Interface/MoleculeMetadataView.swift | 1 + Molecules/Rendering/Matrix.swift | 190 ++++++++++------ Molecules/Rendering/MetalRenderView.swift | 51 ++++- .../Rendering/MetalRenderingDevice.swift | 2 +- Molecules/Rendering/MoleculeRenderer.swift | 206 ++++++++++++------ .../Rendering/Shaders/SphereRaytracing.metal | 11 +- MoleculesTests/MatrixTests.swift | 161 ++++++++++++++ MoleculesTests/RenderingTests.swift | 2 +- 14 files changed, 620 insertions(+), 166 deletions(-) create mode 100644 MoleculesTests/MatrixTests.swift diff --git a/Molecules.xcodeproj/project.pbxproj b/Molecules.xcodeproj/project.pbxproj index 5f91210..3226bd7 100644 --- a/Molecules.xcodeproj/project.pbxproj +++ b/Molecules.xcodeproj/project.pbxproj @@ -58,6 +58,7 @@ BC5F1FF32A5CD81400416000 /* RenderingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1FF22A5CD81400416000 /* RenderingTests.swift */; }; BCD090CD2A7098850034650F /* MoleculeRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCD090CC2A7098850034650F /* MoleculeRenderer.swift */; }; BCD090CF2A70A7960034650F /* Matrix.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCD090CE2A70A7960034650F /* Matrix.swift */; }; + BCD090D12A748C860034650F /* MatrixTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCD090D02A748C860034650F /* MatrixTests.swift */; }; BCF265E32A6377D300374DB0 /* AminoAcidResidue.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCF265E22A6377D300374DB0 /* AminoAcidResidue.swift */; }; /* End PBXBuildFile section */ @@ -122,6 +123,7 @@ BC5F1FF22A5CD81400416000 /* RenderingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenderingTests.swift; sourceTree = ""; }; BCD090CC2A7098850034650F /* MoleculeRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeRenderer.swift; sourceTree = ""; }; BCD090CE2A70A7960034650F /* Matrix.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Matrix.swift; sourceTree = ""; }; + BCD090D02A748C860034650F /* MatrixTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixTests.swift; sourceTree = ""; }; BCF265E22A6377D300374DB0 /* AminoAcidResidue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AminoAcidResidue.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -202,6 +204,7 @@ BC5F1FD72A5A4CAB00416000 /* SDFFileTests.swift */, BC5F1FD92A5A4EA900416000 /* XYZFileTests.swift */, BC5F1FF22A5CD81400416000 /* RenderingTests.swift */, + BCD090D02A748C860034650F /* MatrixTests.swift */, ); path = MoleculesTests; sourceTree = ""; @@ -475,6 +478,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + BCD090D12A748C860034650F /* MatrixTests.swift in Sources */, BC5F1FD82A5A4CAB00416000 /* SDFFileTests.swift in Sources */, BC5F1FDA2A5A4EA900416000 /* XYZFileTests.swift in Sources */, BC5F1FF32A5CD81400416000 /* RenderingTests.swift in Sources */, diff --git a/Molecules/FileProcessing/Atom.swift b/Molecules/FileProcessing/Atom.swift index 7048963..c656f50 100644 --- a/Molecules/FileProcessing/Atom.swift +++ b/Molecules/FileProcessing/Atom.swift @@ -1,6 +1,6 @@ /// Individual atoms within a molecular structure, located in 3-D space. struct Atom { - enum Element { + enum Element: CaseIterable, Hashable { case carbon case hydrogen case oxygen @@ -19,6 +19,18 @@ struct Atom { case sodium case magnesium case unknown + + struct Color { + let red: Float + let green: Float + let blue: Float + + init (_ red: Float, _ green: Float, _ blue: Float) { + self.red = red + self.green = green + self.blue = blue + } + } } let element: Element @@ -49,3 +61,53 @@ extension Atom.Element { } } } + +extension Atom.Element { + var vanderWaalsRadius: Float { + switch self { + case .carbon: return 1.55 + case .hydrogen: return 1.10 + case .oxygen: return 1.35 + case .nitrogen: return 1.40 + case .sulfur: return 1.81 + case .phosphorous: return 1.88 + case .iron: return 1.95 + case .silicon: return 1.50 + case .fluorine: return 1.47 + case .chlorine: return 1.75 + case .bromine: return 1.85 + case .iodine: return 1.75 + case .calcium: return 1.95 + case .zinc: return 1.15 + case .cadmium: return 1.75 + case .sodium: return 1.02 + case .magnesium: return 0.72 + case .unknown: return 1.50 + } + } +} + +extension Atom.Element { + var color: Color { + switch self { + case .carbon: return Color(0.47, 0.47, 0.47) + case .hydrogen: return Color(0.90, 0.90, 0.90) + case .oxygen: return Color(0.94, 0.16, 0.16) + case .nitrogen: return Color(0.19, 0.31, 0.97) + case .sulfur: return Color(1.0, 1.0, 0.19) + case .phosphorous: return Color(1.0, 0.5, 0.0) + case .iron: return Color(0.88, 0.4, 0.2) + case .silicon: return Color(0.78, 0.78, 0.35) + case .fluorine: return Color(0.56, 0.88, 0.31) + case .chlorine: return Color(0.12, 0.94, 0.12) + case .bromine: return Color(0.65, 0.16, 0.16) + case .iodine: return Color(0.58, 0.0, 0.58) + case .calcium: return Color(0.25, 1.0, 0.0) + case .zinc: return Color(0.49, 0.5, 0.69) + case .cadmium: return Color(1.0, 0.85, 0.56) + case .sodium: return Color(0.67, 0.36, 0.95) + case .magnesium: return Color(0.54, 1.0, 0.0) + case .unknown: return Color(0.0, 1.0, 0.0) + } + } +} diff --git a/Molecules/FileProcessing/Coordinate.swift b/Molecules/FileProcessing/Coordinate.swift index d2c2bad..0844cdc 100644 --- a/Molecules/FileProcessing/Coordinate.swift +++ b/Molecules/FileProcessing/Coordinate.swift @@ -4,3 +4,25 @@ struct Coordinate { let y: Float let z: Float } + +extension Coordinate: AdditiveArithmetic { + static func - (lhs: Coordinate, rhs: Coordinate) -> Coordinate { + Coordinate(x: lhs.x - rhs.x, y: lhs.y - rhs.y, z: lhs.z - rhs.z) + } + + static func + (lhs: Coordinate, rhs: Coordinate) -> Coordinate { + Coordinate(x: lhs.x + rhs.x, y: lhs.y + rhs.y, z: lhs.z + rhs.z) + } + + static var zero: Coordinate { Coordinate(x: 0.0, y: 0.0, z: 0.0) } +} + +extension Coordinate { + static func * (lhs: Coordinate, rhs: Float) -> Coordinate { + Coordinate(x: lhs.x * rhs, y: lhs.y * rhs, z: lhs.z * rhs) + } + + static func * (lhs: Float, rhs: Coordinate) -> Coordinate { + Coordinate(x: lhs * rhs.x, y: lhs * rhs.y, z: lhs * rhs.z) + } +} diff --git a/Molecules/FileProcessing/MolecularStructure.swift b/Molecules/FileProcessing/MolecularStructure.swift index c2bcb31..2a261bf 100644 --- a/Molecules/FileProcessing/MolecularStructure.swift +++ b/Molecules/FileProcessing/MolecularStructure.swift @@ -16,6 +16,12 @@ protocol MolecularStructure { init(data: Data) throws } +extension MolecularStructure { + var overallScaleFactor: Float { + return min(suggestedScaleFactor.x, suggestedScaleFactor.y, suggestedScaleFactor.z) + } +} + /// Protein Data Bank files contain a lot of structure and publication metadata. struct MolecularMetadata { diff --git a/Molecules/Info.plist b/Molecules/Info.plist index 2b63427..cd5a46f 100644 --- a/Molecules/Info.plist +++ b/Molecules/Info.plist @@ -43,9 +43,9 @@ UIFileSharingEnabled - UIUserInterfaceStyle - Dark - UTExportedTypeDeclarations + UIUserInterfaceStyle + Dark + UTExportedTypeDeclarations UTTypeConformsTo diff --git a/Molecules/Interface/MoleculeDisplayView.swift b/Molecules/Interface/MoleculeDisplayView.swift index 011a3c1..446fc01 100644 --- a/Molecules/Interface/MoleculeDisplayView.swift +++ b/Molecules/Interface/MoleculeDisplayView.swift @@ -1,8 +1,12 @@ import SwiftUI struct MoleculeDisplayView: View { + @Environment(\.horizontalSizeClass) var horizontalSizeClass + @Environment(\.verticalSizeClass) var verticalSizeClass + @Binding var document: MoleculeDocument @State private var autorotate = true + @State private var showingMetadata = false var body: some View { ZStack { @@ -35,16 +39,31 @@ struct MoleculeDisplayView: View { } } ToolbarItem(placement: .navigationBarTrailing) { - NavigationLink { - MoleculeMetadataView(molecule: document.molecule) - .navigationTitle("Details") - .toolbarRole(.automatic) - .navigationBarTitleDisplayMode(.inline) - } label: { - // TODO: Popover on iPad? - Image(systemName: "info.circle") - .imageScale(.large) - .foregroundColor(.accentColor) + if (horizontalSizeClass == .regular) && (verticalSizeClass == .regular) { + // Popover on full screen for iPad. + Button { + showingMetadata = true + } label: { + Image(systemName: "info.circle") + .imageScale(.large) + .foregroundColor(.accentColor) + } + .popover(isPresented: $showingMetadata) { + MoleculeMetadataView(molecule: document.molecule) + .frame(width: 300, height: 600) + } + } else { + // Navigate to a new view for smaller sizes. + NavigationLink { + MoleculeMetadataView(molecule: document.molecule) + .navigationTitle("Details") + .toolbarRole(.automatic) + .navigationBarTitleDisplayMode(.inline) + } label: { + Image(systemName: "info.circle") + .imageScale(.large) + .foregroundColor(.accentColor) + } } } } @@ -61,5 +80,26 @@ struct MoleculeDisplayView_Previews: PreviewProvider { .toolbarRole(.automatic) .navigationBarTitleDisplayMode(.inline) } + .previewDevice(PreviewDevice(rawValue: "iPhone 14")) + .previewDisplayName("iPhone 14") + + NavigationStack { + MoleculeDisplayView(document: .constant(document)) + .navigationTitle("Caffeine") + .toolbarRole(.automatic) + .navigationBarTitleDisplayMode(.inline) + } + .previewDevice(PreviewDevice(rawValue: "iPad (10th generation)")) + .previewDisplayName("iPad") + + NavigationStack { + MoleculeDisplayView(document: .constant(document)) + .navigationTitle("Caffeine") + .toolbarRole(.automatic) + .navigationBarTitleDisplayMode(.inline) + } + .previewDevice(PreviewDevice(rawValue: "iPad (10th generation)")) + .previewInterfaceOrientation(.landscapeLeft) + .previewDisplayName("iPad landscape") } } diff --git a/Molecules/Interface/MoleculeMetadataView.swift b/Molecules/Interface/MoleculeMetadataView.swift index 5cc71ff..21dc2c0 100644 --- a/Molecules/Interface/MoleculeMetadataView.swift +++ b/Molecules/Interface/MoleculeMetadataView.swift @@ -35,6 +35,7 @@ struct MoleculeMetadataView: View { } } } + .scrollContentBackground(.hidden) .padding() } } diff --git a/Molecules/Rendering/Matrix.swift b/Molecules/Rendering/Matrix.swift index 9736e93..21fe968 100644 --- a/Molecules/Rendering/Matrix.swift +++ b/Molecules/Rendering/Matrix.swift @@ -1,4 +1,4 @@ -import QuartzCore +import Foundation // Originally from GPUImage3: https://github.com/BradLarson/GPUImage3 @@ -40,39 +40,10 @@ public struct Matrix4x4 { 0.0, 0.0, 0.0, 1.0]) } -public struct Matrix3x3 { - public let m11: Float, m12: Float, m13: Float - public let m21: Float, m22: Float, m23: Float - public let m31: Float, m32: Float, m33: Float - - public init(rowMajorValues: [Float]) { - guard rowMajorValues.count > 8 else { - fatalError("Tried to initialize a 3x3 matrix with fewer than 9 values") - } - - self.m11 = rowMajorValues[0] - self.m12 = rowMajorValues[1] - self.m13 = rowMajorValues[2] - - self.m21 = rowMajorValues[3] - self.m22 = rowMajorValues[4] - self.m23 = rowMajorValues[5] - - self.m31 = rowMajorValues[6] - self.m32 = rowMajorValues[7] - self.m33 = rowMajorValues[8] - } - - public static let identity = Matrix3x3(rowMajorValues: [1.0, 0.0, 0.0, - 0.0, 1.0, 0.0, - 0.0, 0.0, 1.0]) - - public static let centerOnly = Matrix3x3(rowMajorValues: [0.0, 0.0, 0.0, - 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0]) -} +// MARK: - +// MARK: Convenience construction. -func orthographicMatrix(_ left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float, anchorTopLeft: Bool = false) -> Matrix4x4 { +func orthographicMatrix(left: Float, right: Float, bottom: Float, top: Float, near: Float, far: Float, anchorTopLeft: Bool = false) -> Matrix4x4 { let r_l = right - left let t_b = top - bottom let f_n = far - near @@ -96,47 +67,140 @@ func orthographicMatrix(_ left: Float, right: Float, bottom: Float, top: Float, 0.0, 0.0, 0.0, 1.0]) } +// MARK: - +// MARK: Matrix manipulation. -public extension Matrix4x4 { - init (_ transform3D: CATransform3D) { - self.m11 = Float(transform3D.m11) - self.m12 = Float(transform3D.m12) - self.m13 = Float(transform3D.m13) - self.m14 = Float(transform3D.m14) - - self.m21 = Float(transform3D.m21) - self.m22 = Float(transform3D.m22) - self.m23 = Float(transform3D.m23) - self.m24 = Float(transform3D.m24) - - self.m31 = Float(transform3D.m31) - self.m32 = Float(transform3D.m32) - self.m33 = Float(transform3D.m33) - self.m34 = Float(transform3D.m34) - - self.m41 = Float(transform3D.m41) - self.m42 = Float(transform3D.m42) - self.m43 = Float(transform3D.m43) - self.m44 = Float(transform3D.m44) +extension Matrix4x4 { + /// Rotates a matrix by a specified angle about an axis. + /// - Parameters: + /// - angle: The rotation angle, in radians. + /// - x: The x component of the axis of rotation. + /// - y: The y component of the axis of rotation. + /// - z: The z component of the axis of rotation. + func rotated(angle: Float, x: Float, y: Float, z: Float) -> Matrix4x4 { + let distance = sqrt(x * x + y * y + z * z) + guard distance > 0 else { return self } + + let normalizedX = -x / distance + let normalizedY = -y / distance + let normalizedZ = -z / distance + + let rotation = Matrix4x4(rowMajorValues: [ + cos(angle) + normalizedX * normalizedX * (1 - cos(angle)), + normalizedX * normalizedY * (1 - cos(angle)) - normalizedZ * sin(angle), + normalizedX * normalizedZ * (1 - cos(angle)) + normalizedY * sin(angle), + 0, + + normalizedY * normalizedX * (1 - cos(angle)) + normalizedZ * sin(angle), + cos(angle) + normalizedY * normalizedY * (1 - cos(angle)), + normalizedY * normalizedZ * (1 - cos(angle)) - normalizedX * sin(angle), + 0, + + normalizedZ * normalizedX * (1 - cos(angle)) - normalizedY * sin(angle), + normalizedZ * normalizedY * (1 - cos(angle)) + normalizedX * sin(angle), + cos(angle) + normalizedZ * normalizedZ * (1 - cos(angle)), + 0, + + 0, 0, 0, 1 + ]) + + return rotation * self } - - init (_ transform: CGAffineTransform) { - self.init(CATransform3DMakeAffineTransform(transform)) + + /// Scales a matrix by specified factors in each dimension. + /// - Parameters: + /// - x: The scale factor to apply in the x dimension. + /// - y: The scale factor to apply in the y dimension. + /// - z: The scale factor to apply in the z dimension. + func scaled(x: Float, y: Float, z: Float) -> Matrix4x4 { + return Matrix4x4(rowMajorValues: [ + m11 * x, m12 * x, m13 * x, m14 * x, + m21 * y, m22 * y, m23 * y, m24 * y, + m31 * z, m32 * z, m33 * z, m34 * z, + m41, m42, m43, m44 + ]) + } + + /// Returns the current scale factor. + var scale: Float { + return sqrt(pow(m11, 2.0) + pow(m12, 2.0) + pow(m13, 2.0)) + } + + /// Inverts the matrix. Assumes coordinate transform matrix. Not totally valid. + func inverted() -> Matrix4x4 { + return Matrix4x4(rowMajorValues: [ + m11, m21, m31, -(m11 * m14 + m21 * m24 + m31 * m34), + m12, m22, m32, -(m12 * m24 + m22 * m24 + m32 * m34), + m13, m23, m33, -(m13 * m24 + m23 * m24 + m33 * m34), + m41, m42, m43, m44 + ]) + } + + static func * (lhs: Matrix4x4, rhs: Matrix4x4) -> Matrix4x4 { + Matrix4x4(rowMajorValues: [ + lhs.m11 * rhs.m11 + lhs.m12 * rhs.m21 + lhs.m13 * rhs.m31 + lhs.m14 * rhs.m41, + lhs.m11 * rhs.m12 + lhs.m12 * rhs.m22 + lhs.m13 * rhs.m32 + lhs.m14 * rhs.m42, + lhs.m11 * rhs.m13 + lhs.m12 * rhs.m23 + lhs.m13 * rhs.m33 + lhs.m14 * rhs.m43, + lhs.m11 * rhs.m14 + lhs.m12 * rhs.m24 + lhs.m13 * rhs.m34 + lhs.m14 * rhs.m44, + + lhs.m21 * rhs.m11 + lhs.m22 * rhs.m21 + lhs.m23 * rhs.m31 + lhs.m24 * rhs.m41, + lhs.m21 * rhs.m12 + lhs.m22 * rhs.m22 + lhs.m23 * rhs.m32 + lhs.m24 * rhs.m42, + lhs.m21 * rhs.m13 + lhs.m22 * rhs.m23 + lhs.m23 * rhs.m33 + lhs.m24 * rhs.m43, + lhs.m21 * rhs.m14 + lhs.m22 * rhs.m24 + lhs.m23 * rhs.m34 + lhs.m24 * rhs.m44, + + lhs.m31 * rhs.m11 + lhs.m32 * rhs.m21 + lhs.m33 * rhs.m31 + lhs.m34 * rhs.m41, + lhs.m31 * rhs.m12 + lhs.m32 * rhs.m22 + lhs.m33 * rhs.m32 + lhs.m34 * rhs.m42, + lhs.m31 * rhs.m13 + lhs.m32 * rhs.m23 + lhs.m33 * rhs.m33 + lhs.m34 * rhs.m43, + lhs.m31 * rhs.m14 + lhs.m32 * rhs.m24 + lhs.m33 * rhs.m34 + lhs.m34 * rhs.m44, + + lhs.m41 * rhs.m11 + lhs.m42 * rhs.m21 + lhs.m43 * rhs.m31 + lhs.m44 * rhs.m41, + lhs.m41 * rhs.m12 + lhs.m42 * rhs.m22 + lhs.m43 * rhs.m32 + lhs.m44 * rhs.m42, + lhs.m41 * rhs.m13 + lhs.m42 * rhs.m23 + lhs.m43 * rhs.m33 + lhs.m44 * rhs.m43, + lhs.m41 * rhs.m14 + lhs.m42 * rhs.m24 + lhs.m43 * rhs.m34 + lhs.m44 * rhs.m44, + ]) } } -extension Matrix3x3 { - public func toFloatArray() -> [Float] { - // Row major, with zero-padding - return [m11, m12, m13, 0.0, m21, m22, m23, 0.0, m31, m32, m33, 0.0] - // return [m11, m12, m13, m21, m22, m23, m31, m32, m33] +// MARK: - +// MARK: Matrix touch manipulation. + +extension Matrix4x4 { + func rotatedFromTouch(x: Float, y: Float) -> Matrix4x4 { + let totalRotation = sqrt(x * x + y * y) + + return self.rotated(angle: totalRotation * .pi / 180.0, + x: ((x / totalRotation) * m12 + (y / totalRotation) * m11), + y: ((x / totalRotation) * m22 + (y / totalRotation) * m21), + z: ((x / totalRotation) * m32 + (y / totalRotation) * m31)) + } + + func scaledFromTouch(scale: Float) -> Matrix4x4 { + return self.scaled(x: scale, y: scale, z: scale) + } + + func translatedFromTouch(currentTranslation: Coordinate, x: Float, y: Float, backingWidth: Float) -> Coordinate { + let currentScaleFactor = self.scale + + let xTranslation = x * 1.0 / (currentScaleFactor * currentScaleFactor * backingWidth * 0.5) + let yTranslation = y * 1.0 / (currentScaleFactor * currentScaleFactor * backingWidth * 0.5) + + // Use the (0,4,8) components to figure the eye's X axis in the model coordinate system, translate along that + // Use the (1,5,9) components to figure the eye's Y axis in the model coordinate system, translate along that + + return Coordinate( + x: currentTranslation.x + xTranslation * m11 + yTranslation * m12, + y: currentTranslation.y + xTranslation * m21 + yTranslation * m22, + z: currentTranslation.z + xTranslation * m31 + yTranslation * m32 + ) } } +// MARK: - +// MARK: Encoding to buffers. + extension Matrix4x4 { public func toFloatArray() -> [Float] { // Row major return [m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44] - // return [m11, m21, m31, m41, m12, m22, m32, m42, m13, m23, m33, m43, m14, m24, m34, m44] } } diff --git a/Molecules/Rendering/MetalRenderView.swift b/Molecules/Rendering/MetalRenderView.swift index 62e5528..716427c 100644 --- a/Molecules/Rendering/MetalRenderView.swift +++ b/Molecules/Rendering/MetalRenderView.swift @@ -9,6 +9,10 @@ class MetalRenderView: MTKView { var rotationGestureRecognizer: UIPanGestureRecognizer! var scaleGestureRecognizer: UIPinchGestureRecognizer! var panGestureRecognizer: UIPanGestureRecognizer! + var lastTranslation = CGPoint.zero + var lastScale: CGFloat = 1.0 + // TODO: Communicate change in autorotation upwards to SwiftUI. + var autoRotating: Bool = true override init(frame frameRect: CGRect, device: MTLDevice?) { super.init(frame: frameRect, device: sharedMetalRenderingDevice.device) @@ -34,7 +38,7 @@ class MetalRenderView: MTKView { self.framebufferOnly = true self.autoResizeDrawable = true - self.depthStencilPixelFormat = .depth16Unorm + self.depthStencilPixelFormat = .depth32Float self.device = sharedMetalRenderingDevice.device self.enableSetNeedsDisplay = true @@ -42,7 +46,6 @@ class MetalRenderView: MTKView { } override func draw(_ rect:CGRect) { - print("Draw") guard let drawable = self.currentDrawable else { print("No drawable") return @@ -53,8 +56,7 @@ class MetalRenderView: MTKView { } if let commandBuffer = sharedMetalRenderingDevice.commandQueue.makeCommandBuffer(), let renderPass = self.currentRenderPassDescriptor { - moleculeRenderer.renderMoleculeFrame(buffer: commandBuffer, renderPass: renderPass) -// commandBuffer?.clear(texture: texture, with: MTLClearColorMake(0.0, 1.0, 0.0, 1.0)) + moleculeRenderer.renderMoleculeFrame(width: self.frame.width, height: self.frame.height, buffer: commandBuffer, renderPass: renderPass) commandBuffer.present(drawable) commandBuffer.commit() } @@ -66,18 +68,46 @@ class MetalRenderView: MTKView { extension MetalRenderView { @objc func rotated(_ sender: UIPanGestureRecognizer) { - // TODO: Translate displacement into rotation matrix. - print("Rotation: \(sender.translation(in: self))") + self.autoRotating = false + let currentTranslation = sender.translation(in: self) + if sender.state == .began { + lastTranslation = CGPoint.zero + } + moleculeRenderer.rotateFromTouch( + x: Float(lastTranslation.x - currentTranslation.x), + y: Float(lastTranslation.y - currentTranslation.y)) + lastTranslation = currentTranslation + self.setNeedsDisplay() } @objc func scaled(_ sender: UIPinchGestureRecognizer) { - // TODO: Translate into scale factor. - print("Scale: \(sender.scale)") + self.autoRotating = false + let currentScale = sender.scale + if sender.state == .began { + lastScale = 1.0 + } + guard lastScale >= 0.01 else { + lastScale = currentScale + return + } + moleculeRenderer.scaleFromTouch(scale: Float(currentScale / lastScale)) + lastScale = currentScale + self.setNeedsDisplay() } @objc func panned(_ sender: UIPanGestureRecognizer) { - // TODO: Translate into pan matrix. - print("Pan: \(sender.translation(in: self))") + self.autoRotating = false + let currentTranslation = sender.translation(in: self) + if sender.state == .began { + lastTranslation = CGPoint.zero + } + moleculeRenderer.translateFromTouch( + x: Float(currentTranslation.x - lastTranslation.x), + y: Float(lastTranslation.y - currentTranslation.y), + backingWidth: Float(self.frame.width) + ) + lastTranslation = currentTranslation + self.setNeedsDisplay() } } @@ -101,6 +131,7 @@ struct MetalView: UIViewRepresentable { func updateUIView(_ uiView: MetalRenderView, context: Context) { // TODO: Enable or disable the autorotation loop. + uiView.autoRotating = autorotate print("Autorotate: \(autorotate)") print("updating view") } diff --git a/Molecules/Rendering/MetalRenderingDevice.swift b/Molecules/Rendering/MetalRenderingDevice.swift index b520a3d..1175ba7 100644 --- a/Molecules/Rendering/MetalRenderingDevice.swift +++ b/Molecules/Rendering/MetalRenderingDevice.swift @@ -39,7 +39,7 @@ class MetalRenderingDevice { } self.sphereRaytracingDescriptor = MTLRenderPipelineDescriptor() - self.sphereRaytracingDescriptor.depthAttachmentPixelFormat = .depth16Unorm + self.sphereRaytracingDescriptor.depthAttachmentPixelFormat = .depth32Float self.sphereRaytracingDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm self.sphereRaytracingDescriptor.rasterSampleCount = 1 self.sphereRaytracingDescriptor.vertexFunction = vertexFunction diff --git a/Molecules/Rendering/MoleculeRenderer.swift b/Molecules/Rendering/MoleculeRenderer.swift index 6c68417..9c5ea5a 100644 --- a/Molecules/Rendering/MoleculeRenderer.swift +++ b/Molecules/Rendering/MoleculeRenderer.swift @@ -3,10 +3,20 @@ import MetalKit final class MoleculeRenderer { let molecule: MolecularStructure - var moleculeVertexBuffer: MTLBuffer! - var moleculeImpostorSpaceCoordinateBuffer: MTLBuffer! - var moleculeIndexBuffer: MTLBuffer! - var moleculeIndexBufferCount: Int! + var sphereVertexBuffers: [Atom.Element: MTLBuffer] = [:] + var sphereImpostorSpaceCoordinateBuffers: [Atom.Element: MTLBuffer] = [:] + var sphereIndexBuffers: [Atom.Element: MTLBuffer] = [:] + var sphereIndexBufferCounts: [Atom.Element: Int] = [:] + let lowerScaleLimit: Float = -100.0 + let upperScaleLimit: Float = 100.0 + + var currentScale: Float = 1.0 + var currentTranslation: Coordinate = .zero + var modelViewProjMatrix = Matrix4x4(rowMajorValues: [ + 0.402560, 0.094840, 0.910469, 0.000000, + 0.913984,-0.096835,-0.394028, 0.000000, + 0.050796, 0.990772,-0.125664, 0.000000, + 0.000000, 0.000000, 0.000000, 1.000000]) init(molecule: MolecularStructure) { self.molecule = molecule @@ -14,48 +24,67 @@ final class MoleculeRenderer { } func initializeMoleculeBuffers() { - var moleculeIndices: [UInt16] = [] - var moleculeVertices: [Float] = [] - var moleculeImpostorSpaceCoordinates: [Float] = [] - - var currentIndex: UInt16 = 0 - // TODO: Loop through all atoms. - appendOctagonVertices( - position: Coordinate(x: 0.0, y: 0.0, z: 0.0), - currentIndex: ¤tIndex, - indices: &moleculeIndices, - vertices: &moleculeVertices, - impostorSpaceCoordinates: &moleculeImpostorSpaceCoordinates) - - moleculeIndexBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: moleculeIndices, - length: moleculeIndices.count * MemoryLayout.size, - options: [])! - moleculeIndexBuffer.label = "Sphere index buffer" - moleculeIndexBufferCount = moleculeIndices.count - - // const device packed_float3 *position [[buffer(0)]], - moleculeVertexBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: moleculeVertices, - length: moleculeVertices.count * MemoryLayout.size, - options: [])! - moleculeVertexBuffer.label = "Sphere vertex buffer" - - - // const device packed_float2 *inputImpostorSpaceCoordinate [[buffer(1)]], - moleculeImpostorSpaceCoordinateBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: moleculeImpostorSpaceCoordinates, - length: moleculeImpostorSpaceCoordinates.count * MemoryLayout.size, - options: [])! - moleculeImpostorSpaceCoordinateBuffer.label = "Sphere impostor space buffer" + final class BufferComponents { + var vertices: [Float] = [] + var indices: [UInt16] = [] + var impostorSpaceCoordinates: [Float] = [] + var index: UInt16 = 0 + } + var sphereComponents: [Atom.Element: BufferComponents] = [:] + let moleculeScaleFactor = molecule.overallScaleFactor + let centerOfMass = molecule.centerOfMass + + // Read all atoms, split into elements, populate individual atomic element buffers. + for atom in molecule.atoms { + let elementComponents: BufferComponents + if let retrievedComponents = sphereComponents[atom.element] { + elementComponents = retrievedComponents + } else { + elementComponents = BufferComponents() + sphereComponents[atom.element] = elementComponents + } + appendOctagonVertices( + position: (atom.location - centerOfMass) * moleculeScaleFactor, + currentIndex: &elementComponents.index, + indices: &elementComponents.indices, + vertices: &elementComponents.vertices, + impostorSpaceCoordinates: &elementComponents.impostorSpaceCoordinates) + } + // Convert those local buffers into Metal buffers for each atomic element. + for element in Atom.Element.allCases { + guard let elementComponents = sphereComponents[element] else { continue } + let elementIndexBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: elementComponents.indices, + length: elementComponents.indices.count * MemoryLayout.size, + options: [])! + elementIndexBuffer.label = "Sphere index buffer: \(element)" + let elementIndexBufferCount = elementComponents.indices.count + sphereIndexBuffers[element] = elementIndexBuffer + sphereIndexBufferCounts[element] = elementIndexBufferCount + + let elementVertexBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: elementComponents.vertices, + length: elementComponents.vertices.count * MemoryLayout.size, + options: [])! + elementVertexBuffer.label = "Sphere vertex buffer: \(element)" + sphereVertexBuffers[element] = elementVertexBuffer + + let elementImpostorSpaceCoordinateBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: elementComponents.impostorSpaceCoordinates, + length: elementComponents.impostorSpaceCoordinates.count * MemoryLayout.size, + options: [])! + elementImpostorSpaceCoordinateBuffer.label = "Sphere impostor space buffer: \(element)" + sphereImpostorSpaceCoordinateBuffers[element] = elementImpostorSpaceCoordinateBuffer + print("Vertices: \(elementComponents.vertices.count) in element: \(element)") + } - // TODO: Complete cylinder impostors. + // TODO: Loop through all bonds. print("Number of atoms to initialize: \(molecule.atoms.count)") } - func renderMoleculeFrame(buffer: MTLCommandBuffer, renderPass: MTLRenderPassDescriptor) { -// let renderPass = MTLRenderPassDescriptor() -// renderPass.colorAttachments[0].texture = output - renderPass.colorAttachments[0].clearColor = MTLClearColorMake(1, 0, 0, 1) + func renderMoleculeFrame(width: CGFloat, height: CGFloat, buffer: MTLCommandBuffer, renderPass: MTLRenderPassDescriptor) { + let orthographicMatrix = orthographicMatrix(left: -1.0, right: 1.0, bottom: Float(-1.0 * height / width), top: Float(height / width), near: -1.0, far: 1.0) + + renderPass.colorAttachments[0].clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 1.0) renderPass.colorAttachments[0].storeAction = .store renderPass.colorAttachments[0].loadAction = .clear @@ -65,47 +94,80 @@ final class MoleculeRenderer { renderEncoder.setFrontFacing(.counterClockwise) renderEncoder.setRenderPipelineState(sharedMetalRenderingDevice.sphereRaytracingPipelineState) - renderEncoder.setVertexBuffer(moleculeVertexBuffer, offset: 0, index: 0) - renderEncoder.setVertexBuffer(moleculeImpostorSpaceCoordinateBuffer, offset: 0, index: 1) + let depthStencilDescriptor = MTLDepthStencilDescriptor() + depthStencilDescriptor.depthCompareFunction = .lessEqual + depthStencilDescriptor.isDepthWriteEnabled = true + let depthStencilState = sharedMetalRenderingDevice.device.makeDepthStencilState(descriptor: depthStencilDescriptor) + renderEncoder.setDepthStencilState(depthStencilState) + + let moleculeScaleFactor = molecule.overallScaleFactor * currentScale + + for element in Atom.Element.allCases { + guard let sphereIndexBuffer = sphereIndexBuffers[element], + let sphereIndexBufferCount = sphereIndexBufferCounts[element], + let sphereVertexBuffer = sphereVertexBuffers[element], + let sphereImpostorSpaceCoordinateBuffer = sphereImpostorSpaceCoordinateBuffers[element] else { + continue + } + + // Setting sphere vertex buffers. + renderEncoder.setVertexBuffer(sphereVertexBuffer, offset: 0, index: 0) + renderEncoder.setVertexBuffer(sphereImpostorSpaceCoordinateBuffer, offset: 0, index: 1) + let vertexUniforms = sphereVertexUniforms(modelViewProjMatrix: modelViewProjMatrix, + orthographicMatrix: orthographicMatrix, + sphereRadius: element.vanderWaalsRadius * moleculeScaleFactor, + translation: currentTranslation) + let vertexUniformBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: vertexUniforms, + length: vertexUniforms.count * MemoryLayout.size, + options: [])! + renderEncoder.setVertexBuffer(vertexUniformBuffer, offset: 0, index: 3) + + // Setting sphere fragment buffers. + let fragmentUniforms = sphereFragmentUniforms(sphereColor: element.color, + inverseModelViewProjMatrix: modelViewProjMatrix.inverted()) + let fragmentUniformBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: fragmentUniforms, + length: fragmentUniforms.count * MemoryLayout.size, + options: [])! + renderEncoder.setFragmentBuffer(fragmentUniformBuffer, offset: 0, index: 1) + + // Draw all spheres for an element. + renderEncoder.drawIndexedPrimitives(type: .triangle, indexCount: sphereIndexBufferCount, indexType: .uint16, indexBuffer: sphereIndexBuffer, indexBufferOffset: 0) + } - let vertexUniforms = sphereVertexUniforms() - let vertexUniformBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: vertexUniforms, - length: vertexUniforms.count * MemoryLayout.size, - options: [])! - renderEncoder.setVertexBuffer(vertexUniformBuffer, offset: 0, index: 3) + renderEncoder.endEncoding() + } - let fragmentUniforms = sphereFragmentUniforms() - let fragmentUniformBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: fragmentUniforms, - length: fragmentUniforms.count * MemoryLayout.size, - options: [])! - renderEncoder.setFragmentBuffer(fragmentUniformBuffer, offset: 0, index: 1) + func sphereVertexUniforms(modelViewProjMatrix: Matrix4x4, orthographicMatrix: Matrix4x4, sphereRadius: Float, translation: Coordinate) -> [Float] { + return modelViewProjMatrix.toFloatArray() + orthographicMatrix.toFloatArray() + [sphereRadius, 0.0, 0.0, 0.0, translation.x, translation.y, translation.z, 0.0] + } + func sphereFragmentUniforms(sphereColor: Atom.Element.Color, inverseModelViewProjMatrix: Matrix4x4) -> [Float] { + return [sphereColor.red, sphereColor.green, sphereColor.blue, 0.0] + inverseModelViewProjMatrix.toFloatArray() + } +} - renderEncoder.drawIndexedPrimitives(type: .triangle, indexCount: moleculeIndexBufferCount, indexType: .uint16, indexBuffer: moleculeIndexBuffer, indexBufferOffset: 0) +// MARK: - +// MARK: Touch manipulation. - renderEncoder.endEncoding() +extension MoleculeRenderer { + func rotateFromTouch(x: Float, y: Float) { + let rotatedMatrix = modelViewProjMatrix.rotatedFromTouch(x: x, y: y) + if ((rotatedMatrix.m11 >= lowerScaleLimit) && (rotatedMatrix.m11 <= upperScaleLimit)) { + modelViewProjMatrix = rotatedMatrix + } } - func sphereVertexUniforms() -> [Float] { -// float3x3 modelViewProjMatrix; -// float4x4 orthographicMatrix; -// float sphereRadius; -// float3 translation; - let modelViewProjMatrix = Matrix3x3.identity - let orthographicMatrix = Matrix4x4.identity - let sphereRadius: Float = 1.0 - let translation: (Float, Float, Float) = (0.0, 0.0, 0.0) - - return modelViewProjMatrix.toFloatArray() + orthographicMatrix.toFloatArray() + [sphereRadius, 0.0, 0.0, 0.0, translation.0, translation.1, translation.2, 0.0] - } + func scaleFromTouch(scale: Float) { + let scaledMatrix = modelViewProjMatrix.scaledFromTouch(scale: scale) - func sphereFragmentUniforms() -> [Float] { -// float3 sphereColor; -// float3x3 inverseModelViewProjMatrix; - let sphereColor: (Float, Float, Float) = (0.0, 1.0, 0.0) - let inverseModelViewProjMatrix = Matrix3x3.identity + if ((scaledMatrix.m11 >= lowerScaleLimit) && (scaledMatrix.m11 <= upperScaleLimit)) { + modelViewProjMatrix = scaledMatrix + currentScale = currentScale * scale + } + } - return [sphereColor.0, sphereColor.1, sphereColor.2, 0.0] + inverseModelViewProjMatrix.toFloatArray() + func translateFromTouch(x: Float, y: Float, backingWidth: Float) { + currentTranslation = modelViewProjMatrix.translatedFromTouch(currentTranslation: currentTranslation, x: x, y: y, backingWidth: backingWidth) } } diff --git a/Molecules/Rendering/Shaders/SphereRaytracing.metal b/Molecules/Rendering/Shaders/SphereRaytracing.metal index e2d68e1..0c160e4 100644 --- a/Molecules/Rendering/Shaders/SphereRaytracing.metal +++ b/Molecules/Rendering/Shaders/SphereRaytracing.metal @@ -3,7 +3,7 @@ using namespace metal; typedef struct { - float3x3 modelViewProjMatrix; + float4x4 modelViewProjMatrix; float4x4 orthographicMatrix; float sphereRadius; float3 translation; @@ -12,7 +12,7 @@ typedef struct typedef struct { float3 sphereColor; - float3x3 inverseModelViewProjMatrix; + float4x4 inverseModelViewProjMatrix; // TODO: Ambient occlusion texture and parameters. } SphereRaytracingFragmentUniform; @@ -34,13 +34,14 @@ vertex SphereRaytracingVertexIO sphereRaytracingVertex(const device packed_float // ambientOcclusionTextureBase = ambientOcclusionTextureOffset; - float3 transformedPosition = uniform.modelViewProjMatrix * (float3(position[vid]) + uniform.translation); + float4 transformedPosition = uniform.modelViewProjMatrix * (float4(position[vid], 1.0) + float4(uniform.translation, 0.0)); outputVertices.impostorSpaceCoordinate = inputImpostorSpaceCoordinate[vid]; transformedPosition.xy = transformedPosition.xy + inputImpostorSpaceCoordinate[vid].xy * float2(uniform.sphereRadius); - float4 transformedPosition2 = float4(transformedPosition, 1.0) * uniform.orthographicMatrix; + float4 transformedPosition2 = transformedPosition * uniform.orthographicMatrix; + transformedPosition2.z = (transformedPosition2.z + 1.0) * 0.5; - float4 depthAdjustmentPoint = float4(0.0, 0.0, 0.5, 1.0) * uniform.orthographicMatrix; + float4 depthAdjustmentPoint = float4(0.0, 0.0, 0.25, 1.0) * uniform.orthographicMatrix; float depthAdjustmentForOrthographicProjection = depthAdjustmentPoint.z / depthAdjustmentPoint.w; outputVertices.adjustedSphereRadius = uniform.sphereRadius * depthAdjustmentForOrthographicProjection; diff --git a/MoleculesTests/MatrixTests.swift b/MoleculesTests/MatrixTests.swift new file mode 100644 index 0000000..4c0c1ee --- /dev/null +++ b/MoleculesTests/MatrixTests.swift @@ -0,0 +1,161 @@ +import QuartzCore +import XCTest +@testable import Molecules + +func assertEqual(_ lhs: Matrix4x4, _ rhs: Matrix4x4, accuracy: Float, file: StaticString = #filePath, line: UInt = #line) { + XCTAssertEqual(lhs.m11, rhs.m11, accuracy: accuracy, file: file, line: line) + XCTAssertEqual(lhs.m12, rhs.m12, accuracy: accuracy, file: file, line: line) + XCTAssertEqual(lhs.m13, rhs.m13, accuracy: accuracy, file: file, line: line) + XCTAssertEqual(lhs.m14, rhs.m14, accuracy: accuracy, file: file, line: line) + + XCTAssertEqual(lhs.m21, rhs.m21, accuracy: accuracy, file: file, line: line) + XCTAssertEqual(lhs.m22, rhs.m22, accuracy: accuracy, file: file, line: line) + XCTAssertEqual(lhs.m23, rhs.m23, accuracy: accuracy, file: file, line: line) + XCTAssertEqual(lhs.m24, rhs.m24, accuracy: accuracy, file: file, line: line) + + XCTAssertEqual(lhs.m31, rhs.m31, accuracy: accuracy, file: file, line: line) + XCTAssertEqual(lhs.m32, rhs.m32, accuracy: accuracy, file: file, line: line) + XCTAssertEqual(lhs.m33, rhs.m33, accuracy: accuracy, file: file, line: line) + XCTAssertEqual(lhs.m34, rhs.m34, accuracy: accuracy, file: file, line: line) + + XCTAssertEqual(lhs.m41, rhs.m41, accuracy: accuracy, file: file, line: line) + XCTAssertEqual(lhs.m42, rhs.m42, accuracy: accuracy, file: file, line: line) + XCTAssertEqual(lhs.m43, rhs.m43, accuracy: accuracy, file: file, line: line) + XCTAssertEqual(lhs.m44, rhs.m44, accuracy: accuracy, file: file, line: line) +} + + +final class MatrixTests: XCTestCase { + + func testRotation() { +// let identity = CATransform3D(m11: 1.0, m12: 1.0, m13: 1.0, m14: 1.0, +// m21: 1.0, m22: 1.0, m23: 1.0, m24: 1.0, +// m31: 1.0, m32: 1.0, m33: 1.0, m34: 1.0, +// m41: 1.0, m42: 1.0, m43: 1.0, m44: 1.0) + let identity = CATransform3D(m11: 0.25, m12: 0.5, m13: 0.75, m14: 1.0, + m21: 1.25, m22: 1.5, m23: 1.75, m24: 1.0, + m31: 2.25, m32: 2.5, m33: 2.75, m34: 1.0, + m41: 1.0, m42: 1.0, m43: 1.0, m44: 1.0) + let scaled = CATransform3DRotate(identity, 1.0, 1.0, 1.0, 1.0) + print("Identity: \(identity)") + print("Rotated: \(scaled)") + + let startingMatrix = Matrix4x4(rowMajorValues: [ + 0.25, 0.5, 0.75, 1.0, + 1.25, 1.5, 1.75, 1.0, + 2.25, 2.5, 2.75, 1.0, + 1.00, 1.0, 1.00, 1.0]) + let rotatedX = startingMatrix.rotated(angle: 1.0, x: 1.0, y: 0.0, z: 0.0) + assertEqual(rotatedX, Matrix4x4(rowMajorValues: [ + 0.25, 0.5, 0.75, 1.0, + 2.568687598152942, 2.9141309208219512, 3.25957424349096, 1.3817732906760363, + 0.1638414571934439, 0.08854928745850477, 0.013257117723565415, -0.30116867893975674, + 1.0, 1.0, 1.0, 1.0 + ]), accuracy: 0.001) + + let rotatedY = startingMatrix.rotated(angle: 1.0, x: 0.0, y: 1.0, z: 0.0) + assertEqual(rotatedY, Matrix4x4(rowMajorValues: [ + -1.7582341393507321, -1.8335263090856715, -1.9088184788206108, -0.30116867893975674, + 1.25, 1.5, 1.75, 1.0, + 1.4260479344052888, 1.7714912570742976, 2.1169345797433063, 1.3817732906760363, + 1.0, 1.0, 1.0, 1.0 + ]), accuracy: 0.001) + + let rotatedZ = startingMatrix.rotated(angle: 1.0, x: 0.0, y: 0.0, z: 1.0) + assertEqual(rotatedZ, Matrix4x4(rowMajorValues: [ + 1.1869143074769055, 1.5323576301459145, 1.8778009528149235, 1.3817732906760363, + 0.4650101361332006, 0.3897179663982614, 0.31442579666332227, -0.30116867893975674, + 2.25, 2.5, 2.75, 1.0, + 1.0, 1.0, 1.0, 1.0 + ]), accuracy: 0.001) + + let rotatedXYZ = startingMatrix.rotated(angle: 1.0, x: 1.0, y: 1.0, z: 1.0) + assertEqual(rotatedXYZ, Matrix4x4(rowMajorValues: [ + 0.22387419453776192, 0.4738741945377618, 0.7238741945377618, 1.0, + 2.2216469991881973, 2.4716469991881977, 2.7216469991881973, 1.0, + 1.3044788062740413, 1.5544788062740413, 1.8044788062740413, 1.0, + 1.0, 1.0, 1.0, 1.0 + ]), accuracy: 0.001) + } + + func testRotationFromTouch() { + } + + func testScale() { + let startingMatrix = Matrix4x4(rowMajorValues: [ + 0.25, 0.5, 0.75, 1.0, + 1.25, 1.5, 1.75, 1.0, + 2.25, 2.5, 2.75, 1.0, + 1.00, 1.0, 1.00, 1.0]) + let scaled = startingMatrix.scaled(x: 2.0, y: 3.0, z: 4.0) + assertEqual(scaled, Matrix4x4(rowMajorValues: [ + 0.5, 1.0, 1.5, 2.0, + 3.75, 4.5, 5.25, 3.0, + 9.0, 10.0, 11.0, 4.0, + 1.0, 1.0, 1.0, 1.0 + ]), accuracy: 0.001) + } + + func testScaleFromTouch() { + let startingMatrix = Matrix4x4(rowMajorValues: [ + 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, 1.0]) + let scaled = startingMatrix.scaledFromTouch(scale: 0.5) + assertEqual(scaled, Matrix4x4(rowMajorValues: [ + 0.5, 0.5, 0.5, 0.5, + 0.5, 0.5, 0.5, 0.5, + 0.5, 0.5, 0.5, 0.5, + 1.0, 1.0, 1.0, 1.0 + ]), accuracy: 0.001) + } + + func testTranslationFromTouch() { + let identity = Matrix4x4.identity + let initialTranslation = Coordinate(x: 1.0, y: 2.0, z: 3.0) + let translated = identity.translatedFromTouch(currentTranslation: initialTranslation, x: 100.0, y: 50.0, backingWidth: 400.0) + XCTAssertEqual(translated.x, 1.5, accuracy: 0.01) + XCTAssertEqual(translated.y, 2.25, accuracy: 0.01) + + let scaled = identity.scaled(x: 0.25, y: 0.25, z: 0.25) + let translated2 = scaled.translatedFromTouch(currentTranslation: initialTranslation, x: 100.0, y: 50.0, backingWidth: 400.0) + XCTAssertEqual(translated2.x, 3.0, accuracy: 0.01) + XCTAssertEqual(translated2.y, 3.0, accuracy: 0.01) + } + + func testInversion() { + let startingMatrix = Matrix4x4(rowMajorValues: [ + 0.25, 0.5, 0.75, 0.0, + 1.25, 1.5, 1.75, 0.0, + 2.25, 2.5, 2.75, 0.0, + 0.00, 0.0, 0.00, 1.0]) + let inverted = startingMatrix.inverted() + assertEqual(inverted, Matrix4x4(rowMajorValues: [ + 0.25, 1.25, 2.25, 0.0, + 0.5, 1.5, 2.5, 0.0, + 0.75, 1.75, 2.75, 0.0, + 0.00, 0.0, 0.0, 1.0 + ]), accuracy: 0.001) + } + + func testMultiplication() { + let identity = Matrix4x4.identity + let multipliedIdentity = identity * identity + assertEqual(multipliedIdentity, identity, accuracy: 0.001) + + let startingMatrix = Matrix4x4(rowMajorValues: [ + 0.25, 0.5, 0.75, 1.0, + 1.25, 1.5, 1.75, 1.0, + 2.25, 2.5, 2.75, 1.0, + 1.00, 1.0, 1.00, 1.0]) + + let squaredMatrix = startingMatrix * startingMatrix + assertEqual(squaredMatrix, Matrix4x4(rowMajorValues: [ + 3.375, 3.75, 4.125, 2.5, + 7.125, 8.25, 9.375, 5.5, + 10.875, 12.75, 14.625, 8.5, + 4.75, 5.5, 6.25, 4.0 + ]), accuracy: 0.001) + } +} diff --git a/MoleculesTests/RenderingTests.swift b/MoleculesTests/RenderingTests.swift index cc66aa7..e750a4d 100644 --- a/MoleculesTests/RenderingTests.swift +++ b/MoleculesTests/RenderingTests.swift @@ -3,7 +3,7 @@ import XCTest final class RenderingTests: XCTestCase { - func testOctagonCoordinates() throws { + func testOctagonCoordinates() { var currentIndex: UInt16 = 0 var indices: [UInt16] = [] var vertices: [Float] = [] From 79840718efe4349da4db4e5eeaf609158c5bfc03 Mon Sep 17 00:00:00 2001 From: Brad Larson Date: Sun, 30 Jul 2023 22:00:56 -0500 Subject: [PATCH 6/9] Added ball-and-stick rendering mode and bond rendering. --- Molecules.xcodeproj/project.pbxproj | 8 + .../FileProcessing/MolecularStructure.swift | 8 + Molecules/Interface/MoleculeDisplayView.swift | 11 +- .../Interface/MoleculeRenderingOptions.swift | 41 +++++ Molecules/Rendering/MetalRenderView.swift | 85 ++++++++-- .../Rendering/MetalRenderingDevice.swift | 34 +++- Molecules/Rendering/MoleculeRenderer.swift | 150 ++++++++++++++++- .../Shaders/CylinderRaytracing.metal | 158 ++++++++++++++++++ MoleculesTests/MatrixTests.swift | 13 -- MoleculesTests/RenderingTests.swift | 44 +++++ 10 files changed, 516 insertions(+), 36 deletions(-) create mode 100644 Molecules/Interface/MoleculeRenderingOptions.swift create mode 100644 Molecules/Rendering/Shaders/CylinderRaytracing.metal diff --git a/Molecules.xcodeproj/project.pbxproj b/Molecules.xcodeproj/project.pbxproj index 3226bd7..0372b9b 100644 --- a/Molecules.xcodeproj/project.pbxproj +++ b/Molecules.xcodeproj/project.pbxproj @@ -56,9 +56,11 @@ BC5F1FEF2A5B51AF00416000 /* SphereRaytracing.metal in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1FEE2A5B51AF00416000 /* SphereRaytracing.metal */; }; BC5F1FF12A5B63B800416000 /* MetalRenderingDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1FF02A5B63B800416000 /* MetalRenderingDevice.swift */; }; BC5F1FF32A5CD81400416000 /* RenderingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1FF22A5CD81400416000 /* RenderingTests.swift */; }; + BC8F86842A76ECF60038D748 /* CylinderRaytracing.metal in Sources */ = {isa = PBXBuildFile; fileRef = BC8F86832A76ECF60038D748 /* CylinderRaytracing.metal */; }; BCD090CD2A7098850034650F /* MoleculeRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCD090CC2A7098850034650F /* MoleculeRenderer.swift */; }; BCD090CF2A70A7960034650F /* Matrix.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCD090CE2A70A7960034650F /* Matrix.swift */; }; BCD090D12A748C860034650F /* MatrixTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCD090D02A748C860034650F /* MatrixTests.swift */; }; + BCD090D32A76C4770034650F /* MoleculeRenderingOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCD090D22A76C4770034650F /* MoleculeRenderingOptions.swift */; }; BCF265E32A6377D300374DB0 /* AminoAcidResidue.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCF265E22A6377D300374DB0 /* AminoAcidResidue.swift */; }; /* End PBXBuildFile section */ @@ -121,9 +123,11 @@ BC5F1FEE2A5B51AF00416000 /* SphereRaytracing.metal */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.metal; path = SphereRaytracing.metal; sourceTree = ""; }; BC5F1FF02A5B63B800416000 /* MetalRenderingDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetalRenderingDevice.swift; sourceTree = ""; }; BC5F1FF22A5CD81400416000 /* RenderingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenderingTests.swift; sourceTree = ""; }; + BC8F86832A76ECF60038D748 /* CylinderRaytracing.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = CylinderRaytracing.metal; sourceTree = ""; }; BCD090CC2A7098850034650F /* MoleculeRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeRenderer.swift; sourceTree = ""; }; BCD090CE2A70A7960034650F /* Matrix.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Matrix.swift; sourceTree = ""; }; BCD090D02A748C860034650F /* MatrixTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixTests.swift; sourceTree = ""; }; + BCD090D22A76C4770034650F /* MoleculeRenderingOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeRenderingOptions.swift; sourceTree = ""; }; BCF265E22A6377D300374DB0 /* AminoAcidResidue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AminoAcidResidue.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -278,6 +282,7 @@ BC5F1FED2A5B421600416000 /* Shaders */ = { isa = PBXGroup; children = ( + BC8F86832A76ECF60038D748 /* CylinderRaytracing.metal */, BC5F1FEE2A5B51AF00416000 /* SphereRaytracing.metal */, ); path = Shaders; @@ -288,6 +293,7 @@ children = ( BC5807062A56395400313289 /* MoleculeDisplayView.swift */, BC5F1FCE2A5A34FD00416000 /* MoleculeMetadataView.swift */, + BCD090D22A76C4770034650F /* MoleculeRenderingOptions.swift */, ); path = Interface; sourceTree = ""; @@ -465,10 +471,12 @@ BC5F1F7A2A58EB5500416000 /* Atom.swift in Sources */, BC5F1FF12A5B63B800416000 /* MetalRenderingDevice.swift in Sources */, BC5F1F742A579BEE00416000 /* PDBFile.swift in Sources */, + BC8F86842A76ECF60038D748 /* CylinderRaytracing.metal in Sources */, BC5F1FA42A59E96E00416000 /* MoleculeDocument.swift in Sources */, BC5F1FEF2A5B51AF00416000 /* SphereRaytracing.metal in Sources */, BC5F1FCC2A5A29D400416000 /* MetalRenderView.swift in Sources */, BCF265E32A6377D300374DB0 /* AminoAcidResidue.swift in Sources */, + BCD090D32A76C4770034650F /* MoleculeRenderingOptions.swift in Sources */, BCD090CD2A7098850034650F /* MoleculeRenderer.swift in Sources */, BC5807052A56395400313289 /* MoleculesApp.swift in Sources */, ); diff --git a/Molecules/FileProcessing/MolecularStructure.swift b/Molecules/FileProcessing/MolecularStructure.swift index 2a261bf..7932a70 100644 --- a/Molecules/FileProcessing/MolecularStructure.swift +++ b/Molecules/FileProcessing/MolecularStructure.swift @@ -20,6 +20,14 @@ extension MolecularStructure { var overallScaleFactor: Float { return min(suggestedScaleFactor.x, suggestedScaleFactor.y, suggestedScaleFactor.z) } + + var defaultVisualizationStyle: MoleculeVisualizationStyle { + if (atoms.count < 600) && (bonds.count > 0) { + return .ballAndStick + } else { + return .spacefilling + } + } } diff --git a/Molecules/Interface/MoleculeDisplayView.swift b/Molecules/Interface/MoleculeDisplayView.swift index 446fc01..301ee72 100644 --- a/Molecules/Interface/MoleculeDisplayView.swift +++ b/Molecules/Interface/MoleculeDisplayView.swift @@ -7,10 +7,12 @@ struct MoleculeDisplayView: View { @Binding var document: MoleculeDocument @State private var autorotate = true @State private var showingMetadata = false + @State private var showingRenderingOptions = false + @State private var visualizationStyle = MoleculeVisualizationStyle.spacefilling var body: some View { ZStack { - MetalView(molecule: .constant(document.molecule), autorotate: $autorotate) + MetalView(molecule: .constant(document.molecule), autorotate: $autorotate, visualizationStyle: $visualizationStyle) VStack(alignment: .trailing) { Spacer() HStack { @@ -30,13 +32,16 @@ struct MoleculeDisplayView: View { .toolbar { ToolbarItem(placement: .navigationBarTrailing) { Button { - // TODO: Rendering options - print("Show rendering options") + showingRenderingOptions = true } label: { Image(systemName: "rotate.3d") .imageScale(.large) .foregroundColor(.accentColor) } + .popover(isPresented: $showingRenderingOptions) { + MoleculeRenderingOptions(visualizationStyle: $visualizationStyle) + .frame(width: 300, height: 150) + } } ToolbarItem(placement: .navigationBarTrailing) { if (horizontalSizeClass == .regular) && (verticalSizeClass == .regular) { diff --git a/Molecules/Interface/MoleculeRenderingOptions.swift b/Molecules/Interface/MoleculeRenderingOptions.swift new file mode 100644 index 0000000..176306c --- /dev/null +++ b/Molecules/Interface/MoleculeRenderingOptions.swift @@ -0,0 +1,41 @@ +import SwiftUI + +struct MoleculeRenderingOptions: View { + @Binding var visualizationStyle: MoleculeVisualizationStyle + @State var selectedVisualizationStyle: MoleculeVisualizationStyle = .spacefilling + @Environment(\.dismiss) var dismiss + + var body: some View { + VStack { + Text("Visualization Style") + .font(.subheadline) + Picker("Visualization Style", selection: $selectedVisualizationStyle) { + Text("Spacefilling").tag(MoleculeVisualizationStyle.spacefilling) + Text("Ball-and-stick").tag(MoleculeVisualizationStyle.ballAndStick) + } + .pickerStyle(.segmented) + Spacer() + Button { + if visualizationStyle != selectedVisualizationStyle { + visualizationStyle = selectedVisualizationStyle + } + dismiss() + } label: { + Text(visualizationStyle == selectedVisualizationStyle ? "Cancel" : "Render") + } + .buttonStyle(.borderedProminent) + } + .padding() + .presentationDetents([.fraction(0.25)]) + .onAppear { + selectedVisualizationStyle = visualizationStyle + } + } +} + +struct MoleculeRenderingOptions_Previews: PreviewProvider { + static var previews: some View { + MoleculeRenderingOptions(visualizationStyle: .constant(.spacefilling)) + .frame(height: 150) + } +} diff --git a/Molecules/Rendering/MetalRenderView.swift b/Molecules/Rendering/MetalRenderView.swift index 716427c..a855866 100644 --- a/Molecules/Rendering/MetalRenderView.swift +++ b/Molecules/Rendering/MetalRenderView.swift @@ -2,6 +2,10 @@ import Foundation import MetalKit import SwiftUI +protocol MetalRenderViewStatusDelegate: NSObject { + func updateAutorotation(rotating: Bool) +} + class MetalRenderView: MTKView { var moleculeRenderer: MoleculeRenderer! @@ -11,8 +15,17 @@ class MetalRenderView: MTKView { var panGestureRecognizer: UIPanGestureRecognizer! var lastTranslation = CGPoint.zero var lastScale: CGFloat = 1.0 - // TODO: Communicate change in autorotation upwards to SwiftUI. - var autoRotating: Bool = true + var autoRotating: Bool = true { + didSet { + autorotationDisplayLink.isPaused = !autoRotating + if !autoRotating { + statusDelegate?.updateAutorotation(rotating: false) + } + } + } + var autorotationDisplayLink: CADisplayLink! + weak var statusDelegate: MetalRenderViewStatusDelegate? + var hasPresented = false override init(frame frameRect: CGRect, device: MTLDevice?) { super.init(frame: frameRect, device: sharedMetalRenderingDevice.device) @@ -43,6 +56,12 @@ class MetalRenderView: MTKView { self.enableSetNeedsDisplay = true self.isPaused = true + + self.autorotationDisplayLink = CADisplayLink( + target: self, + selector: #selector(autorotationStep) + ) + self.autorotationDisplayLink.add(to: .main, forMode: .default) } override func draw(_ rect:CGRect) { @@ -61,6 +80,23 @@ class MetalRenderView: MTKView { commandBuffer.commit() } } + + @objc func autorotationStep(displaylink: CADisplayLink) { + moleculeRenderer.rotateFromTouch( + x: 1.0, + y: 0.0) + self.setNeedsDisplay() + } + + // FIXME: I should set up a more elegant way of handing switchover of renderers. + func pauseRendering() { + autorotationDisplayLink.isPaused = true + } + + func resumeRendering() { + autorotationDisplayLink.isPaused = false + self.autoRotating = true + } } // MARK: - @@ -117,28 +153,55 @@ extension MetalRenderView { struct MetalView: UIViewRepresentable { @Binding var molecule: MolecularStructure @Binding var autorotate: Bool - - init(molecule: Binding, autorotate: Binding) { + @Binding var visualizationStyle: MoleculeVisualizationStyle + + init(molecule: Binding, autorotate: Binding, visualizationStyle: Binding) { self._molecule = molecule self._autorotate = autorotate + self._visualizationStyle = visualizationStyle } func makeUIView(context: Context) -> MetalRenderView { let view = MetalRenderView(frame: .zero, device: nil) - view.moleculeRenderer = MoleculeRenderer(molecule: molecule) + DispatchQueue.main.async { + self.visualizationStyle = molecule.defaultVisualizationStyle + view.hasPresented = true + } + + view.moleculeRenderer = MoleculeRenderer(molecule: molecule, visualizationStyle: molecule.defaultVisualizationStyle) + view.statusDelegate = context.coordinator return view } func updateUIView(_ uiView: MetalRenderView, context: Context) { - // TODO: Enable or disable the autorotation loop. - uiView.autoRotating = autorotate - print("Autorotate: \(autorotate)") - print("updating view") + if (visualizationStyle != uiView.moleculeRenderer.visualizationStyle) && uiView.hasPresented { + uiView.pauseRendering() + uiView.moleculeRenderer = MoleculeRenderer(molecule: molecule, visualizationStyle: visualizationStyle) + uiView.resumeRendering() + } else { + uiView.autoRotating = autorotate + } } func dismantleUIView(_ uiView: MetalRenderView, coordinator: Coordinator) { - print("dismantling view") - // TODO: Handle removing the view + } + + class Coordinator: NSObject, MetalRenderViewStatusDelegate { + var parent: MetalView + + init(_ parent: MetalView) { + self.parent = parent + } + + func updateAutorotation(rotating: Bool) { + DispatchQueue.main.async { + self.parent.autorotate = rotating + } + } + } + + func makeCoordinator() -> Coordinator { + return Coordinator(self) } } diff --git a/Molecules/Rendering/MetalRenderingDevice.swift b/Molecules/Rendering/MetalRenderingDevice.swift index 1175ba7..13aced1 100644 --- a/Molecules/Rendering/MetalRenderingDevice.swift +++ b/Molecules/Rendering/MetalRenderingDevice.swift @@ -10,6 +10,8 @@ class MetalRenderingDevice { let shaderLibrary: MTLLibrary let sphereRaytracingDescriptor: MTLRenderPipelineDescriptor let sphereRaytracingPipelineState: MTLRenderPipelineState + let cylinderRaytracingDescriptor: MTLRenderPipelineDescriptor + let cylinderRaytracingPipelineState: MTLRenderPipelineState init() { // Configure the Metal device and command queue. @@ -30,11 +32,11 @@ class MetalRenderingDevice { } // Create the render pipeline state for the sphere raytracing shader. - guard let vertexFunction = self.shaderLibrary.makeFunction(name: "sphereRaytracingVertex") else { + guard let sphereVertexFunction = self.shaderLibrary.makeFunction(name: "sphereRaytracingVertex") else { fatalError("Sphere raytracing: could not load vertex function sphereRaytracingVertex") } - guard let fragmentFunction = self.shaderLibrary.makeFunction(name: "sphereRaytracingFragment") else { + guard let sphereFragmentFunction = self.shaderLibrary.makeFunction(name: "sphereRaytracingFragment") else { fatalError("Sphere raytracing: could not load fragment function sphereRaytracingFragment") } @@ -42,8 +44,8 @@ class MetalRenderingDevice { self.sphereRaytracingDescriptor.depthAttachmentPixelFormat = .depth32Float self.sphereRaytracingDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm self.sphereRaytracingDescriptor.rasterSampleCount = 1 - self.sphereRaytracingDescriptor.vertexFunction = vertexFunction - self.sphereRaytracingDescriptor.fragmentFunction = fragmentFunction + self.sphereRaytracingDescriptor.vertexFunction = sphereVertexFunction + self.sphereRaytracingDescriptor.fragmentFunction = sphereFragmentFunction do { self.sphereRaytracingPipelineState = try self.device.makeRenderPipelineState(descriptor: self.sphereRaytracingDescriptor) @@ -51,5 +53,29 @@ class MetalRenderingDevice { // TODO: Examine the potential error cases here. fatalError("Unable to create sphere raytracing render pipeline state with error: \(error)") } + + // Create the render pipeline state for the cylinder raytracing shader. + guard let cylinderVertexFunction = self.shaderLibrary.makeFunction(name: "cylinderRaytracingVertex") else { + fatalError("Cylinder raytracing: could not load vertex function cylinderRaytracingVertex") + } + + guard let cylinderFragmentFunction = self.shaderLibrary.makeFunction(name: "cylinderRaytracingFragment") else { + fatalError("Cylinder raytracing: could not load fragment function cylinderRaytracingFragment") + } + + self.cylinderRaytracingDescriptor = MTLRenderPipelineDescriptor() + self.cylinderRaytracingDescriptor.depthAttachmentPixelFormat = .depth32Float + self.cylinderRaytracingDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm + self.cylinderRaytracingDescriptor.rasterSampleCount = 1 + self.cylinderRaytracingDescriptor.vertexFunction = cylinderVertexFunction + self.cylinderRaytracingDescriptor.fragmentFunction = cylinderFragmentFunction + + do { + self.cylinderRaytracingPipelineState = try self.device.makeRenderPipelineState(descriptor: self.cylinderRaytracingDescriptor) + } catch { + // TODO: Examine the potential error cases here. + fatalError("Unable to create cylinder raytracing render pipeline state with error: \(error)") + } + } } diff --git a/Molecules/Rendering/MoleculeRenderer.swift b/Molecules/Rendering/MoleculeRenderer.swift index 9c5ea5a..a0916e3 100644 --- a/Molecules/Rendering/MoleculeRenderer.swift +++ b/Molecules/Rendering/MoleculeRenderer.swift @@ -1,12 +1,25 @@ import MetalKit +enum MoleculeVisualizationStyle { + case spacefilling + case ballAndStick +} + final class MoleculeRenderer { let molecule: MolecularStructure + let visualizationStyle: MoleculeVisualizationStyle var sphereVertexBuffers: [Atom.Element: MTLBuffer] = [:] var sphereImpostorSpaceCoordinateBuffers: [Atom.Element: MTLBuffer] = [:] var sphereIndexBuffers: [Atom.Element: MTLBuffer] = [:] var sphereIndexBufferCounts: [Atom.Element: Int] = [:] + + var cylinderVertexBuffer: MTLBuffer? + var cylinderDirectionBuffer: MTLBuffer? + var cylinderImpostorSpaceCoordinateBuffer: MTLBuffer? + var cylinderIndexBuffer: MTLBuffer? + var cylinderIndexBufferCount: Int = 0 + let lowerScaleLimit: Float = -100.0 let upperScaleLimit: Float = 100.0 @@ -18,8 +31,9 @@ final class MoleculeRenderer { 0.050796, 0.990772,-0.125664, 0.000000, 0.000000, 0.000000, 0.000000, 1.000000]) - init(molecule: MolecularStructure) { + init(molecule: MolecularStructure, visualizationStyle: MoleculeVisualizationStyle) { self.molecule = molecule + self.visualizationStyle = visualizationStyle initializeMoleculeBuffers() } @@ -73,12 +87,48 @@ final class MoleculeRenderer { options: [])! elementImpostorSpaceCoordinateBuffer.label = "Sphere impostor space buffer: \(element)" sphereImpostorSpaceCoordinateBuffers[element] = elementImpostorSpaceCoordinateBuffer - print("Vertices: \(elementComponents.vertices.count) in element: \(element)") } - // TODO: Loop through all bonds. + // If bonds are visible, populate all of them in a single buffer. + if visualizationStyle == .ballAndStick, molecule.bonds.count > 0 { + var cylinderVertices: [Float] = [] + var cylinderDirections: [Float] = [] + var cylinderIndices: [UInt16] = [] + var cylinderImpostorSpaceCoordinates: [Float] = [] + var cylinderIndex: UInt16 = 0 + + for bond in molecule.bonds { + appendRectangularBondVertices( + start: (bond.start - centerOfMass) * moleculeScaleFactor, + end: (bond.end - centerOfMass) * moleculeScaleFactor, + currentIndex: &cylinderIndex, + indices: &cylinderIndices, + vertices: &cylinderVertices, + directions: &cylinderDirections, + impostorSpaceCoordinates: &cylinderImpostorSpaceCoordinates) + } + + cylinderIndexBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: cylinderIndices, + length: cylinderIndices.count * MemoryLayout.size, + options: [])! + cylinderIndexBuffer?.label = "Cylinder index buffer" + cylinderIndexBufferCount = cylinderIndices.count - print("Number of atoms to initialize: \(molecule.atoms.count)") + cylinderVertexBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: cylinderVertices, + length: cylinderVertices.count * MemoryLayout.size, + options: [])! + cylinderVertexBuffer?.label = "Cylinder vertex buffer" + + cylinderDirectionBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: cylinderDirections, + length: cylinderDirections.count * MemoryLayout.size, + options: [])! + cylinderDirectionBuffer?.label = "Cylinder direction buffer" + + cylinderImpostorSpaceCoordinateBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: cylinderImpostorSpaceCoordinates, + length: cylinderImpostorSpaceCoordinates.count * MemoryLayout.size, + options: [])! + cylinderImpostorSpaceCoordinateBuffer?.label = "Cylinder impostor space buffer" + } } func renderMoleculeFrame(width: CGFloat, height: CGFloat, buffer: MTLCommandBuffer, renderPass: MTLRenderPassDescriptor) { @@ -100,6 +150,12 @@ final class MoleculeRenderer { let depthStencilState = sharedMetalRenderingDevice.device.makeDepthStencilState(descriptor: depthStencilDescriptor) renderEncoder.setDepthStencilState(depthStencilState) + let atomRadiusScaleFactor: Float + switch visualizationStyle { + case .spacefilling: atomRadiusScaleFactor = 1.0 + case .ballAndStick: atomRadiusScaleFactor = 0.35 + } + let moleculeScaleFactor = molecule.overallScaleFactor * currentScale for element in Atom.Element.allCases { @@ -115,7 +171,7 @@ final class MoleculeRenderer { renderEncoder.setVertexBuffer(sphereImpostorSpaceCoordinateBuffer, offset: 0, index: 1) let vertexUniforms = sphereVertexUniforms(modelViewProjMatrix: modelViewProjMatrix, orthographicMatrix: orthographicMatrix, - sphereRadius: element.vanderWaalsRadius * moleculeScaleFactor, + sphereRadius: element.vanderWaalsRadius * moleculeScaleFactor * atomRadiusScaleFactor, translation: currentTranslation) let vertexUniformBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: vertexUniforms, length: vertexUniforms.count * MemoryLayout.size, @@ -134,6 +190,39 @@ final class MoleculeRenderer { renderEncoder.drawIndexedPrimitives(type: .triangle, indexCount: sphereIndexBufferCount, indexType: .uint16, indexBuffer: sphereIndexBuffer, indexBufferOffset: 0) } + if visualizationStyle == .ballAndStick, molecule.bonds.count > 0 { + renderEncoder.setRenderPipelineState(sharedMetalRenderingDevice.cylinderRaytracingPipelineState) + + // Setting cylinder vertex buffers. + let bondRadius: Float = 1.0 + let bondRadiusScaleFactor: Float = 0.15 + let bondColor = Atom.Element.Color(0.75, 0.75, 0.75) + renderEncoder.setVertexBuffer(cylinderVertexBuffer, offset: 0, index: 0) + renderEncoder.setVertexBuffer(cylinderDirectionBuffer, offset: 0, index: 1) + renderEncoder.setVertexBuffer(cylinderImpostorSpaceCoordinateBuffer, offset: 0, index: 2) + let vertexUniforms = cylinderVertexUniforms(modelViewProjMatrix: modelViewProjMatrix, + orthographicMatrix: orthographicMatrix, + cylinderRadius: bondRadius * moleculeScaleFactor * bondRadiusScaleFactor, + translation: currentTranslation) + let vertexUniformBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: vertexUniforms, + length: vertexUniforms.count * MemoryLayout.size, + options: [])! + renderEncoder.setVertexBuffer(vertexUniformBuffer, offset: 0, index: 4) + + // Setting cylinder fragment buffers. + let fragmentUniforms = cylinderFragmentUniforms(cylinderColor: bondColor, + inverseModelViewProjMatrix: modelViewProjMatrix.inverted()) + let fragmentUniformBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: fragmentUniforms, + length: fragmentUniforms.count * MemoryLayout.size, + options: [])! + renderEncoder.setFragmentBuffer(fragmentUniformBuffer, offset: 0, index: 1) + + // Draw all cylinders for bonds. + if let cylinderIndexBuffer = cylinderIndexBuffer { + renderEncoder.drawIndexedPrimitives(type: .triangle, indexCount: cylinderIndexBufferCount, indexType: .uint16, indexBuffer: cylinderIndexBuffer, indexBufferOffset: 0) + } + } + renderEncoder.endEncoding() } @@ -144,6 +233,14 @@ final class MoleculeRenderer { func sphereFragmentUniforms(sphereColor: Atom.Element.Color, inverseModelViewProjMatrix: Matrix4x4) -> [Float] { return [sphereColor.red, sphereColor.green, sphereColor.blue, 0.0] + inverseModelViewProjMatrix.toFloatArray() } + + func cylinderVertexUniforms(modelViewProjMatrix: Matrix4x4, orthographicMatrix: Matrix4x4, cylinderRadius: Float, translation: Coordinate) -> [Float] { + return modelViewProjMatrix.toFloatArray() + orthographicMatrix.toFloatArray() + [cylinderRadius, 0.0, 0.0, 0.0, translation.x, translation.y, translation.z, 0.0] + } + + func cylinderFragmentUniforms(cylinderColor: Atom.Element.Color, inverseModelViewProjMatrix: Matrix4x4) -> [Float] { + return [cylinderColor.red, cylinderColor.green, cylinderColor.blue, 0.0] + inverseModelViewProjMatrix.toFloatArray() + } } // MARK: - @@ -210,3 +307,46 @@ func appendOctagonVertices( currentIndex += 8 } + +func appendRectangularBondVertices( + start: Coordinate, + end: Coordinate, + currentIndex: inout UInt16, + indices: inout [UInt16], + vertices: inout [Float], + directions: inout [Float], + impostorSpaceCoordinates: inout [Float] +) { + // Add the start and end points twice, to be displaced in the vertex shader. + let startVertex = [-start.x, start.y, start.z] + vertices.append(contentsOf: startVertex) + vertices.append(contentsOf: startVertex) + let endVertex = [-end.x, end.y, end.z] + vertices.append(contentsOf: endVertex) + vertices.append(contentsOf: endVertex) + + // The same bond direction is used by each vertex. + let cylinderDirection = [start.x - end.x, end.y - start.y, end.z - start.z] + for _ in 0..<4 { + directions.append(contentsOf: cylinderDirection) + } + + let cylinderImpostorCoordinates: [Float] = [ + -1.0, -1.0, + 1.0, -1.0, + -1.0, 1.0, + 1.0, 1.0 + ] + impostorSpaceCoordinates += cylinderImpostorCoordinates + + let cylinderIndices: [UInt16] = [ + currentIndex, + currentIndex + 1, + currentIndex + 2, + currentIndex + 1, + currentIndex + 3, + currentIndex + 2 + ] + indices.append(contentsOf: cylinderIndices) + currentIndex += 4 +} diff --git a/Molecules/Rendering/Shaders/CylinderRaytracing.metal b/Molecules/Rendering/Shaders/CylinderRaytracing.metal new file mode 100644 index 0000000..b838d0c --- /dev/null +++ b/Molecules/Rendering/Shaders/CylinderRaytracing.metal @@ -0,0 +1,158 @@ +#include +using namespace metal; + +typedef struct +{ + float4x4 modelViewProjMatrix; + float4x4 orthographicMatrix; + float cylinderRadius; + float3 translation; +} CylinderRaytracingVertexUniform; + +typedef struct +{ + float3 cylinderColor; + float4x4 inverseModelViewProjMatrix; + // TODO: Ambient occlusion texture and parameters. +} CylinderRaytracingFragmentUniform; + +struct CylinderRaytracingVertexIO +{ + float4 position [[position]]; + float2 impostorSpaceCoordinate [[user(impostorSpaceCoordinate)]]; + float3 normalizedViewCoordinate [[user(normalizedViewCoordinate)]]; + float3 normalAlongCenterAxis [[user(normalAlongCenterAxis)]]; + float2 rotationFactor [[user(rotationFactor)]]; + float depthOffsetAlongCenterAxis; + float normalizedDepthOffsetAlongCenterAxis; + float normalizedDisplacementAtEndCaps; + float normalizedRadialDisplacementAtEndCaps; + float depthAdjustmentForOrthographicProjection; + float normalizedDistanceAlongZAxis; +}; + +vertex CylinderRaytracingVertexIO cylinderRaytracingVertex(const device packed_float3 *position [[buffer(0)]], + const device packed_float3 *direction [[buffer(1)]], + const device packed_float2 *inputImpostorSpaceCoordinate [[buffer(2)]], + //const device packed_float2 *ambientOcclusionTextureOffset [[buffer(2)]], + constant CylinderRaytracingVertexUniform& uniform [[buffer(4)]], + uint vid [[vertex_id]]) +{ + CylinderRaytracingVertexIO outputVertices; + +// ambientOcclusionTextureBase = ambientOcclusionTextureOffset; + outputVertices.normalizedDistanceAlongZAxis = inputImpostorSpaceCoordinate[vid].y; + + float4 transformedDirection, transformedPosition, transformedOtherPosition; + float3 viewDisplacementForVertex, displacementDirectionAtEndCap; + float displacementAtEndCaps, lengthOfCylinder, lengthOfCylinderInView; + + outputVertices.depthAdjustmentForOrthographicProjection = (float4(0.0, 0.0, 0.5, 1.0) * uniform.orthographicMatrix).z; + + transformedPosition = uniform.modelViewProjMatrix * (float4(position[vid] + uniform.translation, 1.0)); + transformedPosition.z = (transformedPosition.z + 1.0) * 0.5; + + transformedOtherPosition = uniform.modelViewProjMatrix * float4(position[vid] + direction[vid] + uniform.translation, 1.0); + transformedOtherPosition.z = (transformedOtherPosition.z + 1.0) * 0.5; + + transformedDirection = transformedOtherPosition - transformedPosition; + + lengthOfCylinder = length(transformedDirection.xyz); + lengthOfCylinderInView = length(transformedDirection.xy); + outputVertices.rotationFactor = transformedDirection.xy / lengthOfCylinderInView; + + displacementAtEndCaps = uniform.cylinderRadius * (transformedOtherPosition.z - transformedPosition.z) / lengthOfCylinder; + outputVertices.normalizedDisplacementAtEndCaps = displacementAtEndCaps / lengthOfCylinderInView; + outputVertices.normalizedRadialDisplacementAtEndCaps = displacementAtEndCaps / uniform.cylinderRadius; + + outputVertices.depthOffsetAlongCenterAxis = uniform.cylinderRadius * lengthOfCylinder * rsqrt(lengthOfCylinder * lengthOfCylinder - (transformedOtherPosition.z - transformedPosition.z) * (transformedOtherPosition.z - transformedPosition.z)); + outputVertices.depthOffsetAlongCenterAxis = clamp(outputVertices.depthOffsetAlongCenterAxis, 0.0, uniform.cylinderRadius * 2.0); + outputVertices.normalizedDepthOffsetAlongCenterAxis = outputVertices.depthOffsetAlongCenterAxis / (uniform.cylinderRadius); + + displacementDirectionAtEndCap.xy = displacementAtEndCaps * outputVertices.rotationFactor; + displacementDirectionAtEndCap.z = transformedDirection.z * displacementAtEndCaps / lengthOfCylinder; + + transformedDirection.xy = normalize(transformedDirection.xy); + + if ((displacementAtEndCaps * inputImpostorSpaceCoordinate[vid].y) > 0.0) + { + viewDisplacementForVertex.x = inputImpostorSpaceCoordinate[vid].x * transformedDirection.y * uniform.cylinderRadius + displacementDirectionAtEndCap.x; + viewDisplacementForVertex.y = -inputImpostorSpaceCoordinate[vid].x * transformedDirection.x * uniform.cylinderRadius + displacementDirectionAtEndCap.y; + viewDisplacementForVertex.z = displacementDirectionAtEndCap.z; + outputVertices.impostorSpaceCoordinate = float2(inputImpostorSpaceCoordinate[vid].x, inputImpostorSpaceCoordinate[vid].y + 1.0 * outputVertices.normalizedDisplacementAtEndCaps); + } + else + { + viewDisplacementForVertex.x = inputImpostorSpaceCoordinate[vid].x * transformedDirection.y * uniform.cylinderRadius; + viewDisplacementForVertex.y = -inputImpostorSpaceCoordinate[vid].x * transformedDirection.x * uniform.cylinderRadius; + viewDisplacementForVertex.z = 0.0; + // impostorSpaceCoordinate = inputImpostorSpaceCoordinate.st; + outputVertices.impostorSpaceCoordinate = float2(inputImpostorSpaceCoordinate[vid].x, inputImpostorSpaceCoordinate[vid].y); + } + + transformedPosition.xyz = transformedPosition.xyz + viewDisplacementForVertex; + // transformedPosition.z = 0.0; + + transformedPosition = transformedPosition * uniform.orthographicMatrix; + + outputVertices.normalizedViewCoordinate = ((transformedPosition / 2.0) + 0.5).xyz; + + outputVertices.position = transformedPosition; + return outputVertices; +} + +struct FragmentColorDepth { + half4 color [[color(0)]]; + float depth [[depth(any)]]; +}; + +constant half3 lightPosition = half3(0.312757, 0.248372, 0.916785); + +fragment FragmentColorDepth cylinderRaytracingFragment(CylinderRaytracingVertexIO fragmentInput [[stage_in]], +// texture2d ambientOcclusionTexture [[texture(0)]], +// texture2d precalculatedAOLookupTexture [[texture(1)]], + constant CylinderRaytracingFragmentUniform& uniform [[ buffer(1) ]]) +{ + float adjustmentFromCenterAxis = sqrt(1.0 - fragmentInput.impostorSpaceCoordinate.x * fragmentInput.impostorSpaceCoordinate.x); + float displacementFromCurvature = fragmentInput.normalizedDisplacementAtEndCaps * adjustmentFromCenterAxis; + float depthOffset = fragmentInput.depthOffsetAlongCenterAxis * adjustmentFromCenterAxis * fragmentInput.depthAdjustmentForOrthographicProjection; + + half3 normal = half3(fragmentInput.normalizedRadialDisplacementAtEndCaps * fragmentInput.rotationFactor.x * adjustmentFromCenterAxis + fragmentInput.impostorSpaceCoordinate.x * fragmentInput.rotationFactor.y, + -(fragmentInput.normalizedRadialDisplacementAtEndCaps * fragmentInput.rotationFactor.y * adjustmentFromCenterAxis + fragmentInput.impostorSpaceCoordinate.x * fragmentInput.rotationFactor.x), + fragmentInput.normalizedDepthOffsetAlongCenterAxis * adjustmentFromCenterAxis); + + normal = normalize(normal); + + if ( (fragmentInput.impostorSpaceCoordinate.y <= (-1.0 + displacementFromCurvature)) || (fragmentInput.impostorSpaceCoordinate.y >= (1.0 + displacementFromCurvature))) + { + FragmentColorDepth colorDepth; + colorDepth.color = half4(0.0); // Black background +// colorDepth.color = half4(1.0); // White background + colorDepth.depth = 1.0; + return colorDepth; + } + else + { + float currentDepthValue = fragmentInput.normalizedViewCoordinate.z - depthOffset + 0.0025; + + half3 finalCylinderColor = half3(uniform.cylinderColor); + + // ambient +// vec3 aoNormal = vec3(0.5, 0.5, normalizedDistanceAlongZAxis); +// vec2 textureCoordinateForAOLookup = ambientOcclusionTextureBase + ambientOcclusionTexturePatchWidth * 0.5 * textureCoordinateForCylinderSurfacePosition(aoNormal); +// vec3 ambientOcclusionIntensity = texture2D(ambientOcclusionTexture, textureCoordinateForAOLookup).rgb; + + half lightingIntensity = 0.1 + clamp(dot(lightPosition, normal), 0.0h, 1.0h); // * ambientOcclusionIntensity.r; + finalCylinderColor *= lightingIntensity; + + // Per fragment specular lighting + lightingIntensity = clamp(dot(lightPosition, normal), 0.0h, 1.0h); + lightingIntensity = pow(lightingIntensity, 60.0h) /* * ambientOcclusionIntensity.r */ * 1.2h; + finalCylinderColor += 0.4 * lightingIntensity; + + FragmentColorDepth colorDepth; + colorDepth.color = half4(finalCylinderColor, 1.0); + colorDepth.depth = currentDepthValue; + return colorDepth; + } +} diff --git a/MoleculesTests/MatrixTests.swift b/MoleculesTests/MatrixTests.swift index 4c0c1ee..cddb2f2 100644 --- a/MoleculesTests/MatrixTests.swift +++ b/MoleculesTests/MatrixTests.swift @@ -1,4 +1,3 @@ -import QuartzCore import XCTest @testable import Molecules @@ -28,18 +27,6 @@ func assertEqual(_ lhs: Matrix4x4, _ rhs: Matrix4x4, accuracy: Float, file: Stat final class MatrixTests: XCTestCase { func testRotation() { -// let identity = CATransform3D(m11: 1.0, m12: 1.0, m13: 1.0, m14: 1.0, -// m21: 1.0, m22: 1.0, m23: 1.0, m24: 1.0, -// m31: 1.0, m32: 1.0, m33: 1.0, m34: 1.0, -// m41: 1.0, m42: 1.0, m43: 1.0, m44: 1.0) - let identity = CATransform3D(m11: 0.25, m12: 0.5, m13: 0.75, m14: 1.0, - m21: 1.25, m22: 1.5, m23: 1.75, m24: 1.0, - m31: 2.25, m32: 2.5, m33: 2.75, m34: 1.0, - m41: 1.0, m42: 1.0, m43: 1.0, m44: 1.0) - let scaled = CATransform3DRotate(identity, 1.0, 1.0, 1.0, 1.0) - print("Identity: \(identity)") - print("Rotated: \(scaled)") - let startingMatrix = Matrix4x4(rowMajorValues: [ 0.25, 0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 1.0, diff --git a/MoleculesTests/RenderingTests.swift b/MoleculesTests/RenderingTests.swift index e750a4d..c4f3efd 100644 --- a/MoleculesTests/RenderingTests.swift +++ b/MoleculesTests/RenderingTests.swift @@ -39,4 +39,48 @@ final class RenderingTests: XCTestCase { XCTAssertEqual(vertices[4], 2.0) XCTAssertEqual(vertices[5], 3.0) } + + func testCylinderCoordinates() { + var currentIndex: UInt16 = 0 + var indices: [UInt16] = [] + var vertices: [Float] = [] + var directions: [Float] = [] + var impostorSpaceCoordinates: [Float] = [] + + let start = Coordinate(x: -1.0, y: 2.0, z: 3.0) + let end = Coordinate(x: -1.0, y: 2.0, z: 3.0) + + appendRectangularBondVertices( + start: start, + end: end, + currentIndex: ¤tIndex, + indices: &indices, + vertices: &vertices, + directions: &directions, + impostorSpaceCoordinates: &impostorSpaceCoordinates + ) + XCTAssertEqual(currentIndex, 4) + XCTAssertEqual(indices.count, 6) + XCTAssertEqual(vertices.count, 12) + XCTAssertEqual(directions.count, 12) + XCTAssertEqual(impostorSpaceCoordinates.count, 8) + + appendRectangularBondVertices( + start: start, + end: end, + currentIndex: ¤tIndex, + indices: &indices, + vertices: &vertices, + directions: &directions, + impostorSpaceCoordinates: &impostorSpaceCoordinates + ) + XCTAssertEqual(currentIndex, 8) + XCTAssertEqual(indices.count, 12) + XCTAssertEqual(vertices.count, 24) + XCTAssertEqual(directions.count, 24) + XCTAssertEqual(impostorSpaceCoordinates.count, 16) + XCTAssertEqual(indices[0], 0) + XCTAssertEqual(indices[6], 4) + + } } From 361ec000d6d1f896ea1f8e664204cff236f614cb Mon Sep 17 00:00:00 2001 From: Brad Larson Date: Mon, 31 Jul 2023 17:47:51 -0500 Subject: [PATCH 7/9] Adding document icons, trying to work around document filetype conflicts. --- Molecules.xcodeproj/project.pbxproj | 18 ++++ .../FileProcessing/MoleculeDocument.swift | 19 +++- Molecules/Icons/Document-molecules-320.png | Bin 0 -> 93052 bytes Molecules/Icons/Document-molecules-64.png | Bin 0 -> 7547 bytes Molecules/Info.plist | 89 ++++++++++++++---- Molecules/Interface/MoleculeDisplayView.swift | 8 +- Molecules/MoleculesApp.swift | 6 +- Molecules/Rendering/MetalRenderView.swift | 5 + 8 files changed, 118 insertions(+), 27 deletions(-) create mode 100644 Molecules/Icons/Document-molecules-320.png create mode 100644 Molecules/Icons/Document-molecules-64.png diff --git a/Molecules.xcodeproj/project.pbxproj b/Molecules.xcodeproj/project.pbxproj index 0372b9b..a83fb6c 100644 --- a/Molecules.xcodeproj/project.pbxproj +++ b/Molecules.xcodeproj/project.pbxproj @@ -7,6 +7,8 @@ objects = { /* Begin PBXBuildFile section */ + BC38949A2A78429F00089C05 /* Document-molecules-320.png in Resources */ = {isa = PBXBuildFile; fileRef = BC3894962A78429F00089C05 /* Document-molecules-320.png */; }; + BC38949B2A78429F00089C05 /* Document-molecules-64.png in Resources */ = {isa = PBXBuildFile; fileRef = BC3894972A78429F00089C05 /* Document-molecules-64.png */; }; BC5807052A56395400313289 /* MoleculesApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5807042A56395400313289 /* MoleculesApp.swift */; }; BC5807072A56395400313289 /* MoleculeDisplayView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5807062A56395400313289 /* MoleculeDisplayView.swift */; }; BC5807092A56395900313289 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BC5807082A56395900313289 /* Assets.xcassets */; }; @@ -82,6 +84,8 @@ /* End PBXContainerItemProxy section */ /* Begin PBXFileReference section */ + BC3894962A78429F00089C05 /* Document-molecules-320.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Document-molecules-320.png"; sourceTree = ""; }; + BC3894972A78429F00089C05 /* Document-molecules-64.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "Document-molecules-64.png"; sourceTree = ""; }; BC5807012A56395400313289 /* Molecules.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Molecules.app; sourceTree = BUILT_PRODUCTS_DIR; }; BC5807042A56395400313289 /* MoleculesApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculesApp.swift; sourceTree = ""; }; BC5807062A56395400313289 /* MoleculeDisplayView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeDisplayView.swift; sourceTree = ""; }; @@ -157,6 +161,15 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + BC3894952A78429F00089C05 /* Icons */ = { + isa = PBXGroup; + children = ( + BC3894962A78429F00089C05 /* Document-molecules-320.png */, + BC3894972A78429F00089C05 /* Document-molecules-64.png */, + ); + path = Icons; + sourceTree = ""; + }; BC5806F82A56395400313289 = { isa = PBXGroup; children = ( @@ -185,6 +198,7 @@ BC5F1FF42A62F52800416000 /* Interface */, BC5F1F722A579BCE00416000 /* FileProcessing */, BC5F1FA62A59FB1F00416000 /* Rendering */, + BC3894952A78429F00089C05 /* Icons */, BC5F1F6E2A578BA000416000 /* Info.plist */, BC5807082A56395900313289 /* Assets.xcassets */, BC58070A2A56395900313289 /* Preview Content */, @@ -411,6 +425,7 @@ BC5F1FE32A5B097E00416000 /* TheoreticalXenonPump.pdb.gz in Resources */, BC5F1FE12A5B097E00416000 /* 3QE5.pdb.gz in Resources */, BC5F1FB92A5A0D4300416000 /* TransferRNA.pdb in Resources */, + BC38949B2A78429F00089C05 /* Document-molecules-64.png in Resources */, BC5F1FB32A5A0D4300416000 /* Insulin.pdb in Resources */, BC5F1FDF2A5B097E00416000 /* Acetylcholinesterase.pdb.gz in Resources */, BC5F1FB52A5A0D4300416000 /* Buckminsterfullerene.sdf in Resources */, @@ -418,6 +433,7 @@ BC5F1F952A59DAC000416000 /* Heme.sdf in Resources */, BC58070C2A56395900313289 /* Preview Assets.xcassets in Resources */, BC5F1F9B2A59DAC000416000 /* Nanotube.pdb in Resources */, + BC38949A2A78429F00089C05 /* Document-molecules-320.png in Resources */, BC5807092A56395900313289 /* Assets.xcassets in Resources */, BC5F1FB12A5A0D4300416000 /* DNA.pdb in Resources */, BC5F1F8D2A59DAC000416000 /* TheoreticalBearing.pdb in Resources */, @@ -651,6 +667,7 @@ INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; INFOPLIST_KEY_UISupportsDocumentBrowser = YES; + INFOPLIST_KEY_UIUserInterfaceStyle = Dark; IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -683,6 +700,7 @@ INFOPLIST_KEY_UISupportedInterfaceOrientations = UIInterfaceOrientationPortrait; INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown"; INFOPLIST_KEY_UISupportsDocumentBrowser = YES; + INFOPLIST_KEY_UIUserInterfaceStyle = Dark; IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", diff --git a/Molecules/FileProcessing/MoleculeDocument.swift b/Molecules/FileProcessing/MoleculeDocument.swift index 3015022..55b5a3f 100644 --- a/Molecules/FileProcessing/MoleculeDocument.swift +++ b/Molecules/FileProcessing/MoleculeDocument.swift @@ -6,8 +6,10 @@ import UniformTypeIdentifiers /// process gzip-compressed .pdb.gz files, although that type registration is currently via fairly /// broad .gz associations. struct MoleculeDocument: FileDocument { - static var readableContentTypes: [UTType] { [.pdb, .sdf, .xyz, .gzip] } - + static var readableContentTypes: [UTType] { [.pdb, .pdbimolebuilder, .sdf, .sdfmodizer, .xyz, .gzip] } + // NOTE: Leaving this in here in case I need to override and load all filetypes. +// static var readableContentTypes: [UTType] { [.item] } + let molecule: MolecularStructure /// The initializer as used by SwiftUI. @@ -25,9 +27,9 @@ struct MoleculeDocument: FileDocument { /// - filename: The string filename, used for later processing. init(data: Data, contentType: UTType, filename: String) throws { switch contentType { - case .pdb: + case .pdb, .pdbimolebuilder: molecule = try PDBFile(data: data) - case .sdf: + case .sdf, .sdfmodizer: molecule = try SDFFile(data: data) case .xyz: molecule = try XYZFile(data: data) @@ -59,6 +61,15 @@ extension UTType { static var xyz: UTType { UTType(exportedAs: "com.sunsetlakesoftware.molecules.xyz") } + + // FIXME: Find way to handle conflicting types, rather than enumerating them. + static var pdbimolebuilder: UTType { + UTType(importedAs: "hssong.chemistry.imolebuilder.pdb") + } + + static var sdfmodizer: UTType { + UTType(importedAs: "com.yoyofr.modizer.vgmstream") + } } diff --git a/Molecules/Icons/Document-molecules-320.png b/Molecules/Icons/Document-molecules-320.png new file mode 100644 index 0000000000000000000000000000000000000000..d29ba4eb0a1dd4e52dee512f3ae0abecee1eefd0 GIT binary patch literal 93052 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�rd1w<_YHSP-9Q0`TW=Oc(maZ@5(Xd)u?VO@-U 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 KMNUMnLSTZIs3#Wy literal 0 HcmV?d00001 diff --git a/Molecules/Icons/Document-molecules-64.png b/Molecules/Icons/Document-molecules-64.png new file mode 100644 index 0000000000000000000000000000000000000000..d26660bfd70ff9bf1e3b67349dcfe6d67d0d1fd1 GIT binary patch literal 7547 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 CFBundleTypeRole Viewer + LSHandlerRank + Owner LSItemContentTypes com.sunsetlakesoftware.molecules.pdb @@ -21,6 +23,8 @@ SDF CFBundleTypeRole Viewer + LSHandlerRank + Owner LSItemContentTypes com.sunsetlakesoftware.molecules.sdf @@ -33,6 +37,8 @@ XYZ CFBundleTypeRole Viewer + LSHandlerRank + Owner LSItemContentTypes com.sunsetlakesoftware.molecules.xyz @@ -43,68 +49,117 @@ UIFileSharingEnabled - UIUserInterfaceStyle - Dark UTExportedTypeDeclarations UTTypeConformsTo - public.plain-text - public.text + public.data UTTypeDescription Protein Data Bank file UTTypeIconFiles - + + Document-molecules-320 + Document-molecules-64 + UTTypeIdentifier com.sunsetlakesoftware.molecules.pdb UTTypeTagSpecification public.filename-extension - pdb + + pdb + public.mime-type - chemical/x-pdb + + chemical/pdb + UTTypeConformsTo - public.plain-text - public.text + public.data UTTypeDescription SDF file UTTypeIconFiles - + + Document-molecules-320 + Document-molecules-64 + Document-molecules-44 + Document-molecules-22 + UTTypeIdentifier com.sunsetlakesoftware.molecules.sdf UTTypeTagSpecification public.filename-extension - sdf + + sdf + public.mime-type - chemical/x-mdl-sdfile + + chemical/sdf + UTTypeConformsTo - public.plain-text - public.text + public.data UTTypeDescription XYZ file UTTypeIconFiles - + + Document-molecules-320 + Document-molecules-64 + Document-molecules-44 + Document-molecules-22 + UTTypeIdentifier com.sunsetlakesoftware.molecules.xyz UTTypeTagSpecification public.filename-extension - xyz + + xyz + public.mime-type - chemical/x-xyz + + chemical/xyz + + + + + UTImportedTypeDeclarations + + + UTTypeIconFiles + + UTTypeIdentifier + hssong.chemistry.imolebuilder.pdb + UTTypeTagSpecification + + public.filename-extension + + pdb + + + + + UTTypeIconFiles + + UTTypeIdentifier + com.yoyofr.modizer.vgmstream + UTTypeTagSpecification + + public.filename-extension + + sdf + diff --git a/Molecules/Interface/MoleculeDisplayView.swift b/Molecules/Interface/MoleculeDisplayView.swift index 301ee72..3d9cb2c 100644 --- a/Molecules/Interface/MoleculeDisplayView.swift +++ b/Molecules/Interface/MoleculeDisplayView.swift @@ -4,7 +4,7 @@ struct MoleculeDisplayView: View { @Environment(\.horizontalSizeClass) var horizontalSizeClass @Environment(\.verticalSizeClass) var verticalSizeClass - @Binding var document: MoleculeDocument + let document: MoleculeDocument @State private var autorotate = true @State private var showingMetadata = false @State private var showingRenderingOptions = false @@ -80,7 +80,7 @@ struct MoleculeDisplayView_Previews: PreviewProvider { let data = try! Bundle.main.loadData(forResource: "Caffeine", withExtension: "pdb") let document = try! MoleculeDocument(data: data, contentType: .pdb, filename: "Caffeine.pdb") NavigationStack { - MoleculeDisplayView(document: .constant(document)) + MoleculeDisplayView(document: document) .navigationTitle("Caffeine") .toolbarRole(.automatic) .navigationBarTitleDisplayMode(.inline) @@ -89,7 +89,7 @@ struct MoleculeDisplayView_Previews: PreviewProvider { .previewDisplayName("iPhone 14") NavigationStack { - MoleculeDisplayView(document: .constant(document)) + MoleculeDisplayView(document: document) .navigationTitle("Caffeine") .toolbarRole(.automatic) .navigationBarTitleDisplayMode(.inline) @@ -98,7 +98,7 @@ struct MoleculeDisplayView_Previews: PreviewProvider { .previewDisplayName("iPad") NavigationStack { - MoleculeDisplayView(document: .constant(document)) + MoleculeDisplayView(document: document) .navigationTitle("Caffeine") .toolbarRole(.automatic) .navigationBarTitleDisplayMode(.inline) diff --git a/Molecules/MoleculesApp.swift b/Molecules/MoleculesApp.swift index 62133fc..ac73fdd 100644 --- a/Molecules/MoleculesApp.swift +++ b/Molecules/MoleculesApp.swift @@ -10,7 +10,7 @@ struct MoleculesApp: App { var body: some Scene { DocumentGroup(viewing: MoleculeDocument.self) { file in - MoleculeDisplayView(document: file.$document) + MoleculeDisplayView(document: file.document) .toolbarRole(.automatic) .navigationBarTitleDisplayMode(.inline) } @@ -41,7 +41,9 @@ func copyBuiltInMoleculesIfNeeded() { for builtInMolecule in builtInMolecules { let filename = builtInMolecule.lastPathComponent let destinationInDocuments = documents.appendingPathComponent(filename) - try FileManager.default.copyItem(at: builtInMolecule, to: destinationInDocuments) + if !FileManager.default.fileExists(atPath: destinationInDocuments.path) { + try FileManager.default.copyItem(at: builtInMolecule, to: destinationInDocuments) + } } } catch { print("Error copying built-in molecules: \(error)") diff --git a/Molecules/Rendering/MetalRenderView.swift b/Molecules/Rendering/MetalRenderView.swift index a855866..480e41e 100644 --- a/Molecules/Rendering/MetalRenderView.swift +++ b/Molecules/Rendering/MetalRenderView.swift @@ -97,6 +97,11 @@ class MetalRenderView: MTKView { autorotationDisplayLink.isPaused = false self.autoRotating = true } + + deinit { + autorotationDisplayLink.isPaused = true + autorotationDisplayLink.invalidate() + } } // MARK: - From bd99577a1a092d35843c534a8452cd93f2ea73c6 Mon Sep 17 00:00:00 2001 From: Brad Larson Date: Tue, 1 Aug 2023 23:37:28 -0500 Subject: [PATCH 8/9] Ambient occlusion added, but not working correctly. --- Molecules.xcodeproj/project.pbxproj | 8 + Molecules/Rendering/MetalRenderView.swift | 11 + .../Rendering/MetalRenderingDevice.swift | 71 ++-- Molecules/Rendering/MoleculeRenderer.swift | 313 +++++++++++++++++- .../Shaders/SphereAmbientOcclusion.metal | 113 +++++++ Molecules/Rendering/Shaders/SphereDepth.metal | 79 +++++ .../Rendering/Shaders/SphereRaytracing.metal | 70 +++- MoleculesTests/RenderingTests.swift | 15 + 8 files changed, 621 insertions(+), 59 deletions(-) create mode 100644 Molecules/Rendering/Shaders/SphereAmbientOcclusion.metal create mode 100644 Molecules/Rendering/Shaders/SphereDepth.metal diff --git a/Molecules.xcodeproj/project.pbxproj b/Molecules.xcodeproj/project.pbxproj index a83fb6c..21a9d7e 100644 --- a/Molecules.xcodeproj/project.pbxproj +++ b/Molecules.xcodeproj/project.pbxproj @@ -59,6 +59,8 @@ BC5F1FF12A5B63B800416000 /* MetalRenderingDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1FF02A5B63B800416000 /* MetalRenderingDevice.swift */; }; BC5F1FF32A5CD81400416000 /* RenderingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC5F1FF22A5CD81400416000 /* RenderingTests.swift */; }; BC8F86842A76ECF60038D748 /* CylinderRaytracing.metal in Sources */ = {isa = PBXBuildFile; fileRef = BC8F86832A76ECF60038D748 /* CylinderRaytracing.metal */; }; + BCCC8D862A7897990078D3A7 /* SphereAmbientOcclusion.metal in Sources */ = {isa = PBXBuildFile; fileRef = BCCC8D852A7897990078D3A7 /* SphereAmbientOcclusion.metal */; }; + BCCC8D882A79C54D0078D3A7 /* SphereDepth.metal in Sources */ = {isa = PBXBuildFile; fileRef = BCCC8D872A79C54D0078D3A7 /* SphereDepth.metal */; }; BCD090CD2A7098850034650F /* MoleculeRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCD090CC2A7098850034650F /* MoleculeRenderer.swift */; }; BCD090CF2A70A7960034650F /* Matrix.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCD090CE2A70A7960034650F /* Matrix.swift */; }; BCD090D12A748C860034650F /* MatrixTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCD090D02A748C860034650F /* MatrixTests.swift */; }; @@ -128,6 +130,8 @@ BC5F1FF02A5B63B800416000 /* MetalRenderingDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetalRenderingDevice.swift; sourceTree = ""; }; BC5F1FF22A5CD81400416000 /* RenderingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenderingTests.swift; sourceTree = ""; }; BC8F86832A76ECF60038D748 /* CylinderRaytracing.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = CylinderRaytracing.metal; sourceTree = ""; }; + BCCC8D852A7897990078D3A7 /* SphereAmbientOcclusion.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = SphereAmbientOcclusion.metal; sourceTree = ""; }; + BCCC8D872A79C54D0078D3A7 /* SphereDepth.metal */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.metal; path = SphereDepth.metal; sourceTree = ""; }; BCD090CC2A7098850034650F /* MoleculeRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoleculeRenderer.swift; sourceTree = ""; }; BCD090CE2A70A7960034650F /* Matrix.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Matrix.swift; sourceTree = ""; }; BCD090D02A748C860034650F /* MatrixTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MatrixTests.swift; sourceTree = ""; }; @@ -296,6 +300,8 @@ BC5F1FED2A5B421600416000 /* Shaders */ = { isa = PBXGroup; children = ( + BCCC8D872A79C54D0078D3A7 /* SphereDepth.metal */, + BCCC8D852A7897990078D3A7 /* SphereAmbientOcclusion.metal */, BC8F86832A76ECF60038D748 /* CylinderRaytracing.metal */, BC5F1FEE2A5B51AF00416000 /* SphereRaytracing.metal */, ); @@ -482,6 +488,7 @@ BC5F1FD52A5A4C5800416000 /* XYZFile.swift in Sources */, BCD090CF2A70A7960034650F /* Matrix.swift in Sources */, BC5F1FD22A5A4B5300416000 /* SDFFile.swift in Sources */, + BCCC8D882A79C54D0078D3A7 /* SphereDepth.metal in Sources */, BC5807072A56395400313289 /* MoleculeDisplayView.swift in Sources */, BC5F1F7D2A58EB6300416000 /* Bond.swift in Sources */, BC5F1F7A2A58EB5500416000 /* Atom.swift in Sources */, @@ -495,6 +502,7 @@ BCD090D32A76C4770034650F /* MoleculeRenderingOptions.swift in Sources */, BCD090CD2A7098850034650F /* MoleculeRenderer.swift in Sources */, BC5807052A56395400313289 /* MoleculesApp.swift in Sources */, + BCCC8D862A7897990078D3A7 /* SphereAmbientOcclusion.metal in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Molecules/Rendering/MetalRenderView.swift b/Molecules/Rendering/MetalRenderView.swift index 480e41e..31a8a1c 100644 --- a/Molecules/Rendering/MetalRenderView.swift +++ b/Molecules/Rendering/MetalRenderView.swift @@ -181,7 +181,18 @@ struct MetalView: UIViewRepresentable { func updateUIView(_ uiView: MetalRenderView, context: Context) { if (visualizationStyle != uiView.moleculeRenderer.visualizationStyle) && uiView.hasPresented { uiView.pauseRendering() + // Store and load previous render state. + let previousScale = uiView.moleculeRenderer.currentScale + var previousTranslation = uiView.moleculeRenderer.currentTranslation + var previousModelViewProjMatrix = uiView.moleculeRenderer.modelViewProjMatrix + uiView.moleculeRenderer = MoleculeRenderer(molecule: molecule, visualizationStyle: visualizationStyle) + + uiView.moleculeRenderer.currentScale = previousScale + uiView.moleculeRenderer.currentTranslation = previousTranslation + uiView.moleculeRenderer.modelViewProjMatrix = previousModelViewProjMatrix + + uiView.setNeedsDisplay() uiView.resumeRendering() } else { uiView.autoRotating = autorotate diff --git a/Molecules/Rendering/MetalRenderingDevice.swift b/Molecules/Rendering/MetalRenderingDevice.swift index 13aced1..5426178 100644 --- a/Molecules/Rendering/MetalRenderingDevice.swift +++ b/Molecules/Rendering/MetalRenderingDevice.swift @@ -8,10 +8,10 @@ class MetalRenderingDevice { let device: MTLDevice let commandQueue: MTLCommandQueue let shaderLibrary: MTLLibrary - let sphereRaytracingDescriptor: MTLRenderPipelineDescriptor let sphereRaytracingPipelineState: MTLRenderPipelineState - let cylinderRaytracingDescriptor: MTLRenderPipelineDescriptor let cylinderRaytracingPipelineState: MTLRenderPipelineState + let sphereDepthPipelineState: MTLRenderPipelineState + let sphereAmbientOcclusionPipelineState: MTLRenderPipelineState init() { // Configure the Metal device and command queue. @@ -28,54 +28,49 @@ class MetalRenderingDevice { self.shaderLibrary = try device.makeLibrary(URL: metalLibraryPath) } catch { - fatalError("Could not load library") + fatalError("Could not load library: \(error)") } - // Create the render pipeline state for the sphere raytracing shader. - guard let sphereVertexFunction = self.shaderLibrary.makeFunction(name: "sphereRaytracingVertex") else { - fatalError("Sphere raytracing: could not load vertex function sphereRaytracingVertex") - } - - guard let sphereFragmentFunction = self.shaderLibrary.makeFunction(name: "sphereRaytracingFragment") else { - fatalError("Sphere raytracing: could not load fragment function sphereRaytracingFragment") - } - - self.sphereRaytracingDescriptor = MTLRenderPipelineDescriptor() - self.sphereRaytracingDescriptor.depthAttachmentPixelFormat = .depth32Float - self.sphereRaytracingDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm - self.sphereRaytracingDescriptor.rasterSampleCount = 1 - self.sphereRaytracingDescriptor.vertexFunction = sphereVertexFunction - self.sphereRaytracingDescriptor.fragmentFunction = sphereFragmentFunction - do { - self.sphereRaytracingPipelineState = try self.device.makeRenderPipelineState(descriptor: self.sphereRaytracingDescriptor) + self.sphereRaytracingPipelineState = try self.shaderLibrary.pipelineState(device: self.device, vertex: "sphereRaytracingVertex", fragment: "sphereRaytracingFragment") + self.cylinderRaytracingPipelineState = try self.shaderLibrary.pipelineState(device: self.device, vertex: "cylinderRaytracingVertex", fragment: "cylinderRaytracingFragment") + self.sphereDepthPipelineState = try self.shaderLibrary.pipelineState(device: self.device, vertex: "sphereDepthVertex", fragment: "sphereDepthFragment", blendOperation: .min) + self.sphereAmbientOcclusionPipelineState = try self.shaderLibrary.pipelineState(device: self.device, vertex: "sphereAmbientOcclusionVertex", fragment: "sphereAmbientOcclusionFragment", enableDepth: false, blendOperation: .add) } catch { - // TODO: Examine the potential error cases here. - fatalError("Unable to create sphere raytracing render pipeline state with error: \(error)") + fatalError("Could not load shader function with error: \(error)") } + } +} - // Create the render pipeline state for the cylinder raytracing shader. - guard let cylinderVertexFunction = self.shaderLibrary.makeFunction(name: "cylinderRaytracingVertex") else { - fatalError("Cylinder raytracing: could not load vertex function cylinderRaytracingVertex") +extension MTLLibrary { + func pipelineState(device: MTLDevice, vertex: String, fragment: String, enableDepth: Bool = true, blendOperation: MTLBlendOperation? = nil) throws -> MTLRenderPipelineState { + guard let vertexFunction = self.makeFunction(name: vertex) else { + fatalError("Could not load vertex function \(vertex)") } - guard let cylinderFragmentFunction = self.shaderLibrary.makeFunction(name: "cylinderRaytracingFragment") else { - fatalError("Cylinder raytracing: could not load fragment function cylinderRaytracingFragment") + guard let fragmentFunction = self.makeFunction(name: fragment) else { + fatalError("Could not load fragment function \(fragment)") } - self.cylinderRaytracingDescriptor = MTLRenderPipelineDescriptor() - self.cylinderRaytracingDescriptor.depthAttachmentPixelFormat = .depth32Float - self.cylinderRaytracingDescriptor.colorAttachments[0].pixelFormat = .bgra8Unorm - self.cylinderRaytracingDescriptor.rasterSampleCount = 1 - self.cylinderRaytracingDescriptor.vertexFunction = cylinderVertexFunction - self.cylinderRaytracingDescriptor.fragmentFunction = cylinderFragmentFunction + let descriptor = MTLRenderPipelineDescriptor() + if enableDepth { + descriptor.depthAttachmentPixelFormat = .depth32Float + } - do { - self.cylinderRaytracingPipelineState = try self.device.makeRenderPipelineState(descriptor: self.cylinderRaytracingDescriptor) - } catch { - // TODO: Examine the potential error cases here. - fatalError("Unable to create cylinder raytracing render pipeline state with error: \(error)") + if let blendOperation = blendOperation { + descriptor.colorAttachments[0].isBlendingEnabled = true + descriptor.colorAttachments[0].rgbBlendOperation = blendOperation + descriptor.colorAttachments[0].alphaBlendOperation = blendOperation + descriptor.colorAttachments[0].sourceRGBBlendFactor = .one + descriptor.colorAttachments[0].sourceAlphaBlendFactor = .one + descriptor.colorAttachments[0].destinationRGBBlendFactor = .one + descriptor.colorAttachments[0].destinationAlphaBlendFactor = .one } + descriptor.colorAttachments[0].pixelFormat = .bgra8Unorm + descriptor.rasterSampleCount = 1 + descriptor.vertexFunction = vertexFunction + descriptor.fragmentFunction = fragmentFunction + return try device.makeRenderPipelineState(descriptor: descriptor) } } diff --git a/Molecules/Rendering/MoleculeRenderer.swift b/Molecules/Rendering/MoleculeRenderer.swift index a0916e3..9cb36a8 100644 --- a/Molecules/Rendering/MoleculeRenderer.swift +++ b/Molecules/Rendering/MoleculeRenderer.swift @@ -11,6 +11,7 @@ final class MoleculeRenderer { var sphereVertexBuffers: [Atom.Element: MTLBuffer] = [:] var sphereImpostorSpaceCoordinateBuffers: [Atom.Element: MTLBuffer] = [:] + var sphereAmbientOcclusionTextureOffsetBuffers: [Atom.Element: MTLBuffer] = [:] var sphereIndexBuffers: [Atom.Element: MTLBuffer] = [:] var sphereIndexBufferCounts: [Atom.Element: Int] = [:] @@ -20,6 +21,10 @@ final class MoleculeRenderer { var cylinderIndexBuffer: MTLBuffer? var cylinderIndexBufferCount: Int = 0 + let ambientOcclusionTextureWidth = 1024 + var sphereAmbientOcclusionTexture: MTLTexture! + var normalizedAOTexturePatchWidth: Float = 0.0 + let lowerScaleLimit: Float = -100.0 let upperScaleLimit: Float = 100.0 @@ -35,6 +40,7 @@ final class MoleculeRenderer { self.molecule = molecule self.visualizationStyle = visualizationStyle initializeMoleculeBuffers() + prepareAmbientOcclusionTexture() } func initializeMoleculeBuffers() { @@ -42,12 +48,17 @@ final class MoleculeRenderer { var vertices: [Float] = [] var indices: [UInt16] = [] var impostorSpaceCoordinates: [Float] = [] + var ambientOcclusionTextureOffsets: [Float] = [] var index: UInt16 = 0 } var sphereComponents: [Atom.Element: BufferComponents] = [:] let moleculeScaleFactor = molecule.overallScaleFactor let centerOfMass = molecule.centerOfMass + normalizedAOTexturePatchWidth = 1.0 / ceil(sqrt(Float(molecule.atoms.count))); + + var currentAmbientOcclusionTextureOffset: (Float, Float) = (normalizedAOTexturePatchWidth / 2.0, normalizedAOTexturePatchWidth / 2.0) + // Read all atoms, split into elements, populate individual atomic element buffers. for atom in molecule.atoms { let elementComponents: BufferComponents @@ -62,6 +73,9 @@ final class MoleculeRenderer { currentIndex: &elementComponents.index, indices: &elementComponents.indices, vertices: &elementComponents.vertices, + normalizedAOTexturePatchWidth: normalizedAOTexturePatchWidth, + currentAmbientOcclusionTextureOffset: ¤tAmbientOcclusionTextureOffset, + ambientOcclusionTextureOffsets: &elementComponents.ambientOcclusionTextureOffsets, impostorSpaceCoordinates: &elementComponents.impostorSpaceCoordinates) } @@ -87,6 +101,12 @@ final class MoleculeRenderer { options: [])! elementImpostorSpaceCoordinateBuffer.label = "Sphere impostor space buffer: \(element)" sphereImpostorSpaceCoordinateBuffers[element] = elementImpostorSpaceCoordinateBuffer + + let elementAmbientOcclusionTextureOffsetBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: elementComponents.ambientOcclusionTextureOffsets, + length: elementComponents.ambientOcclusionTextureOffsets.count * MemoryLayout.size, + options: [])! + elementAmbientOcclusionTextureOffsetBuffer.label = "Sphere ambient occlusion texture offset buffer: \(element)" + sphereAmbientOcclusionTextureOffsetBuffers[element] = elementAmbientOcclusionTextureOffsetBuffer } // If bonds are visible, populate all of them in a single buffer. @@ -131,7 +151,244 @@ final class MoleculeRenderer { } } + let ambientOcclusionSamplingAngles: [(Float, Float)] = [ + (0.0, 0.0), + (.pi / 2.0, 0.0), + (.pi, 0.0), + (3.0 * .pi / 2.0, 0.0), + (0.0, .pi / 2.0), + (0.0, 3.0 * .pi / 2.0), + + (.pi / 4.0, .pi / 4.0), + (3.0 * .pi / 4.0, .pi / 4.0), + (5.0 * .pi / 4.0, .pi / 4.0), + (7.0 * .pi / 4.0, .pi / 4.0), + + (.pi / 4.0, 7.0 * .pi / 4.0), + (3.0 * .pi / 4.0, 7.0 * .pi / 4.0), + (5.0 * .pi / 4.0, 7.0 * .pi / 4.0), + (7.0 * .pi / 4.0, 7.0 * .pi / 4.0), + + (.pi / 4.0, 0.0), + (3.0 * .pi / 4.0, 0.0), + (5.0 * .pi / 4.0, 0.0), + (7.0 * .pi / 4.0, 0.0), + + (0.0, .pi / 4.0), + (0.0, 3.0 * .pi / 4.0), + (0.0, 5.0 * .pi / 4.0), + (0.0, 7.0 * .pi / 4.0), + ] + + func prepareAmbientOcclusionTexture() { + // Initialize the ambient occlusion texture. + let textureDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .bgra8Unorm, + width: ambientOcclusionTextureWidth, + height: ambientOcclusionTextureWidth, + mipmapped: false) + textureDescriptor.usage = [.renderTarget, .shaderRead, .shaderWrite] + + guard let newTexture = sharedMetalRenderingDevice.device.makeTexture(descriptor: textureDescriptor) else { + fatalError("Could not create ambient occlusion texture of size: (\(ambientOcclusionTextureWidth), \(ambientOcclusionTextureWidth))") + } + sphereAmbientOcclusionTexture = newTexture + sphereAmbientOcclusionTexture.label = "Ambient occlustion texture" + + // TODO: Extend ambient occlusion to ball-and-stick rendering modes. + if visualizationStyle == .ballAndStick { + // Set the ambient lighting texture to all-white for uniform illumination. + if let commandBuffer = sharedMetalRenderingDevice.commandQueue.makeCommandBuffer() { + commandBuffer.clear(texture: sphereAmbientOcclusionTexture, with: MTLClearColorMake(1.0, 1.0, 1.0, 1.0)) + commandBuffer.commit() + } + + return + } + + guard let commandBuffer = sharedMetalRenderingDevice.commandQueue.makeCommandBuffer() else { return } + commandBuffer.clear(texture: sphereAmbientOcclusionTexture, with: MTLClearColorMake(0.0, 0.0, 0.0, 0.0)) + let sphereDepthTextureWidth = 1024 + + let intensityFactor: Float = 0.5 / Float(ambientOcclusionSamplingAngles.count) +// let intensityFactor: Float = 1.0 + for (theta, phi) in ambientOcclusionSamplingAngles { + var rotationMatrix = Matrix4x4.identity.rotated(angle: theta, x: 1.0, y: 0.0, z: 0.0) + rotationMatrix = rotationMatrix.rotated(angle: phi, x: 0.0, y: 1.0, z: 0.0) + + let depthTextureDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .bgra8Unorm, + width: sphereDepthTextureWidth, + height: sphereDepthTextureWidth, + mipmapped: false) + depthTextureDescriptor.usage = [.renderTarget, .shaderRead, .shaderWrite] + guard let depthTexture = sharedMetalRenderingDevice.device.makeTexture(descriptor: depthTextureDescriptor) else { + fatalError("Could not create depth texture of size: (\(sphereDepthTextureWidth), \(sphereDepthTextureWidth))") + } + depthTexture.label = "Depth texture" + + renderDepthTexture(targetModelViewProjMatrix: rotationMatrix, buffer: commandBuffer, depthTexture: depthTexture) + renderAmbientOcclusion(targetModelViewProjMatrix: rotationMatrix, buffer: commandBuffer, depthTexture: depthTexture, intensityFactor: intensityFactor) + + rotationMatrix = Matrix4x4.identity.rotated(angle: theta + (.pi / 8.0), x: 1.0, y: 0.0, z: 0.0) + rotationMatrix = rotationMatrix.rotated(angle: phi + (.pi / 8.0), x: 0.0, y: 1.0, z: 0.0) + + renderDepthTexture(targetModelViewProjMatrix: rotationMatrix, buffer: commandBuffer, depthTexture: depthTexture) + renderAmbientOcclusion(targetModelViewProjMatrix: rotationMatrix, buffer: commandBuffer, depthTexture: depthTexture, intensityFactor: intensityFactor) + } + + commandBuffer.commit() + } + + func renderDepthTexture(targetModelViewProjMatrix: Matrix4x4, buffer: MTLCommandBuffer, depthTexture: MTLTexture) { + let orthographicMatrix = orthographicMatrix(left: -1.0, right: 1.0, bottom: Float(-1.0 * 1024 / 1024), top: Float(1024 / 1024), near: -1.0, far: 1.0) + + let depthStencilDescriptor1 = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .depth32Float, + width: 1024, + height: 1024, + mipmapped: false) + depthStencilDescriptor1.storageMode = .private + depthStencilDescriptor1.usage = [.renderTarget, .shaderRead, .shaderWrite] + guard let depthStencil = sharedMetalRenderingDevice.device.makeTexture(descriptor: depthStencilDescriptor1) else { + fatalError("Could not create depth stencil texture)") + } + depthStencil.label = "Depth stencil" + + let renderPass = MTLRenderPassDescriptor() + renderPass.depthAttachment.texture = depthStencil + renderPass.colorAttachments[0].texture = depthTexture + renderPass.colorAttachments[0].clearColor = MTLClearColorMake(1, 1, 1, 1) + renderPass.colorAttachments[0].storeAction = .store + renderPass.colorAttachments[0].loadAction = .clear + + guard let renderEncoder = buffer.makeRenderCommandEncoder(descriptor: renderPass) else { + fatalError("Could not create render encoder") + } + renderEncoder.setFrontFacing(.counterClockwise) + renderEncoder.setRenderPipelineState(sharedMetalRenderingDevice.sphereDepthPipelineState) + + let depthStencilDescriptor = MTLDepthStencilDescriptor() + depthStencilDescriptor.depthCompareFunction = .lessEqual + depthStencilDescriptor.isDepthWriteEnabled = true + let depthStencilState = sharedMetalRenderingDevice.device.makeDepthStencilState(descriptor: depthStencilDescriptor) + renderEncoder.setDepthStencilState(depthStencilState) + + let atomRadiusScaleFactor: Float + switch visualizationStyle { + case .spacefilling: atomRadiusScaleFactor = 1.0 + case .ballAndStick: atomRadiusScaleFactor = 0.35 + } + + let moleculeScaleFactor = molecule.overallScaleFactor + + for element in Atom.Element.allCases { + guard let sphereIndexBuffer = sphereIndexBuffers[element], + let sphereIndexBufferCount = sphereIndexBufferCounts[element], + let sphereVertexBuffer = sphereVertexBuffers[element], + let sphereImpostorSpaceCoordinateBuffer = sphereImpostorSpaceCoordinateBuffers[element] else { + continue + } + + // Setting sphere vertex buffers. + renderEncoder.setVertexBuffer(sphereVertexBuffer, offset: 0, index: 0) + renderEncoder.setVertexBuffer(sphereImpostorSpaceCoordinateBuffer, offset: 0, index: 1) + let vertexUniforms = sphereDepthVertexUniforms(modelViewProjMatrix: targetModelViewProjMatrix, + orthographicMatrix: orthographicMatrix, + sphereRadius: element.vanderWaalsRadius * moleculeScaleFactor * atomRadiusScaleFactor, + translation: Coordinate(x: 0.0, y: 0.0, z: 0.0)) + let vertexUniformBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: vertexUniforms, + length: vertexUniforms.count * MemoryLayout.size, + options: [])! + renderEncoder.setVertexBuffer(vertexUniformBuffer, offset: 0, index: 3) + + // Setting sphere fragment buffers. + let fragmentUniforms = sphereDepthFragmentUniforms(inverseModelViewProjMatrix: targetModelViewProjMatrix.inverted(), + ambientOcclusionTextureWidth: normalizedAOTexturePatchWidth) + let fragmentUniformBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: fragmentUniforms, + length: fragmentUniforms.count * MemoryLayout.size, + options: [])! + renderEncoder.setFragmentBuffer(fragmentUniformBuffer, offset: 0, index: 1) + + // Draw all spheres for an element. + renderEncoder.drawIndexedPrimitives(type: .triangle, indexCount: sphereIndexBufferCount, indexType: .uint16, indexBuffer: sphereIndexBuffer, indexBufferOffset: 0) + } + renderEncoder.endEncoding() + } + + func renderAmbientOcclusion(targetModelViewProjMatrix: Matrix4x4, buffer: MTLCommandBuffer, depthTexture: MTLTexture, intensityFactor: Float) { + let orthographicMatrix = orthographicMatrix(left: -1.0, right: 1.0, bottom: Float(-1.0 * 1024 / 1024), top: Float(1024 / 1024), near: -1.0, far: 1.0) + + let renderPass = MTLRenderPassDescriptor() + renderPass.colorAttachments[0].texture = sphereAmbientOcclusionTexture + renderPass.colorAttachments[0].storeAction = .store + renderPass.colorAttachments[0].loadAction = .load + + guard let renderEncoder = buffer.makeRenderCommandEncoder(descriptor: renderPass) else { + fatalError("Could not create render encoder") + } + renderEncoder.setFrontFacing(.counterClockwise) + renderEncoder.setRenderPipelineState(sharedMetalRenderingDevice.sphereAmbientOcclusionPipelineState) + + let atomRadiusScaleFactor: Float + switch visualizationStyle { + case .spacefilling: atomRadiusScaleFactor = 1.0 + case .ballAndStick: atomRadiusScaleFactor = 0.35 + } + + let moleculeScaleFactor = molecule.overallScaleFactor + + for element in Atom.Element.allCases { + guard let sphereIndexBuffer = sphereIndexBuffers[element], + let sphereIndexBufferCount = sphereIndexBufferCounts[element], + let sphereVertexBuffer = sphereVertexBuffers[element], + let sphereImpostorSpaceCoordinateBuffer = sphereImpostorSpaceCoordinateBuffers[element], + let sphereAmbientOcclusionTextureOffsetBuffer = sphereAmbientOcclusionTextureOffsetBuffers[element] else { + continue + } + + // Setting sphere vertex buffers. + renderEncoder.setVertexBuffer(sphereVertexBuffer, offset: 0, index: 0) + renderEncoder.setVertexBuffer(sphereImpostorSpaceCoordinateBuffer, offset: 0, index: 1) + renderEncoder.setVertexBuffer(sphereAmbientOcclusionTextureOffsetBuffer, offset: 0, index: 2) + let vertexUniforms = sphereAmbientOcclusionVertexUniforms(modelViewProjMatrix: targetModelViewProjMatrix, + orthographicMatrix: orthographicMatrix, + sphereRadius: element.vanderWaalsRadius * moleculeScaleFactor * atomRadiusScaleFactor, + ambientOcclusionTexturePatchWidth: normalizedAOTexturePatchWidth) + let vertexUniformBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: vertexUniforms, + length: vertexUniforms.count * MemoryLayout.size, + options: [])! + renderEncoder.setVertexBuffer(vertexUniformBuffer, offset: 0, index: 3) + + // Setting sphere fragment buffers. + let fragmentUniforms = sphereAmbientOcclusionFragmentUniforms(modelViewProjMatrix: targetModelViewProjMatrix, + inverseModelViewProjMatrix: targetModelViewProjMatrix.inverted(), + intensityFactor: intensityFactor) + let fragmentUniformBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: fragmentUniforms, + length: fragmentUniforms.count * MemoryLayout.size, + options: [])! + renderEncoder.setFragmentBuffer(fragmentUniformBuffer, offset: 0, index: 1) + renderEncoder.setFragmentTexture(depthTexture, index: 0) + + // Draw all spheres for an element. + renderEncoder.drawIndexedPrimitives(type: .triangle, indexCount: sphereIndexBufferCount, indexType: .uint16, indexBuffer: sphereIndexBuffer, indexBufferOffset: 0) + } + renderEncoder.endEncoding() + + } + func renderMoleculeFrame(width: CGFloat, height: CGFloat, buffer: MTLCommandBuffer, renderPass: MTLRenderPassDescriptor) { + let sphereDepthTextureWidth = 1024 + let depthTextureDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .bgra8Unorm, + width: sphereDepthTextureWidth, + height: sphereDepthTextureWidth, + mipmapped: false) + depthTextureDescriptor.usage = [.renderTarget, .shaderRead, .shaderWrite] + guard let depthTexture = sharedMetalRenderingDevice.device.makeTexture(descriptor: depthTextureDescriptor) else { + fatalError("Could not create depth texture of size: (\(sphereDepthTextureWidth), \(sphereDepthTextureWidth))") + } + depthTexture.label = "Depth texture" +// renderDepthTexture(buffer: buffer, depthTexture: depthTexture) +// renderAmbientOcclusion(buffer: buffer, depthTexture: depthTexture, intensityFactor: 1.0) + + let orthographicMatrix = orthographicMatrix(left: -1.0, right: 1.0, bottom: Float(-1.0 * height / width), top: Float(height / width), near: -1.0, far: 1.0) renderPass.colorAttachments[0].clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 1.0) @@ -158,17 +415,20 @@ final class MoleculeRenderer { let moleculeScaleFactor = molecule.overallScaleFactor * currentScale + // TODO: Generalize iterating over all atoms for the multiple render pass variants. for element in Atom.Element.allCases { guard let sphereIndexBuffer = sphereIndexBuffers[element], let sphereIndexBufferCount = sphereIndexBufferCounts[element], let sphereVertexBuffer = sphereVertexBuffers[element], - let sphereImpostorSpaceCoordinateBuffer = sphereImpostorSpaceCoordinateBuffers[element] else { + let sphereImpostorSpaceCoordinateBuffer = sphereImpostorSpaceCoordinateBuffers[element], + let sphereAmbientOcclusionTextureOffsetBuffer = sphereAmbientOcclusionTextureOffsetBuffers[element] else { continue } // Setting sphere vertex buffers. renderEncoder.setVertexBuffer(sphereVertexBuffer, offset: 0, index: 0) renderEncoder.setVertexBuffer(sphereImpostorSpaceCoordinateBuffer, offset: 0, index: 1) + renderEncoder.setVertexBuffer(sphereAmbientOcclusionTextureOffsetBuffer, offset: 0, index: 2) let vertexUniforms = sphereVertexUniforms(modelViewProjMatrix: modelViewProjMatrix, orthographicMatrix: orthographicMatrix, sphereRadius: element.vanderWaalsRadius * moleculeScaleFactor * atomRadiusScaleFactor, @@ -180,11 +440,13 @@ final class MoleculeRenderer { // Setting sphere fragment buffers. let fragmentUniforms = sphereFragmentUniforms(sphereColor: element.color, - inverseModelViewProjMatrix: modelViewProjMatrix.inverted()) + inverseModelViewProjMatrix: modelViewProjMatrix.inverted(), + ambientOcclusionTextureWidth: normalizedAOTexturePatchWidth) let fragmentUniformBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: fragmentUniforms, length: fragmentUniforms.count * MemoryLayout.size, options: [])! renderEncoder.setFragmentBuffer(fragmentUniformBuffer, offset: 0, index: 1) + renderEncoder.setFragmentTexture(sphereAmbientOcclusionTexture, index: 0) // Draw all spheres for an element. renderEncoder.drawIndexedPrimitives(type: .triangle, indexCount: sphereIndexBufferCount, indexType: .uint16, indexBuffer: sphereIndexBuffer, indexBufferOffset: 0) @@ -201,9 +463,9 @@ final class MoleculeRenderer { renderEncoder.setVertexBuffer(cylinderDirectionBuffer, offset: 0, index: 1) renderEncoder.setVertexBuffer(cylinderImpostorSpaceCoordinateBuffer, offset: 0, index: 2) let vertexUniforms = cylinderVertexUniforms(modelViewProjMatrix: modelViewProjMatrix, - orthographicMatrix: orthographicMatrix, - cylinderRadius: bondRadius * moleculeScaleFactor * bondRadiusScaleFactor, - translation: currentTranslation) + orthographicMatrix: orthographicMatrix, + cylinderRadius: bondRadius * moleculeScaleFactor * bondRadiusScaleFactor, + translation: currentTranslation) let vertexUniformBuffer = sharedMetalRenderingDevice.device.makeBuffer(bytes: vertexUniforms, length: vertexUniforms.count * MemoryLayout.size, options: [])! @@ -230,8 +492,8 @@ final class MoleculeRenderer { return modelViewProjMatrix.toFloatArray() + orthographicMatrix.toFloatArray() + [sphereRadius, 0.0, 0.0, 0.0, translation.x, translation.y, translation.z, 0.0] } - func sphereFragmentUniforms(sphereColor: Atom.Element.Color, inverseModelViewProjMatrix: Matrix4x4) -> [Float] { - return [sphereColor.red, sphereColor.green, sphereColor.blue, 0.0] + inverseModelViewProjMatrix.toFloatArray() + func sphereFragmentUniforms(sphereColor: Atom.Element.Color, inverseModelViewProjMatrix: Matrix4x4, ambientOcclusionTextureWidth: Float) -> [Float] { + return [sphereColor.red, sphereColor.green, sphereColor.blue, 0.0] + inverseModelViewProjMatrix.toFloatArray() + [ambientOcclusionTextureWidth, 0.0, 0.0, 0.0] } func cylinderVertexUniforms(modelViewProjMatrix: Matrix4x4, orthographicMatrix: Matrix4x4, cylinderRadius: Float, translation: Coordinate) -> [Float] { @@ -241,6 +503,22 @@ final class MoleculeRenderer { func cylinderFragmentUniforms(cylinderColor: Atom.Element.Color, inverseModelViewProjMatrix: Matrix4x4) -> [Float] { return [cylinderColor.red, cylinderColor.green, cylinderColor.blue, 0.0] + inverseModelViewProjMatrix.toFloatArray() } + + func sphereDepthVertexUniforms(modelViewProjMatrix: Matrix4x4, orthographicMatrix: Matrix4x4, sphereRadius: Float, translation: Coordinate) -> [Float] { + return modelViewProjMatrix.toFloatArray() + orthographicMatrix.toFloatArray() + [sphereRadius, 0.0, 0.0, 0.0, translation.x, translation.y, translation.z, 0.0] + } + + func sphereDepthFragmentUniforms(inverseModelViewProjMatrix: Matrix4x4, ambientOcclusionTextureWidth: Float) -> [Float] { + return inverseModelViewProjMatrix.toFloatArray() + [ambientOcclusionTextureWidth, 0.0, 0.0, 0.0] + } + + func sphereAmbientOcclusionVertexUniforms(modelViewProjMatrix: Matrix4x4, orthographicMatrix: Matrix4x4, sphereRadius: Float, ambientOcclusionTexturePatchWidth: Float) -> [Float] { + return modelViewProjMatrix.toFloatArray() + orthographicMatrix.toFloatArray() + [sphereRadius] + [ambientOcclusionTexturePatchWidth, 0.0, 0.0] + } + + func sphereAmbientOcclusionFragmentUniforms(modelViewProjMatrix: Matrix4x4, inverseModelViewProjMatrix: Matrix4x4, intensityFactor: Float) -> [Float] { + return modelViewProjMatrix.toFloatArray() + inverseModelViewProjMatrix.toFloatArray() + [intensityFactor, 0.0, 0.0, 0.0] + } } // MARK: - @@ -273,6 +551,9 @@ func appendOctagonVertices( currentIndex: inout UInt16, indices: inout [UInt16], vertices: inout [Float], + normalizedAOTexturePatchWidth: Float, + currentAmbientOcclusionTextureOffset: inout (Float, Float), + ambientOcclusionTextureOffsets: inout [Float], impostorSpaceCoordinates: inout [Float] ) { let vertex = [-position.x, position.y, position.z] @@ -301,11 +582,24 @@ func appendOctagonVertices( currentIndex + 2, currentIndex + 3, currentIndex + 4, currentIndex, currentIndex + 2, currentIndex + 5, currentIndex + 1, currentIndex, currentIndex + 6, - currentIndex + 3, currentIndex + 1, currentIndex + 7 + currentIndex + 3, currentIndex + 1, currentIndex + 7, ] indices += octagonIndices currentIndex += 8 + + for _ in 0..<8 { + ambientOcclusionTextureOffsets.append(currentAmbientOcclusionTextureOffset.0) + ambientOcclusionTextureOffsets.append(currentAmbientOcclusionTextureOffset.1) + } + + var newOffsetInX = currentAmbientOcclusionTextureOffset.0 + normalizedAOTexturePatchWidth + var newOffsetInY = currentAmbientOcclusionTextureOffset.1 + if newOffsetInX > (1.0 - (normalizedAOTexturePatchWidth * 0.15)) { + newOffsetInX = normalizedAOTexturePatchWidth / 2.0 + newOffsetInY += normalizedAOTexturePatchWidth + } + currentAmbientOcclusionTextureOffset = (newOffsetInX, newOffsetInY) } func appendRectangularBondVertices( @@ -317,6 +611,9 @@ func appendRectangularBondVertices( directions: inout [Float], impostorSpaceCoordinates: inout [Float] ) { + // TODO: Switch to using 32-bit indexing to expand geometry size. + guard currentIndex < 65530 else { return } + // Add the start and end points twice, to be displaced in the vertex shader. let startVertex = [-start.x, start.y, start.z] vertices.append(contentsOf: startVertex) diff --git a/Molecules/Rendering/Shaders/SphereAmbientOcclusion.metal b/Molecules/Rendering/Shaders/SphereAmbientOcclusion.metal new file mode 100644 index 0000000..f019582 --- /dev/null +++ b/Molecules/Rendering/Shaders/SphereAmbientOcclusion.metal @@ -0,0 +1,113 @@ +#include +using namespace metal; + +constant float ambientOcclusionTextureWidth = 1024.0; + +typedef struct +{ + float4x4 modelViewProjMatrix; + float4x4 orthographicMatrix; + float sphereRadius; + float ambientOcclusionTexturePatchWidth; +} SphereAmbientOcclusionVertexUniform; + +typedef struct +{ + float4x4 modelViewProjMatrix; + float4x4 inverseModelViewProjMatrix; + float intensityFactor; +} SphereAmbientOcclusionFragmentUniform; + +struct SphereAmbientOcclusionVertexIO +{ + float4 position [[position]]; + float2 impostorSpaceCoordinate [[user(impostorSpaceCoordinate)]]; + float3 normalizedViewCoordinate [[user(normalizedViewCoordinate)]]; + float adjustedSphereRadius; + float3 adjustmentForOrthographicProjection [[user(adjustmentForOrthographicProjection)]]; + float depthAdjustmentForOrthographicProjection; +}; + +vertex SphereAmbientOcclusionVertexIO sphereAmbientOcclusionVertex(const device packed_float3 *position [[buffer(0)]], + const device packed_float2 *inputImpostorSpaceCoordinate [[buffer(1)]], + const device packed_float2 *ambientOcclusionTextureOffset [[buffer(2)]], + constant SphereAmbientOcclusionVertexUniform& uniform [[buffer(3)]], + uint vid [[vertex_id]]) +{ + SphereAmbientOcclusionVertexIO outputVertices; + + float4 transformedPosition = uniform.modelViewProjMatrix * float4(position[vid], 1.0); + // impostorSpaceCoordinate = inputImpostorSpaceCoordinate; + float2 adjustedImpostorSpaceCoordinate; + if (inputImpostorSpaceCoordinate[vid].x != 0.0) + { + adjustedImpostorSpaceCoordinate = sign(inputImpostorSpaceCoordinate[vid]); + } + else + { + adjustedImpostorSpaceCoordinate = float2(0.0, 0.0); + } + + + outputVertices.impostorSpaceCoordinate = adjustedImpostorSpaceCoordinate * (1.0 + 2.0 / (ambientOcclusionTextureWidth * uniform.ambientOcclusionTexturePatchWidth)); + + outputVertices.adjustedSphereRadius = uniform.sphereRadius; + + transformedPosition = transformedPosition * uniform.orthographicMatrix; + transformedPosition.z = (transformedPosition.z + 1.0) * 0.5; + + outputVertices.adjustmentForOrthographicProjection = (float4(0.5, 0.5, 0.5, 1.0) * uniform.orthographicMatrix).xyz; + + outputVertices.normalizedViewCoordinate = ((transformedPosition / 2.0) + 0.5).xyz; + + outputVertices.position = float4(ambientOcclusionTextureOffset[vid] * 2.0 - float2(1.0) + (uniform.ambientOcclusionTexturePatchWidth * adjustedImpostorSpaceCoordinate), 0.0, 1.0); + return outputVertices; +} + +constant float oneThird = 1.0 / 3.0; + +float depthFromEncodedColor(float4 encodedColor) +{ + return oneThird * (encodedColor.r + encodedColor.g + encodedColor.b); +} + +float3 coordinateFromTexturePosition(float2 texturePosition) +{ + float2 absoluteTexturePosition = abs(texturePosition); + float h = 1.0 - absoluteTexturePosition.x - absoluteTexturePosition.y; + + if (h >= 0.0) + { + return float3(texturePosition.x, texturePosition.y, h); + } + else + { + return float3(sign(texturePosition.x) * (1.0 - absoluteTexturePosition.y), sign(texturePosition.y) * (1.0 - absoluteTexturePosition.x), h); + } +} + +fragment half4 sphereAmbientOcclusionFragment(SphereAmbientOcclusionVertexIO fragmentInput [[stage_in]], + texture2d depthTexture [[texture(0)]], + constant SphereAmbientOcclusionFragmentUniform& uniform [[ buffer(1) ]]) +{ + float4 currentSphereSurfaceCoordinate = float4(coordinateFromTexturePosition(clamp(fragmentInput.impostorSpaceCoordinate, -1.0, 1.0)), 1.0); + + currentSphereSurfaceCoordinate = normalize(uniform.modelViewProjMatrix * currentSphereSurfaceCoordinate); + + float3 currentPositionCoordinate = fragmentInput.normalizedViewCoordinate + fragmentInput.adjustedSphereRadius * currentSphereSurfaceCoordinate.xyz * fragmentInput.adjustmentForOrthographicProjection; + + + constexpr sampler depthSampler; + currentPositionCoordinate.y = 1.0 - currentPositionCoordinate.y; + float previousDepthValue = depthFromEncodedColor(float4(depthTexture.sample(depthSampler, currentPositionCoordinate.xy))); + + return half4(1.0, 1.0, 1.0, 1.0); +// if ( (floor(currentPositionCoordinate.z * 765.0 - 5.0)) <= (ceil(previousDepthValue * 765.0)) ) +// { +// return half4(half3(uniform.intensityFactor), 1.0h); +// } +// else +// { +// return half4(0.0h, 0.0h, 0.0h, 1.0h); +// } +} diff --git a/Molecules/Rendering/Shaders/SphereDepth.metal b/Molecules/Rendering/Shaders/SphereDepth.metal new file mode 100644 index 0000000..4a8f4d4 --- /dev/null +++ b/Molecules/Rendering/Shaders/SphereDepth.metal @@ -0,0 +1,79 @@ +#include +using namespace metal; + +typedef struct +{ + float4x4 modelViewProjMatrix; + float4x4 orthographicMatrix; + float sphereRadius; + float3 translation; +} SphereDepthVertexUniform; + +typedef struct +{ + float4x4 inverseModelViewProjMatrix; + float ambientOcclusionTexturePatchWidth; +} SphereDepthFragmentUniform; + +struct SphereDepthVertexIO +{ + float4 position [[position]]; + float2 impostorSpaceCoordinate [[user(impostorSpaceCoordinate)]]; + float normalizedDepth [[user(normalizedDepth)]]; + float adjustedSphereRadius; +}; + +vertex SphereDepthVertexIO sphereDepthVertex(const device packed_float3 *position [[buffer(0)]], + const device packed_float2 *inputImpostorSpaceCoordinate [[buffer(1)]], + constant SphereDepthVertexUniform& uniform [[buffer(3)]], + uint vid [[vertex_id]]) +{ + SphereDepthVertexIO outputVertices; + + float4 transformedPosition = uniform.modelViewProjMatrix * (float4(position[vid], 1.0) + float4(uniform.translation, 0.0)); +// transformedPosition.z = transformedPosition.z + 10.0; // ? + + outputVertices.impostorSpaceCoordinate = inputImpostorSpaceCoordinate[vid]; + + transformedPosition.xy = transformedPosition.xy + inputImpostorSpaceCoordinate[vid].xy * float2(uniform.sphereRadius); + float4 transformedPosition2 = transformedPosition * uniform.orthographicMatrix; + transformedPosition2.z = (transformedPosition2.z + 1.0) * 0.5; + + float4 depthAdjustmentPoint = float4(0.0, 0.0, 0.25, 1.0) * uniform.orthographicMatrix; + float depthAdjustmentForOrthographicProjection = depthAdjustmentPoint.z / depthAdjustmentPoint.w; + outputVertices.adjustedSphereRadius = uniform.sphereRadius * depthAdjustmentForOrthographicProjection; + + outputVertices.normalizedDepth = (transformedPosition2.z / 2.0) + 0.5; + outputVertices.position = transformedPosition2; + return outputVertices; +} + +struct FragmentColorDepth { + half4 color [[color(0)]]; + float depth [[depth(any)]]; +}; + +constant float3 stepValues = float3(2.0, 1.0, 0.0); + +fragment FragmentColorDepth sphereDepthFragment(SphereDepthVertexIO fragmentInput [[stage_in]], + constant SphereDepthFragmentUniform& uniform [[ buffer(1) ]]) +{ + float distanceFromCenter = length(fragmentInput.impostorSpaceCoordinate); + distanceFromCenter = min(distanceFromCenter, 1.0); + float normalizedSphereDepth = sqrt(1.0 - distanceFromCenter * distanceFromCenter); + float alphaComponent = step(distanceFromCenter, 0.99); + + float currentDepthValue = fragmentInput.normalizedDepth - fragmentInput.adjustedSphereRadius * normalizedSphereDepth; + + // Inlined color encoding for the depth values + currentDepthValue = currentDepthValue * 3.0; + + float3 intDepthValue = float3(currentDepthValue) - stepValues; + + float3 temporaryColor = float3(1.0 - alphaComponent) + float3(alphaComponent) * intDepthValue; + + FragmentColorDepth colorDepth; + colorDepth.color = half4(half3(temporaryColor), 1.0h); + colorDepth.depth = float(currentDepthValue + (1.0 - alphaComponent)); + return colorDepth; +} diff --git a/Molecules/Rendering/Shaders/SphereRaytracing.metal b/Molecules/Rendering/Shaders/SphereRaytracing.metal index 0c160e4..e48727f 100644 --- a/Molecules/Rendering/Shaders/SphereRaytracing.metal +++ b/Molecules/Rendering/Shaders/SphereRaytracing.metal @@ -13,7 +13,7 @@ typedef struct { float3 sphereColor; float4x4 inverseModelViewProjMatrix; - // TODO: Ambient occlusion texture and parameters. + float ambientOcclusionTexturePatchWidth; } SphereRaytracingFragmentUniform; struct SphereRaytracingVertexIO @@ -21,18 +21,19 @@ struct SphereRaytracingVertexIO float4 position [[position]]; float2 impostorSpaceCoordinate [[user(impostorSpaceCoordinate)]]; float3 normalizedViewCoordinate [[user(normalizedViewCoordinate)]]; + float2 ambientOcclusionTextureBase [[user(ambientOcclusionTextureBase)]]; float adjustedSphereRadius; }; vertex SphereRaytracingVertexIO sphereRaytracingVertex(const device packed_float3 *position [[buffer(0)]], const device packed_float2 *inputImpostorSpaceCoordinate [[buffer(1)]], - //const device packed_float2 *ambientOcclusionTextureOffset [[buffer(2)]], + const device packed_float2 *ambientOcclusionTextureOffset [[buffer(2)]], constant SphereRaytracingVertexUniform& uniform [[buffer(3)]], uint vid [[vertex_id]]) { SphereRaytracingVertexIO outputVertices; -// ambientOcclusionTextureBase = ambientOcclusionTextureOffset; + outputVertices.ambientOcclusionTextureBase = ambientOcclusionTextureOffset[vid]; float4 transformedPosition = uniform.modelViewProjMatrix * (float4(position[vid], 1.0) + float4(uniform.translation, 0.0)); outputVertices.impostorSpaceCoordinate = inputImpostorSpaceCoordinate[vid]; @@ -57,9 +58,48 @@ struct FragmentColorDepth { constant half3 lightPosition = half3(0.312757, 0.248372, 0.916785); + +float2 ambientOcclusionLookupCoordinate(float2 impostorSpaceCoordinate, + float4x4 inverseModelViewProjMatrix, + float distanceFromCenter) +{ + float4 aoNormal; + + if (distanceFromCenter > 1.0) + { + distanceFromCenter = 1.0; + aoNormal = float4(normalize(impostorSpaceCoordinate), 0.0, 1.0); + } + else + { + float precalculatedDepth = sqrt(1.0 - distanceFromCenter * distanceFromCenter); + aoNormal = float4(impostorSpaceCoordinate, -precalculatedDepth, 1.0); + } + + // Ambient occlusion factor + aoNormal = inverseModelViewProjMatrix * aoNormal; + aoNormal.z = -aoNormal.z; + + float4 absoluteSphereSurfacePosition = abs(aoNormal); + float d = absoluteSphereSurfacePosition.x + absoluteSphereSurfacePosition.y + absoluteSphereSurfacePosition.z; + + float2 lookupTextureCoordinate; + if (aoNormal.z <= 0.0) + { + lookupTextureCoordinate = aoNormal.xy / d; + } + else + { + float2 theSign = aoNormal.xy / absoluteSphereSurfacePosition.xy; + //vec2 aSign = sign(aoNormal.xy); + lookupTextureCoordinate = theSign - absoluteSphereSurfacePosition.yx * (theSign / d); + } + + return (lookupTextureCoordinate / 2.0) + 0.5; +} + fragment FragmentColorDepth sphereRaytracingFragment(SphereRaytracingVertexIO fragmentInput [[stage_in]], -// texture2d ambientOcclusionTexture [[texture(0)]], -// texture2d precalculatedAOLookupTexture [[texture(1)]], + texture2d ambientOcclusionTexture [[texture(0)]], constant SphereRaytracingFragmentUniform& uniform [[ buffer(1) ]]) { half distanceFromCenter = length(fragmentInput.impostorSpaceCoordinate); @@ -69,14 +109,18 @@ fragment FragmentColorDepth sphereRaytracingFragment(SphereRaytracingVertexIO fr half currentDepthValue = fragmentInput.normalizedViewCoordinate.z - fragmentInput.adjustedSphereRadius * normalizedDepth; -// half2 lookupTextureCoordinate = ambientOcclusionLookupCoordinate(distanceFromCenter); -// -// lookupTextureCoordinate = (lookupTextureCoordinate * 2.0h) - 1.0h; -// -// half2 textureCoordinateForAOLookup = ambientOcclusionTextureBase + ambientOcclusionTexturePatchWidth * lookupTextureCoordinate; -// half ambientOcclusionIntensity = texture2D(ambientOcclusionTexture, textureCoordinateForAOLookup).r; - half ambientOcclusionIntensity = 1.0h; - + // Ambient occlusion sampling + float2 lookupTextureCoordinate = ambientOcclusionLookupCoordinate(fragmentInput.impostorSpaceCoordinate, + uniform.inverseModelViewProjMatrix, + distanceFromCenter); + + lookupTextureCoordinate = (lookupTextureCoordinate * 2.0) - 1.0; + + float2 textureCoordinateForAOLookup = fragmentInput.ambientOcclusionTextureBase + uniform.ambientOcclusionTexturePatchWidth * lookupTextureCoordinate; + textureCoordinateForAOLookup.y = 1.0 - textureCoordinateForAOLookup.y; + constexpr sampler ambientOcclusionSampler; + half ambientOcclusionIntensity = ambientOcclusionTexture.sample(ambientOcclusionSampler, textureCoordinateForAOLookup).x; + // Ambient lighting half3 normal = half3(fragmentInput.impostorSpaceCoordinate.x, fragmentInput.impostorSpaceCoordinate.y, normalizedDepth); half ambientLightingIntensityFactor = clamp(dot(lightPosition, normal), 0.0h, 1.0h); diff --git a/MoleculesTests/RenderingTests.swift b/MoleculesTests/RenderingTests.swift index c4f3efd..8369ca9 100644 --- a/MoleculesTests/RenderingTests.swift +++ b/MoleculesTests/RenderingTests.swift @@ -10,30 +10,45 @@ final class RenderingTests: XCTestCase { var impostorSpaceCoordinates: [Float] = [] let position = Coordinate(x: -1.0, y: 2.0, z: 3.0) + let normalizedAOTexturePatchWidth: Float = 0.25 + var currentAmbientOcclusionTextureOffset: (Float, Float) = (0.125, 0.125) + var ambientOcclusionTextureOffsets: [Float] = [] appendOctagonVertices( position: position, currentIndex: ¤tIndex, indices: &indices, vertices: &vertices, + normalizedAOTexturePatchWidth: normalizedAOTexturePatchWidth, + currentAmbientOcclusionTextureOffset: ¤tAmbientOcclusionTextureOffset, + ambientOcclusionTextureOffsets: &ambientOcclusionTextureOffsets, impostorSpaceCoordinates: &impostorSpaceCoordinates) XCTAssertEqual(currentIndex, 8) XCTAssertEqual(indices.count, 18) XCTAssertEqual(vertices.count, 24) XCTAssertEqual(impostorSpaceCoordinates.count, 16) + XCTAssertEqual(ambientOcclusionTextureOffsets.count, 16) + XCTAssertEqual(currentAmbientOcclusionTextureOffset.0, 0.375) + XCTAssertEqual(currentAmbientOcclusionTextureOffset.1, 0.125) appendOctagonVertices( position: position, currentIndex: ¤tIndex, indices: &indices, vertices: &vertices, + normalizedAOTexturePatchWidth: normalizedAOTexturePatchWidth, + currentAmbientOcclusionTextureOffset: ¤tAmbientOcclusionTextureOffset, + ambientOcclusionTextureOffsets: &ambientOcclusionTextureOffsets, impostorSpaceCoordinates: &impostorSpaceCoordinates) XCTAssertEqual(currentIndex, 16) XCTAssertEqual(indices.count, 36) XCTAssertEqual(vertices.count, 48) XCTAssertEqual(impostorSpaceCoordinates.count, 32) + XCTAssertEqual(ambientOcclusionTextureOffsets.count, 32) + XCTAssertEqual(currentAmbientOcclusionTextureOffset.0, 0.625) + XCTAssertEqual(currentAmbientOcclusionTextureOffset.1, 0.125) XCTAssertEqual(vertices[3], 1.0) XCTAssertEqual(vertices[4], 2.0) From 005ead8cbb51781ae846ff1f308b1f95cd6ec434 Mon Sep 17 00:00:00 2001 From: Brad Larson Date: Wed, 2 Aug 2023 12:36:36 -0500 Subject: [PATCH 9/9] Fixed ambient occlusion texture sampling, worked on some shading artifacts. --- Molecules/Rendering/MetalRenderView.swift | 2 - Molecules/Rendering/MoleculeRenderer.swift | 46 +++++++------------ .../Shaders/SphereAmbientOcclusion.metal | 17 ++++--- .../Rendering/Shaders/SphereRaytracing.metal | 13 +++--- 4 files changed, 30 insertions(+), 48 deletions(-) diff --git a/Molecules/Rendering/MetalRenderView.swift b/Molecules/Rendering/MetalRenderView.swift index 31a8a1c..e0ffaa5 100644 --- a/Molecules/Rendering/MetalRenderView.swift +++ b/Molecules/Rendering/MetalRenderView.swift @@ -66,11 +66,9 @@ class MetalRenderView: MTKView { override func draw(_ rect:CGRect) { guard let drawable = self.currentDrawable else { - print("No drawable") return } guard let moleculeRenderer = moleculeRenderer else { - print("No molecule renderer") return } if let commandBuffer = sharedMetalRenderingDevice.commandQueue.makeCommandBuffer(), diff --git a/Molecules/Rendering/MoleculeRenderer.swift b/Molecules/Rendering/MoleculeRenderer.swift index 9cb36a8..6d994b3 100644 --- a/Molecules/Rendering/MoleculeRenderer.swift +++ b/Molecules/Rendering/MoleculeRenderer.swift @@ -195,7 +195,7 @@ final class MoleculeRenderer { sphereAmbientOcclusionTexture.label = "Ambient occlustion texture" // TODO: Extend ambient occlusion to ball-and-stick rendering modes. - if visualizationStyle == .ballAndStick { + if (visualizationStyle == .ballAndStick) || (molecule.atoms.count < 100) { // Set the ambient lighting texture to all-white for uniform illumination. if let commandBuffer = sharedMetalRenderingDevice.commandQueue.makeCommandBuffer() { commandBuffer.clear(texture: sphereAmbientOcclusionTexture, with: MTLClearColorMake(1.0, 1.0, 1.0, 1.0)) @@ -206,25 +206,25 @@ final class MoleculeRenderer { } guard let commandBuffer = sharedMetalRenderingDevice.commandQueue.makeCommandBuffer() else { return } - commandBuffer.clear(texture: sphereAmbientOcclusionTexture, with: MTLClearColorMake(0.0, 0.0, 0.0, 0.0)) + commandBuffer.clear(texture: sphereAmbientOcclusionTexture, with: MTLClearColorMake(0.0, 0.0, 0.0, 1.0)) let sphereDepthTextureWidth = 1024 - let intensityFactor: Float = 0.5 / Float(ambientOcclusionSamplingAngles.count) -// let intensityFactor: Float = 1.0 + let depthTextureDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .bgra8Unorm, + width: sphereDepthTextureWidth, + height: sphereDepthTextureWidth, + mipmapped: false) + depthTextureDescriptor.usage = [.renderTarget, .shaderRead, .shaderWrite] + guard let depthTexture = sharedMetalRenderingDevice.device.makeTexture(descriptor: depthTextureDescriptor) else { + fatalError("Could not create depth texture of size: (\(sphereDepthTextureWidth), \(sphereDepthTextureWidth))") + } + depthTexture.label = "Depth texture" + + + let intensityFactor: Float = 2.0 / Float(ambientOcclusionSamplingAngles.count) for (theta, phi) in ambientOcclusionSamplingAngles { var rotationMatrix = Matrix4x4.identity.rotated(angle: theta, x: 1.0, y: 0.0, z: 0.0) rotationMatrix = rotationMatrix.rotated(angle: phi, x: 0.0, y: 1.0, z: 0.0) - let depthTextureDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .bgra8Unorm, - width: sphereDepthTextureWidth, - height: sphereDepthTextureWidth, - mipmapped: false) - depthTextureDescriptor.usage = [.renderTarget, .shaderRead, .shaderWrite] - guard let depthTexture = sharedMetalRenderingDevice.device.makeTexture(descriptor: depthTextureDescriptor) else { - fatalError("Could not create depth texture of size: (\(sphereDepthTextureWidth), \(sphereDepthTextureWidth))") - } - depthTexture.label = "Depth texture" - renderDepthTexture(targetModelViewProjMatrix: rotationMatrix, buffer: commandBuffer, depthTexture: depthTexture) renderAmbientOcclusion(targetModelViewProjMatrix: rotationMatrix, buffer: commandBuffer, depthTexture: depthTexture, intensityFactor: intensityFactor) @@ -239,7 +239,7 @@ final class MoleculeRenderer { } func renderDepthTexture(targetModelViewProjMatrix: Matrix4x4, buffer: MTLCommandBuffer, depthTexture: MTLTexture) { - let orthographicMatrix = orthographicMatrix(left: -1.0, right: 1.0, bottom: Float(-1.0 * 1024 / 1024), top: Float(1024 / 1024), near: -1.0, far: 1.0) + let orthographicMatrix = orthographicMatrix(left: -1.0, right: 1.0, bottom: -1.0, top: 1.0, near: -1.0, far: 1.0) let depthStencilDescriptor1 = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .depth32Float, width: 1024, @@ -314,7 +314,7 @@ final class MoleculeRenderer { } func renderAmbientOcclusion(targetModelViewProjMatrix: Matrix4x4, buffer: MTLCommandBuffer, depthTexture: MTLTexture, intensityFactor: Float) { - let orthographicMatrix = orthographicMatrix(left: -1.0, right: 1.0, bottom: Float(-1.0 * 1024 / 1024), top: Float(1024 / 1024), near: -1.0, far: 1.0) + let orthographicMatrix = orthographicMatrix(left: -1.0, right: 1.0, bottom: -1.0, top: 1.0, near: -1.0, far: 1.0) let renderPass = MTLRenderPassDescriptor() renderPass.colorAttachments[0].texture = sphereAmbientOcclusionTexture @@ -375,20 +375,6 @@ final class MoleculeRenderer { } func renderMoleculeFrame(width: CGFloat, height: CGFloat, buffer: MTLCommandBuffer, renderPass: MTLRenderPassDescriptor) { - let sphereDepthTextureWidth = 1024 - let depthTextureDescriptor = MTLTextureDescriptor.texture2DDescriptor(pixelFormat: .bgra8Unorm, - width: sphereDepthTextureWidth, - height: sphereDepthTextureWidth, - mipmapped: false) - depthTextureDescriptor.usage = [.renderTarget, .shaderRead, .shaderWrite] - guard let depthTexture = sharedMetalRenderingDevice.device.makeTexture(descriptor: depthTextureDescriptor) else { - fatalError("Could not create depth texture of size: (\(sphereDepthTextureWidth), \(sphereDepthTextureWidth))") - } - depthTexture.label = "Depth texture" -// renderDepthTexture(buffer: buffer, depthTexture: depthTexture) -// renderAmbientOcclusion(buffer: buffer, depthTexture: depthTexture, intensityFactor: 1.0) - - let orthographicMatrix = orthographicMatrix(left: -1.0, right: 1.0, bottom: Float(-1.0 * height / width), top: Float(height / width), near: -1.0, far: 1.0) renderPass.colorAttachments[0].clearColor = MTLClearColorMake(0.0, 0.0, 0.0, 1.0) diff --git a/Molecules/Rendering/Shaders/SphereAmbientOcclusion.metal b/Molecules/Rendering/Shaders/SphereAmbientOcclusion.metal index f019582..7ba4952 100644 --- a/Molecules/Rendering/Shaders/SphereAmbientOcclusion.metal +++ b/Molecules/Rendering/Shaders/SphereAmbientOcclusion.metal @@ -101,13 +101,12 @@ fragment half4 sphereAmbientOcclusionFragment(SphereAmbientOcclusionVertexIO fra currentPositionCoordinate.y = 1.0 - currentPositionCoordinate.y; float previousDepthValue = depthFromEncodedColor(float4(depthTexture.sample(depthSampler, currentPositionCoordinate.xy))); - return half4(1.0, 1.0, 1.0, 1.0); -// if ( (floor(currentPositionCoordinate.z * 765.0 - 5.0)) <= (ceil(previousDepthValue * 765.0)) ) -// { -// return half4(half3(uniform.intensityFactor), 1.0h); -// } -// else -// { -// return half4(0.0h, 0.0h, 0.0h, 1.0h); -// } + if ( (floor(currentPositionCoordinate.z * 765.0 - 5.0)) <= (ceil(previousDepthValue * 765.0)) ) + { + return half4(half3(uniform.intensityFactor), 1.0h); + } + else + { + return half4(0.0h, 0.0h, 0.0h, 1.0h); + } } diff --git a/Molecules/Rendering/Shaders/SphereRaytracing.metal b/Molecules/Rendering/Shaders/SphereRaytracing.metal index e48727f..50b2e2a 100644 --- a/Molecules/Rendering/Shaders/SphereRaytracing.metal +++ b/Molecules/Rendering/Shaders/SphereRaytracing.metal @@ -91,11 +91,11 @@ float2 ambientOcclusionLookupCoordinate(float2 impostorSpaceCoordinate, else { float2 theSign = aoNormal.xy / absoluteSphereSurfacePosition.xy; - //vec2 aSign = sign(aoNormal.xy); - lookupTextureCoordinate = theSign - absoluteSphereSurfacePosition.yx * (theSign / d); + lookupTextureCoordinate = theSign - absoluteSphereSurfacePosition.yx * (theSign / d); } - return (lookupTextureCoordinate / 2.0) + 0.5; + // Using a slight inset here to avoid seam artifacts, should examine this further to fix. + return lookupTextureCoordinate / 2.1; } fragment FragmentColorDepth sphereRaytracingFragment(SphereRaytracingVertexIO fragmentInput [[stage_in]], @@ -114,11 +114,10 @@ fragment FragmentColorDepth sphereRaytracingFragment(SphereRaytracingVertexIO fr uniform.inverseModelViewProjMatrix, distanceFromCenter); - lookupTextureCoordinate = (lookupTextureCoordinate * 2.0) - 1.0; - float2 textureCoordinateForAOLookup = fragmentInput.ambientOcclusionTextureBase + uniform.ambientOcclusionTexturePatchWidth * lookupTextureCoordinate; textureCoordinateForAOLookup.y = 1.0 - textureCoordinateForAOLookup.y; - constexpr sampler ambientOcclusionSampler; + constexpr sampler ambientOcclusionSampler(mag_filter::linear, + min_filter::linear); half ambientOcclusionIntensity = ambientOcclusionTexture.sample(ambientOcclusionSampler, textureCoordinateForAOLookup).x; // Ambient lighting @@ -130,7 +129,7 @@ fragment FragmentColorDepth sphereRaytracingFragment(SphereRaytracingVertexIO fr // Specular lighting half specularLightingIntensityFactor = pow(ambientLightingIntensityFactor, 60.0h) * 0.6h; - finalSphereColor = finalSphereColor + ((specularLightingIntensityFactor * ambientOcclusionIntensity) * (half3(1.0h) - finalSphereColor)); + finalSphereColor = finalSphereColor + ((specularLightingIntensityFactor * ambientOcclusionIntensity) * (half3(1.0h) - finalSphereColor)); FragmentColorDepth colorDepth; colorDepth.color = half4(finalSphereColor * alphaComponent, 1.0);