Uppsala universitet Institutionen för informatik och media

Modularitet och objektorientering

Byggandet av ett program som kan visa en molekyl

Lars Tedenborg

Examensarbete C Höstterminen 2010 2011-01-04 Abstract

The development of IT systems is usually accomplished by some form of system development methodology. It can be performed as the waterfall method, where each phase is completed before the next begins. One of the reasons to follow a development methodology is that the process is more structured, faster and that the product will have higher quality. One risk of not using a system development approach is that the code can be unstructured and difficult to maintain.

This paper describes an alternative method in which the development occurred without the use of any system development methodology. This has been possible because there was a clear goal of the program that should be developed. The goal was to read a molfile and from the information stored in the file plot the molecular structure. The appearance of the molecule has been fine-tuned as more features are added. A molfile is able to store all the information about the different characteristics a molecule can have. Not all molecules containing all the properties.

Development has proceeded as follows: Each property has been classified as an object and then implemented. The whole way from reading the property in the file into the computer program and then interpret the information to plot the property as a part of the molecule. The most important programming principle has been to develop the program as a number of more or less independent modules for the system to have high modularity.

2 Sammanfattning

Vid utveckling av IT-system brukar någon form av systemutvecklingsmetod följas. Det kan vara exempelvis vattenfallsmetoden där varje fas slutförs innan nästa påbörjas. Ett av skälen att följa en utvecklingsmetod är att arbetet ska bli mer strukturerat, gå fortare samt att produkten ska ha hög kvalité. En risk med att inte använda en systemutvecklingsmetod är att koden kan bli ostrukturerad och svår att underhålla.

Denna uppsats beskriver en alternativ metod där utvecklingen skett utan användandet av någon etablerad systemutvecklingsmetod. Det var möjligt då det fanns ett tydligt mål med programmet som skulle utvecklas. Målet var att läsa en molfil och från den information som finns lagrad i filen rita molekylärstrukturen. Utseendet på molekylen har finjusterats allteftersom fler egenskaper lagts till programmet. En molfil har möjlighet att lagra all information om de olika egenskaper en molekyl kan ha. Alla molekyler innehåller inte alla egenskaper.

Utvecklingen har gått till på följande sätt: Varje egenskap har klassats som ett objekt och sedan implementerats. Det innebär allt ifrån att läsa in egenskapen i filen till dataprogrammet och sedan tolka informationen till att rita ut den egenskapen. Den viktigaste programmeringsprincipen har varit att utveckla programmet som ett antal mer eller mindre fristående moduler för att systemet ska ha hög modularitet.

3

Innehållsförteckning Abstract ...... 2 Sammanfattning ...... 3 Innehållsförteckning ...... 4 Ordlista ...... 5 Inledning ...... 6 Problemformulering ...... 6 Syfte ...... 8 Bakgrund ...... 9 Avgränsning ...... 12 Hur en molfil är uppbyggd ...... 12 Metod ...... 15 Undersökningsstrategi ...... 15 Teori ...... 17 Utveckling av programmkod ...... 17 Analys och diskussion ...... 31 Slutsatser ...... 32 Källförteckning ...... 33 Elektroniska källor ...... 33 Bilaga 1 lista över program för att rita molekyler ...... 34 Bilaga 2 CTFilen från Symyx som beskriver hur en molfil är uppbyggd ...... 35 Bilaga 3 Komplett kod från programmet ...... 43

4

Ordlista Acyklisk En struktur som inte innehåller någon cykliskt element. Agil systemutveckling Metod för programmering, med korta utvecklingscykler med nära kontakt mellan utvecklare och beställare. http://www.agilemanifesto.org/iso/sv/ Cyklisk En struktur som innehåller ett cykliskt element. Deprotonerad Heteroatom som saknar ett väte och därmed har laddningen -1. Eter En syreatom som binder två olika kolatomer. Generisk struktur Struktur där vissa delar inte är fullständigt specificerade, utan de kan innehålla olika atomer eller fragment. Heteroatom Atom som inte är kol eller väte. Isotop Atomer av samma grundämne, alltså med samma antal protoner, men med olika antal neutroner. InChi filformat IUPAC standard för lagring av molekylär information. (http://www.iupac.org/inchi/) Kiral En atoms möjlighet att vara asymmetrisk. Bindningar från atomen kan peka åt olika håll i tre dimensioner. Konformation En molekyls utseende vid en viss tidpunkt vilket påverkas av molekylens rörlighet i dess bindningar. Masstal Summan av antalet protoner och neutroner. Molekyl När atomer sitter ihop i ett visst arrangemang tack vare bindningar. Molfil Ett filformat utvecklat av MDL som blivit en inofficiell standard för lagring av molekylär struktur information. Radikaler Atom som inte har tillräckligt med elektroner runt sig. Stereokemi Förtydligande av hur bindningarna pekar om en atom är kiral. SMILES fil format Simple Molecular Input Line Entry System. Filformat för att lagra molekylär struktur information. Varje molekyl lagras som textsträng.

5 Inledning

Contur AB var i behov av ett dataprogram vars uppgift var att rita en molekylstruktur från en lagrad molfil. Den utritade molekylen skulle visas upp som en bild. Redan vid projektets början sågs ett flertal problem med hur kraven för programmet skulle formuleras. Då det är en bild som ska visas upp finns det en estetisk parameter att ta hänsyn till under utvecklingen. Det är svårt att ge specifika krav beträffande hur alla molekylens egenskaper ska anges, när de olika delarnas utseende i en molekyl påverkar varandra. Därför utvecklades programmet utan någon form av föregående systemanalys, förutom ett begränsat antal funktionskrav. Här används begreppet systemanalys som benämning på den dokumenterade kravspecifikation som normalt föregår programmering. I molfilen finns alla molekylens egenskaper beskriva och utvecklingen gick till så att en egenskap i sänder implementerades.

Denna uppsats kommer att beskriva hur man kan bygga ett datorprogram genom att lägga till en liten funktion i taget, förutsatt att programmeraren kan bedöma resultatet. I detta fall har utvecklaren utbildning i kemi. Molekylen delas alltså upp i ett flertal moduler som en efter en lagts till i programmet. Detta har varit möjligt genom att strikt använda ett objektorienterat synsätt.

Problemformulering

Den mest rigida formen av systemutveckling är vattenfallsmodellen(Royce 1970). Denna metod är ett bra exempel på systemutveckling då varje fas är isolerad och dokumenteras innan nästa fas tar vid. Jag använder den som exempel för att förklara alla faser som ett systemutvecklingsprojekt normalt har. Varje fas slutar med ett beslut, ska projektet fortsätta eller avslutas. Vattenfallsmodellen består av följande faser:

1. Planering eller förstudie. Man kan också kalla denna fas behovsstudie. Den ska leverera argument för eller emot den fortsatta utvecklingen av IT-systemet.

2. Analysfas. Vad är det systemet ska utföra?

3. Design. Här utvecklas kod och arbetsprocesserna förändras för att passa det nya IT- systemet.

4. Testning. Blev produkten rätt?

5. Driftsättning. Systemet sätts i drift och allt levereras till användarna.

6. Underhåll. Lägga till nya funktioner. Fixa buggar. Förvalta systemet.

De största argumenten mot vattenfallsmetoden är risken för missförstånd mellan utvecklarna och beställarna samt att omvärlden utvecklas och förändras under tiden utvecklingen sker. Slutresultatet kan bli en produkt som beställaren inte längre är i behov av.

6 Alternativt till den strukturerade vattenfallsmetoden är Rapid Application Development (RAD) metoderna (Dennis et al 2006). Dessa metoder adresserar den långa utvecklingstiden innan en första fungerade produkt kan testas av användaren. Det uppnås genom att enbart utveckla ett fåtal funktioner i systemet från början, och i och med detta kan användarna tidigt se ett fungerade system och dess funktioner och hur det ska utvecklas vidare.

Det finns även en tredje typ av systemutvecklingsmetoder de Agila (Martin 2003). De är ännu mer flexibla än RAD metoderna. Grundsynsättet i dessa metoder är fokus på användaren av systemet. Utvecklingen sker i små steg med regelbundna leveranser av nyaste versionen sker till användaren sker under hela utvecklingen. Detta arbetssätt främjar förändrade krav och nya önskemål av användarna. Det agila sättet att utveckla mjukvara anser att människor och kommunikation är viktigare än formella dokument och att detta arbetssätt ska underlätta lösandet av problem under utvecklingsfasen. En annan central grundtanke är att den del av systemet som levererats till kunden faktiskt fungerar och därmed redan skapat ett värde för kunden.

Vid början av utvecklingen av molekylritarprogrammet såg jag tidigt problemet med att ställa en exakt kravspecifikation av hur molekylen skulle ritas ut. Det finns många egenskaper i en molekyl som måste ritas ut tydligt för betraktaren, så att strukturinformationen går att förstå. Eftersom det är en bild som ritas ut påverkar de olika delarna varandra. Exempelvis kräver en tjockare bindning en anpassning av typsnitt och storlek på tecknen. Skulle det gå att skapa en kravspecifikation när utseendet av en egenskap påverkar de övriga? Troligen möjligt, men den skulle behöva revideras under utvecklingen, om det inte gjordes en noggrann analys av alla möjligheter från början. Därför valde jag att utveckla programmet helt utan specifikation av hur programmet skulle rita ut de olika egenskaperna. Nedan visas hur olika storlek på atomsymboler och tjocklek på bindning ser ut. Vilket är bäst, eller snyggast är inte lätt att bestämma. Detta är bara ett exempel på hur utseendet av en sak påverkar en annan.

Figur 0. Olika sätt att visa en bindning

7 Syfte

Syftet med denna uppsats är att dokumentera ett sätt att utveckla programkod till färdigt program, genom att dela upp programmet i ett flertal moduler. Det finns många metoder att utveckla mjukvara. Jag väljer att inte specificera vilken typ som använts, mer än att säga att någon typ av Agil programmering (Martin 2003) använts. En av de viktigaste principerna i Agil systemutveckling är fokus på resultatet och det är precis så utvecklingen gick till.

En frågeställning som uppsatsen ska försöka besvara är:

Går det att utveckla mjukvara genom att tillämpa objektorienterad programmering utan att använda sig av systemutvecklingsverktygen?

Redan i inledningen nämndes att ett program byggts och vad det gör. Alltså är det kortfattade svaret på frågan ”ja det kan man göra”. Argument för detta påstående utvecklas i resterade delar av uppsatsen.

8 Bakgrund

Det finns ett flertal program som kan rita och visa molekylstrukturer. Dessa kan grovt delas in i två grupper. Den första gruppen är de som kan utföra molekylsimuleringar eller andra typer av beräkningar på den ritade molekylen. Dessa är oftast svårare för en lekman att använda, då det finns många olika typer av inställningar och parametrar att ta hänsyn till. Dessa har även möjlighet att visa molekyler i 3D. 3D genererade strukturer kräver någon form av speciell algoritm som kan beräkna den mest energisnåla konformationen, dessutom krävs att användaren kan bedöma om det är en rimlig struktur som presenteras. Denna funktion brukar inte finnas i de mer Office liknande produkterna, som är tänkta att användas av alla kemister som vill kunna rita en molekyl. Denna den andra gruppen är till utseende och funktion likt det vanliga enkla ritprogrammet som följer med alla datorer. Förutom möjligheten att rita enkla geometriska former kan olika molekylstrukturer, atomer och bindningar ritas, i vissa program finns även mallar för laboratorieutrustning och liknande bilder som används i rapporter eller presentationer. Ibland kan en enkel 3D generator finnas med som ger en ungefärlig 3D bild. Det är den senare typen av molekylritarprogram som hädanefter hänvisas till när ett program som kan rita molekyler nämns i texten. De som tas upp i tabellen nedan är de absolut vanligaste förekommande för PC. För en mer komplett lista se bilaga 1.

ChemSketch ACD Labs Freeware*

ChemDraw Cambridge Soft Inget gratis

ISISDraw/SymyxDraw Symyx Freeware* –Accelrys Draw

MarwinDraw Chemaxon Freeware*

* för privat bruk, eller utbildning

Tabell 1. De fyra absolut vanligaste molekylritprogrammen.

Alla dessa har stöd för att kopiera eller klistra mellan olika program, samt oftast olika plug-in program som kan köpas separat för att utföra olika typer av beräkningar eller för att få fullständig integrering med exempelvis Excel. Med denna funktion kan cellerna i Excel förstå molekylstrukturer och beräkningar direkt på strukturen kan utföras.

Det finns även ett stort antal mer eller mindre utvecklade open source program. De mest användarvänliga har sitt ursprung i (Open Babel). Open Babel är ett bra exempel på vad open source kan resultera i. Det är ett mer eller mindre komplett system av olika paket för att hantera och omvandla olika typer av kemi-relaterade data. Det är skrivet i Phyton och tänkt att köras på server. Med tiden har det tillkommit fler och fler GUI funktioner. Det mest utvecklade ritprogrammet baserad på Open Babel är Avagadro också det skrivet i Phyton. För att kunna använda dessa open source program i en .NET miljö har delar av .NET biblioteket översatts till Phyton. Det finns ännu så länge inga spridda open source molekylritarprogram skrivna i C#, vad jag kunnat hitta.

9 Företaget Contur Software har ett behov att ett program som kan visa upp en molekyl. De har två typer av kunder, de som har behov av att kunna rita molekyler, dessa kunder köper då licens på ett kommersiellt program och kan även använda det för att titta på redan ritade molekyler. Den andra typen av kunder är de som inte kommer att rita molekyler men som ibland behöver kunna titta på en tidigare ritad molekyl. Det är åt dessa kunder som programmet är tänkt att användas.

Det finns några olika sätt att rita molekyler. En struktur formel visar hur atomerna sitter ihop. Det mest korrekta sättet är att visa en molekyl i 3D, eftersom all molekyler har utbredning i alla tre riktningar. Dock kan det ofta vara svårt att se alla atomer och bindningar i större molekyler om de representeras i tre dimensioner. Jag har försökt att åskådliggöra detta med de tre bilderna nedan. Det är samma molekyl som vridigt tills det att de två kolatomerna tillslut överlappar varandra.

Figur 1. Etanol sett från olika vinklar

Alternativt kan man använda en tvådimensionell representation av strukturen. Det är det normala sättet att visa en molekyl. Här kan man välja att visa alla väten som förekommer i molekylen eller bara vissa utvalda. Skälet för detta är att dölja självklar information och göra det lättare för betraktaren att se molekylens viktiga element och göra det enklare att se helheten. Nackdelen är att betraktaren måste har mer kunskap om hur bilden ska tolkas. Exempel på detta ses i bilden nedan. Det är samma etanol molekyl som i 3D bilden ovan, men i mer och mer förkortad form. Det är D formen som är den vanligaste förekommande.

10

Figur 2. Molekyl A har alla väten explisivt utritade, molekyl B har alla väten utritade i en förkortad form, molekyl C har enbart terminala väten med samt vätet på heteroatomen, molekyl D har enbart vätet på heteroatomen.

Även cykliska strukturer kan ritas på några olika sätt, dessa två är de vanligaste. Molekylen heter bensen och är aromatisk. I fallet A har dubbelbindningarna valts att läggas inom den aromatiska strukturen. I fallet B ritas alla dubbelbindningar ut som en vanlig dubbelbindning. Här kan man argumentera för att representationen A ger en mer överskådlig bild av strukturen. Det syns direkt att det är en cyklisk struktur. Liknande argument kan användas på butadien som visas i C och D. Båda representationerna är lika korrekta men A och C är att föredra, av estetiska skäl.

Figur 3. Två olika representationer av dubbelbindningar.

Det som hittills redovisats av olika molekyl representationer är bara en liten del av alla egenskaper en molekyl kan ha. Det finns många olika typer av bindningar, det finns laddning, kiralitet, isotoper samt radikaler som alla kan representeras på olika sätt.

11 Avgränsning

Det fanns fyra krav på programmet. 1. Det ska visa upp en korrekt struktur på molekylen från molfilen. 2. Det ska inte ske något minnesläckage i programmet. 3. Programmet som fungera på .NET 2.0 biblioteket. 4. Det ska skrivas i C#.

Det finns en stor mängd olika filformat för att lagra information om molekylersstruktur. Varje mjukvaruföretag har valt att utveckla sitt eget format. Informationen som lagras i filen beskriver atomerna och bindningarna i molekylen. För att underlätta konvertering och kompabilitet mellan olika program har några filformat blivit standarder som de flesta program kan hantera. Ett av dessa av cml () som är ett XML baserat filformat. Ett annat är smiles (Simplified molecular input line entry specification) som använder ASCII strängar för att lagra information. Ett av de tidigaste molekylritprogrammen var MDLs ISISDraw som också, troligen på grund av att ta de var tidigt ute, blivit en inofficiell standard. MDL har efter ett flertal företagsuppköp kommit att ingå i Accelrys Inc. Alla molekylritprogram har inställningsmöjligheter för att få olika utseende på strukturen, ungefär på samma sätt som andra ritprogram. Jag har valt att inte implementera några inställningar annat än möjlighet att ändra storleken på molekylen.

En väldigt tydlig avgränsning i detta arbete är att programmet enbart läser molfiler av version V2000 som är den vanligaste förekommande.

Hur en molfil är uppbyggd

En molfil består av tre delar, fullständig dokumentation av hur en molfil är uppbyggd ses i bilaga 2. Först i molfilen kommer en rubrik del följt av anslutningstabell som innehåller information om atomerna och bindningarna och deras egenskaper. På slutet finns en sektion för mer komplex information.

Följande är ett exempel på en molfil av aminosyran alanin här angiven med dess summa formel som också är ett sätt att representera molekyler C3H7NO2. Summaformel används bara för att visa på typ och antal av atomer och kan inte ge en bild av hur atomerna sitter ihop eller utseendet på en molekyl. Bilden är inklippt för att visa alanins struktur.

12

Figur 5. Molfil skapad på alanin, öppnad i text editor, samt utritad struktur på molekylen.

Varje egenskap eller annan specifikation av molekylens struktur lagras med ett värde i molfilen. 0 betyder frånvaro av den egenskapen eller specifikationen. Värdet översätts sedan till den verkliga egenskapen av programmet som ritar ut molekylen. Nedan följer en förkortad förklaring av vad siffrorna betyder och hur de ska tolkas. Har inte tagit med alla typer av generiska sökfunktioner.

Rubrik Anger vilket program som skapade molfilen. I detta fallet MarwinDraw. Raden under antalet atomer, antalet bindningar följt av olika typer av listor om det är en generisk struktur som används för att söka relaterade strukturer. Denna rad slutar med versionen av molfil, i detta fall V2000 som är standard versionen. Det har nyligen kommit en V3000 som är bättre på att hantera stereokemi.

Atomblock Varje rad håller information om en atom. Först kommer x-, y-, z-koordinater följt av atomsymbolen. Efter det kommer massdifferens från naturliga isotopen, följt av laddning (där 1 = +3, 2 = +2, 3 = +1, 5 = -1, 6 = -2, 7 = -3). Nästa siffra anger om atomen är kiral. De följande egenskaperna ger möjlighet att utföra generiska struktur sökningar. När V2000 blev standard istället för den tidigare V1000 versionen har delar av atomblocket och bindningsblocket ersatts

13 av egenskapblocket. Därför används inte alla egenskaper längre. Av kompabilitets skäl finns dessa kvar i de senare versionerna.

Bindningsblock Varje rad utgör en bindning i molekylen. Först kommer information om vilka atomer bindningen består, från atom nummer och till atomnummer. Den tredje siffran anger bindningstypen (1 = enkelbindning, 2 = dubbelbindning, 3 = tripelbindning, 4 = aromatisk, samt många fler) . Det finns även här möjlighet att ange andra typer av bindningar för att utföra generiska sökningar. Nästa siffra anger stereokemi hos bindningen följt av fler generiska inställningsmöjligheter.

Egenskapblocket Här anges en egenskap per rad. Alltså samlas alla likadana egenskaper på en rad. Av kompabilitets skäl finns här fler parametrar än nödvändigt. Kommer bara att förklara de två som finns med i exemplet. M CHG 2 1 1….Läser från vänster till höger. M CHG Det en del av egenskap blocket som beskriver laddning (charge). 2 Det finns en laddad atom i molekylen. 1 Det är atom nummer 1 som har laddning 1 Laddningen på atomen översätts till +1.

På samma sätt kan nästa egenskaps rad läsas. M ISO 1 6 13 Det finns en isotop i molekylen och det är atom 6 som har vikten 13 . M END avslutar filen.

En avgränsning som gjordes direkt i programmeringen av att inte implementera alla egenskaper som en molfil kan innehålla. Syftet med programmet är att läsa diskreta substanser som saknar generiska element. Exakt vilka som skulle implementeras gick dock inte att bestämma innan ett test set av substanser ritats och respektive molfils innehåll jämförts med strukturen. Även manipulering av molfilerna öppnad i texteditor utfördes för att jämföra vilka egenskaper som är nödvändiga för att kunna visa upp en struktur på rätt sätt.

Följande egenskaper valdes; En atom ska kunna ha X och Y koordinater, atomsymbol, isotopmassa, laddning, radikal. En bindning ska visa upp vilken typ det är, hur dess stereokemi är, från vilken atom den kommer, till vilken atom den sträcker sig.

14 Metod

Undersökningsstrategi

För att analysera arbetet med utvecklingen av programmet har undersökningsstrategin Design och Skapade valts.(Oates 2006), jag har valt att översätta ”Design and Creation” till design och skapande. Design och skapande metoden används med fördel när utvecklingen bedrivs som en iterativ process enligt Oates. Med iterativ process menas att utvecklingen sker i många små steg där vissa steg upprepas för att förbättra dem. Oftast består också utvecklingen av en problemlösande del där design och skapade metoden används. Design och skapade processen används även när nya IT produkter framställs. IT produkter kan vara dataprogram eller studier av olika metoder att skapa datorprogram eller skapandet av av olika slag för att tydliggöra förlopp eller processer. Det kan även vara skapandet av definitioner av ord eller uttryck. Slutresultatet brukar vara en konstruktion, modell, metod, instansiering eller en blandning av dessa (March, Smith 1995).

• Konstruktion eller tankeskapelse: En exakt definition på ord eller objekt inom ett visst område. Detta är själva grunden för all IT utveckling, att alla som jobbar inom ett område menar samma sak när de använder ett visst ord eller uttryck. • Modell: Använder en kombination av olika konstruktioner för att beskriva en process eller flöde av information, för att tydliggöra en viss händelse eller företeelse och därmed underlätta problemlösning. • Metod eller handling: Ett förtydligande av modellen där mer exakt processen beskrivs. Det kan vara allt ifrån en exakt matematisk formel till detaljerade manualer över procedurer. • Instansiering: Ett fullt fungerade IT-baserat system som demonstrerar att konstruktioner, modeller och metoder kan implementeras.

Denna uppsats beskriver utveckling av mjukvara till färdigt program där utvecklingen bedrevs med hjälp av en massa iterativa processer vilket passar perfekt för design och skapande metoden. Därför valdes denna metod.

Design och skapande är en forskningsstrategi. Med forskningsstrategi menas att kunskapen inom ett visst ämne utökas. Det är alltså skillnad på att enbart skapa en IT-produkt och att öka kunskapen inom en IT-produkt. För att räknas som forskning måste utvecklingen innehålla analys, förklaringar, argumentation och kritisktgranskande. Det är svårt att ge exakta definitioner på dessa ord då deras betydelse delvis kommer att bero på vilket området som undersöks. Generellt sagt måste dock utvecklingen följa en iterativ process där utvecklaren hela tiden lär sig mer och mer under tiden projektet utvecklas. Denna iterativa process kan delas in i följande steg:

• Medvetenhet om att ett problem existerar. Det kan utvecklaren få genom att studera litteraturen eller genom att följa utvecklingen inom andra discipliner. • Förslag på en lösning av problemet. Här kommer det in en kreativ aspekt på arbetet. Utvecklaren måste uppfinna en ny lösning eller alternativt angreppssätt på problemet.

15 • Utvecklandet av en fungerade produkt. Det räcker inte med att föreslå en ny ide utvecklaren måste också utveckla en produkt av iden. Detta kan ske på många olika sätt beroende på vilken typ av produkt som utvecklas. Produkten måste vara tillräckligt välutvecklad för att kunna bevisa att förslaget på lösningen av problemet fungerar. • Utvärdering av produkten. När produkten är klar måste utvecklaren jämföra med andra produkter och kontrollera att produkten uppfyller de angivna önskemålen. • Slutsats. Här summeras resultatet och dokumenteras.

Figur 6. Iterativ process. En pil in som beskriver att processen börjar. Cykeln i mitten symboliserar den iterativa processen som upprepas tills resultatet uppnår de ställda kraven, pilen ut ur cykeln visar att resultatet tas till vara.

En nackdel med att använda design och skapade metoden för att beskriva arbetet i denna uppsats var att det inte gick att hitta exempel på forskning utförd med design och skapande metoden i litteraturen. Det blir svårt att ställa olika metoder mot varandra när det inte finns exempel på dess användning i litteraturen. Skälet till detta kan vara att Oates utvecklat denna metod själv och att den ännu inte fått fäste inom den akademiska världen. Det kan också vara så att den inte tillför något nytt till de redan etablerade metoderna och därför inte används. Dessutom är utvecklingen denna uppsats beskriver varken ren systemutveckling eller ren datavetenskap så det är svårt att hitta någon annan metod som bättre skulle passa att använda än design och skapade. Uppsatsen beskriver gränslandet mellan de båda verksamhetsområdena. Metoden har fungerat men det har varit till nackdel att det inte gått att hitta andra exempel där den använts.

16 Teori

Det är svårt att jämföra utveckling av mjukvara som utförs av en person med de etablerade systemutvecklingsmetoderna. Både vattenfallsmetoden och de olika RAD metoderna har båda analys och planeringsfaser och är tänkta att användas av grupper av utvecklare för att hjälpa till med koordinering av planering av utvecklingen. När en ensam utvecklare arbetar finns bara en version av produkten och alla funktioner och förändringar styrs av samma person. Därför är det svårt att ge någon jämförelse mellan detta mer hacker liknande sätt att programmera med den mer styrda och organiserade formen som en grupp av utvecklare måste hålla sig inom.

Jag har valt att kalla utvecklingen agil av det enda skälet att fokus hela tiden legat på produkten och att det ska vara korta utvecklingscykler.

Kodningen har bitvis varit svår. Inte för att det saknats modeller för hur programmet skulle se ut och inte heller för att det varit svårt att hitta en viss egenskap i molfilen. Däremot har det varit svårigheter i fasen att rita ut molekylen, för att varje egenskaps utseende har påverkat de övriga. Algoritmerna för att dra linjer eller symboler har ständigt förändrats för att den ritade bilden ska bli så lättläst och tydlig som möjligt.

Självklart hade det varit lättare att utgå från en färdigdesignad mall hur alla egenskaper och metoder skulle se ut, då hade det varit lättare att implementera alla egenskaper. En sådan design skulle dock kräva noggranna tester och jämförelser för att resultatet ska bli en så tydlig bild som möjligt.

Utveckling av programkod

I molfilen finns all information som behövs för att rita upp vilken molekyl som helst. Jag gjorde viss prioritering mellan de olika egenskaperna från början. De första egenskaperna jag valde var att läsa av atomsymbol och dess position. Skälet till detta var att det skulle det vara lätt att se om programmet fungerade eller inte. Om programmet fungerade skulle atomsymboler ritas ut på ritytan. Dessutom la jag in tre olika egenskaper från början, för att se om det fungerade att lagra informationen i en lista. På samma sätt har sedan egenskap för egenskap adderats i programkoden.

Programmet är skrivet i C# och med Visual Studio 2008.

Syftet med programmet jag skrev var att det skulle gå att använda i ett större system, därför valde jag att bygga det i två delar. Först själva molekylritarprogrammet kallat MoleculeRenderer, som tar argumentet en molfil och som returnerar en bild av molekylen. Dessutom ett Windows formulär program för att det under utvecklingen ska gå att enkelt välja en molfil och visa upp den ritade molekylen. Detta program anropar MoleculeRender programmet med argumentet en molfil. MoleculeRender returnerar en bild som visas upp på formuläret. Alla hänvisningar till molekylritarprogrammet är till det första MoleculeRenderer programmet.

17 Molekylritarprogrammet utför sin uppgift i fyra faser. Den första fasen läser in molfilen i programmet. Den andra fasen läser varje rad i molfilen och skapar en lista av atomer och en lista av bindningar. När detta är klart finns molekylen lagrad i programmet. Den tredje fasen ritar ut molekylen baserad på den information som finns lagrad. Slutligen den fjärde fasen returnerar bilden ut från programmet.

Det första som gjordes i programmet var att läsa in molfilen som innehåller en massa rader med text. Den enda publika metoden Render skapades som tar argumentet en sträng och returnerar en bild. Initialt lästes en rad i taget för att sedan gå in i fas två i programmet och skapa listor med atomer och bindningar. Detta upprepades tills alla raderna var lästa i molfilen. Exempel på kod där varje rad lästes in för en atom, den första fungerande versionen som kunde läsa in en molfil. Siffran numerOfAtoms lästes in först, denna information finns lagrad i molfilen och anger antalet atomer.

private void createAtoms(int numerOfAtoms) { StreamReader readFile = File.OpenText(molfileLocation); readFile.ReadLine(); readFile.ReadLine(); readFile.ReadLine(); readFile.ReadLine();

for (int i = 0; i < numerOfAtoms; i++) { string lineRead = readFile.ReadLine(); //läser en rad i taget

myAtom = new Atom(); myAtom.AtomCount = i + 1; myAtom.XCoordinate = double.Parse(lineRead.Substring(0, 10).Trim(), CultureInfo.InvariantCulture);

myAtom.YCoordiante = double.Parse(lineRead.Substring(10, 10).Trim(), CultureInfo.InvariantCulture); myAtom.AtomSymbol = lineRead.Substring(30, 3).Trim(); atoms[i] = myAtom; } readFile.Close(); }

Detta fungerade, men det blev otydligt vad programmet gjorde när både fas ett och fas två utfördes samtidigt för varje rad i programmet. Därför ändrades koden så hela molfilen läses in vid ett tillfälle och därefter klipps varje rad ut i en array enligt följande kod exempel. Där molfile är den inlästa molfilen som text sträng.

String [] lines = molfile.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);

Denna metod ligger i Controller klassen fick namnet MoleculeRenderer och hanterar de fyra faserna. För att hålla sig till strikt objektorienterad programmering skapades en ny klass Molecule som hanterar skapandet av molekylen som i programmet representeras av två listor med atomer och bindningar. För att kunna rita ut något valdes att först att lägga in X och Y koordinaterna

18 samt atomsymbolen i programmet. En molekyl har alltså då enbart en atomsymbol och position. En molekyl består av objekten atomer och bindningar, så det skapades två klasser Atom och Bond som beskriver de egenskaper dessa kan ha. En molekyl var nu klar att ritas ut, alltså tillbaka till MoleculeRenderer klassen och metoden Render. Först måste ett bild objekt skapas och på detta objekt ritas atomerna ut med metoden RenderMolecule. Delar av denna kod visas nedan.

Image myImage = new Bitmap(300, 300); Graphics paper = Graphics.FromImage(myImage); this.RenderMolecule(paper, mymolecule);

Metoden RenderMolecule tar den skapade molekylen och ritar ut atomerna för att se att alla fyra faserna fungerar. Här är den första fungerade kod som visar upp en molekyls atomer i bilden.

private void RenderMolecule(Graphics paper, Molecule mymolecule) { for (int i = 0; i < atoms.Length; i++) { Font atomFont = new Font("Arial", 8, FontStyle.Regular); SolidBrush symbolBrush = new SolidBrush(Color.Black);

paper.DrawString(atoms[i].AtomSymbol, atomFont, symbolBrush, (float)atoms[i].XCoordinate, (float) atoms[i].YCoordiante); } }

Det visade sig att molfilen har koordinaterna lagrade som kartesiksta koordinater medan C# har längst upp till vänster som (0,0). Därför behövdes en omvandlingsmetod för att få koordinaterna rätt. När detta var gjort valdes etan samt etylenglykol som test molekyler för att se om programmet fungerade. Nedan ses hur etan, molekyl A, ritades ut. Den enda information som fanns lagrad var X/Y-koordinat och atomsymbol. Molekyl B är hur etan ska ritas ut. Man kan se att atomerna kommer på rätt ställe och har rätt atomsymbol. Bindningar finns ännu inte med, inte heller möjlighet att visa väten på heteroatomer, vilket ses i etylenglykol exemplet molekyl C och D. Det viktiga här är att programmet fungerade i sin enkelhet.

Figur 7. De första molekylerna A och C som ritades ut med hjälp av programmet. A molekylen etan, ska ritas som B. Molekyl C etylenglykol, ska ritas som D.

När hela processen från att läsa in molfil till att visa upp molekylen som en bild fungerade började själva byggandet av den kompletta molekylen. Den fortsatta utvecklingen följde samma mönster. Först lästes en ny egenskap in i atom atomlistan för att sedan implementeras i ritamolekyl fasen.

19 Nedan visas den nästan kompletta metoden för att skapa en atom med alla dess egenskaper. Eftersom alla atomens egenskaper återfinns på samma ställe i varje rad går det att förutse vart en siffra ska plockas upp. Från början ställdes räknaren i till 2 för att hoppa över de två rubrik raderna, men för att göra koden tydligare räknas det numera från noll och två adderas till radens nummer vid varje programrad.

private void createAtoms(int numerOfAtoms, string[] molfilelines) { for (int i = 0; i < numerOfAtoms; i++) { Atom myatom = new Atom(); myatom.AtomCount = i + 1; // molfilen börjar med atom 1 och inte atom 0

myatom.XCoordinate = double.Parse(molfilelines[2 + i].Substring(0, 10).Trim(), CultureInfo.InvariantCulture);//hade problem med att omvandla med decimalpunkt, detta löste problemet

myatom.YCoordiante = double.Parse(molfilelines[2 + i].Substring(10, 10).Trim(), CultureInfo.InvariantCulture);

myatom.AtomSymbol = molfilelines[2 + i].Substring(30, 3).Trim();

myatom.Charge = Convert.ToInt32(molfilelines[2 + i].Substring(35, 3).Trim());

atoms[i] = myatom; }

Kod har utelämnats från den fullständiga metoden, nedan visas ett exempel på hur det ser ut för att lägga till laddning som finns representerad i egenskapblocket.

if (isCharged(molfilelines)) //om den är laddad gör detta { for (int i = 0; i < molfilelines.Length; i++) { if (molfilelines[i].Substring(0, 6) == "M CHG") { int numberOfChargedAtoms = int.Parse(molfilelines[i].Substring(6, 3).Trim());

for (int j = 0; j < numberOfChargedAtoms; j++) { int atomNumberOfChargedAtom = int.Parse(molfilelines[i].Substring(9 + j * 8, 4).Trim()); int chargeValue = Convert.ToInt32( molfilelines[i].Substring(13 + j * 8, 4).Trim() ); atoms[atomNumberOfChargedAtom - 1].Charge = chargeValue; //'atomNumber - 1' därför att molfilen börjar med atomnummer 1 och listan har första possitionen index 0. } } } }

20

Det har generellt varit lätt att lägga till molekylens egenskaper, när väl strukturen på molfilens innehåll var förstådd. Det har varit desto svårare att rita ut molekylens egenskaper.

Tidigt i utveckling noterades det att storleken på molekylen borde anpassas efter storleken på ritarean. En liten molekyl blev liten och en stor molekyl blev stor på den fixerade storleken på ritarean. En metod som räknar ut storleken på molekylen lades till och används för att skapa ritytan innan molekylen börjar rits ut. För att få bilden snyggare läggs molekylen en bit in på ritarean. Det finns även en skalningsfakor med som konstant i programmet för att enkelt kunna ändra storleken på molekylen. Denna skalningsfaktor finns med i alla delar av utritningsfasen som påverkas av storleken på molekylen, allt ifrån bindningstjocklek, typsnittets storlek till hur långt in på ritarean molekylen ska komma. Denna skalningsfaktor ligger med som konstant i klassen MoleculeRender.

En klass RenderAtoms skapades för att sköta all hantering av hur atomerna ska ritas ut.

När utvecklingen kommit till denna punkt fungerade programmet på detta sätt. MoleculeRender skapar en molekyl från molecule klassen. Molecule klassen innehåller en lista med atomer som är skapade från Atom klassen. När molekylen är skapad anropas AtomRender klassen som ritar ut molekylen. Eftersom alla instansieringar av klasserna ligger inne i olika metoder och instansieringarna skickas med som argument dör dessa när metoden är klar.

När bindningarna skulle läggas till i programmet valde jag att först visa dem som ett streck. Nedan visas hur resultatet av detta blev när etylenglykol ritades ut. Bindningen går in i atomsymbolen, vilket inte ser bra ut. Detta visar tydligt hur sammanvävda alla delar i den grafiska delen av programmet är. Det hade varit väldigt svår att utföra systemanalys och fått med den aspekten. Istället har varje problem löst när det uppkommit.

Figur 8. Atomer och bindningar tillsammans för första gången.

När molekylen ovan visades valde jag att istället lägga atomsymbolen i en vit ruta ovanpå bindningen. Delar av denna kod ses nedan. Resultatet av detta är att atomsymbolen inte nuddar bindningen.

//mäter storleken av atomsymbolen på ritobjektet i pixlar atomSymbolSize = paper.MeasureString(atomSymbol, atomFont);

//korrigerar för rektangels hörn i förhållande till X-koordinaten xCorrection = atomSymbolSize.Width / 2; yCorrection = atomSymbolSize.Height / 2;

21 //MeasureString metoden adderar extra marginal runt strängen, därför reduceras storleken med 0.9 atomRectangle = new RectangleF(Convert.ToInt32(mymolecule.Atoms[i].XCoordinate - xCorrection * 0.9), Convert.ToInt32(mymolecule.Atoms[i].YCoordiante) - yCorrection, atomSymbolSize.Width, atomSymbolSize.Height);

//ritar den vita rektangeln där atomsymbolen kommer att läggas ovanpå paper.FillRectangle(backgroundBrush, atomRectangle); //ritar atomsymbolen paper.DrawString(atomSymbol, atomFont, symbolBrush, atomRectangle);

En atom ska enbart visas om det är en heteroatom eller om kolatomen har laddning, är en isotop eller radikal. Därför börjar metoden för att rita ut alla atomer med följande if-sats.

if (atomSymbol != "C" || ( (atomSymbol == "C" && isotopeSymbol != "0") || (atomSymbol == "C" && chargeAsString != "0") || (atomSymbol == "C" && radicalType != 0) ) )

Vissa atomer brukar visas tillsammans med de väten som finns på atomen, exempelvis syre och kväve. För att lösa detta används ett Dictionary som innehåller alla de atomer som ska visas på detta sätt samt hur många bindningar dessa atomer har. Dessutom måste hänsyn tas till laddning eller övriga bindningar till denna atom. Ett syre har två bindningar. Är syret en alkohol ska ett väte synas, men sitter syret som en eter finns det inget väte. Samma gäller om alkoholen är deprotonerad och därmed har laddningen -1. För att lösa detta skapades en metod som returnerar antalet väten som ska visas upp, argumenten är den i programmet skapade molekylen samt index i en tidigare metod som ritar ut atom för atom. numberOfBondsDictionary är dictionaryt som håller reda på hur många bindningar olika heteroatomer har. Koden ses nedan. Bondlist är en lista som innehåller alla bindningar från en atom.

private int returnNumberOfHydrogenOnHeteroAtom(Molecule mymolecule, int index) { //ställer lokala variabler till noll från start int numberOfBonds = 0; int totalBonds = 0;

//plockar fram atomsymbolen för att göra koden lättare att läsa string atomSymbol = mymolecule.Atoms[index].AtomSymbol;

//loopar igenom bondlist för att få reda på totala antalet bindningar atomen har for (int i = 0; i < mymolecule.Atoms[index].BondsList.Count; i++) { //adderar antalet bindningar numberOfBonds += mymolecule.Atoms[index].BondsList[i].Type; }

//om atomen är laddad måste hänsyn tas

22 if (mymolecule.Atoms[index].Charge != 0) { //använder värdet då + innebär fler väten och - färre numberOfBonds -= mymolecule.Atoms[index].Charge; }

//läs hur många bindningar atomen kan ha totalBonds = numberOfBondsDictionary[atomSymbol];

//svaret är totalt möjliga – hur många det faktisk är return totalBonds - numberOfBonds; } }

I och med detta har en kortfattad beskrivning av utvecklingen av hur atomerna lästs in i programmet och hur de sedan ritats ut gjorts. En liknande beskrivning av utvecklingen av bindningarna följer.

En bindning har inte så många egenskaper. Den kan vara av en viss typ och den kan innehålla information om den är kiral och hur den i så fall pekar. Dessutom innehåller den information om mellan vilka två atomer den går, från atom och till atom. För at göra koden mer lättläslig har koordinaterna för från atom och till atom plockats ut ur listorna, enligt följande kod fragment.

for (int j = 0; j < bonds.Length; j++) { int fromX = Convert.ToInt32(atoms[bonds[j].From - 1].XCoordinate ); int fromY = Convert.ToInt32(atoms[bonds[j].From - 1].YCoordiante ); int toX = Convert.ToInt32(atoms[bonds[j].To - 1].XCoordinate ); int toY = Convert.ToInt32(atoms[bonds[j].To - 1].YCoordiante);

En switch sats har använts I stället för en massa if satser, allt för att göra koden lättare att följa. En enkelbindning består bara av ett streck mellan atomerna. Nedan visas ett kodfragment på en enkelbindning, där även delar av switch satsen kan ses.

switch (bonds[j].Type) { case 1: //Type = 1 => enkelbindning paper.DrawLine(bondPen, fromX, fromY, toX, toY); break;

En dubbelbindning skapade stora problem att rita ut. Först provades att ställa in bindningarna med tre if satser beroende på om bindningen var horisontell, vertikal eller sned. Resultatet av detta ses i bild nedan. Det är bensen som använts som exempel. Det syns att båda dubbelbindningarna har samma Y-värde när bindningen är horisontell och att de har samma X- värde när bindningen lutar. Det ser inte tillräckligt bra ut och en alternativ metod måste utvecklas, jämför med bilden till höger.

Figur 9. Dåligt sätt att rita dubbelbindningar.

23

Lite kod exempel på detta första försök att rita dubbelbindningar, som inte höll måttet.

case 2: //Type = 2 => dubbelbindning if (fromX == toX) //vertikal bindning { paper.DrawLine(bondPen, fromX + 2, fromY, toX + 2, toY); paper.DrawLine(bondPen, fromX - 2, fromY, toX - 2, toY); }

else if (fromY == toY) //horisontell bindning { paper.DrawLine(bondPen, fromX, fromY + 2, toX, toY + 2); paper.DrawLine(bondPen, fromX, fromY - 2, toX, toY - 2); }

else //diagonal bindning { paper.DrawLine(bondPen, fromX, fromY + 2, toX, toY + 2); paper.DrawLine(bondPen, fromX, fromY - 2, toX, toY - 2); } break;

Det gjordes även ett försök att ställa in längden på alla dubbelbindningar genom att ha en if sats för varje möjligt fall. Visar ett exempel av de 32 möjliga på denna kod.

if (fromX > toX && fromY > toY)

Det visade sig att det inte alls gick att göra på detta sätt. När alla tänkbara fall ritades ut fanns det väldigt många fall på hur en bindning kan peka. Och det blev ännu svårare. Vissa fall kräver att koordinaterna kan vara större eller mindre än den det jämförs med, medan andra kräver större eller lika med den det jämförs med. Resultatet blev att vissa fall av bindningar passade in i flera if- satser. En bindning har en början och ett slut, därför blev det dessutom dubbla fall på alla if satser. Visar nedan bild på alla fall som skulle omsättas i en större mängd if satser. Dessa 16 if satser blir sedan dubblerade av en liknande analys där bindningen pekar ut från atomen. Denna strategi övergavs efter att de 16 if-satserna skrivits och programmet fortfarande inte ritade rätt då vissa fall passerar flera if-satser. En alternativ strategi måste utvecklas.

24

Figur 10. Alla olika sätt en dubbelbindning av vara lagrad i molfilen.

Nu vändes hoppet till linjär algebra, att det skulle gå att generalisera en dubbelbindning som två parallell förflyttade linjer från samma punkt. Varje bindning hanteras som en vektor. Det behövdes en metod som flyttar ut linjen parallellt från atomen mot den andra atomen. Denna skulle även gå att använda när trippel bindningar eller zig-zag bindningar ska ritas, därför gjordes den generell under utvecklingen. Det blev två metoder beroende på vilken sida om atomen dubbelbindningen ska vara. Visar först metoden addTheSecondDubbleBond som returnerar en point array för en parallell förflyttad linje mellan de båda atomerna. Denna metod anropar metoderna calculateOrthogonalDistanceAtFromAtom och calculateOrthogonalDistanceAtToAtom. Dessa båda metoder räknar ut en vinkelrät punkt från atomen enligt följande bild.

25

Bild 1. Vektorn mellan punkt (X1, Y1) och (X2, Y”) Vektorn har lutningen (X2-X1)/(Y2-Y1). Den nya linjen parallellt till höger om linjen mellan punkterna har funktionen f(x) = X1 + (Y2 – Y1) och f(y) = Y1 – (X2 – X1).

private Point[] addTheSecondDubbleBondLine(int fromX, int fromY, int toX, int toY, Molecule mymolecule) {

Point[] theCoordinatesForTheDubbleBond = new Point[4];//I ordningen fromX1 fromX2, toY1, toY2

//arrayen som har punkterna för den nya linjen Point[] newToPoint = new Point[3]; Point[] newFromPoint = new Point[3];

newToPoint = calculateOrhtogonalDistancetAtToAtom(fromX, fromY, toX, toY, DISTANCEFROMTHEMIDDLEOFTHEBOND); newFromPoint = calculateOrthogonalDistanceAtFromAtom(fromX, fromY, toX, toY, DISTANCEFROMTHEMIDDLEOFTHEBOND);

theCoordinatesForTheDubbleBond[0] = newFromPoint[1]; theCoordinatesForTheDubbleBond[1] = newFromPoint[2]; theCoordinatesForTheDubbleBond[2] = newToPoint[1]; theCoordinatesForTheDubbleBond[3] = newToPoint[2];

return theCoordinatesForTheDubbleBond; }

private Point[] calculateOrthogonalDistanceAtFromAtom(int fromX, int fromY, int toX, int toY, int distanceToBondLine) //distanceToBondLine avser hur brett isär de båda linjerna kommer att hamna { Point[] array = new Point[3];

array[0].X = fromX; //toppen på triangeln som bildas X-koord array[0].Y = fromY; // toppen på triangeln som bildas Y-koord //förklaring (addera koord till var den pekar) + (korrektion för triangel toppen)

26 array[1].X = ((fromX) + Convert.ToInt32(toY - fromY) / distanceToBondLine); //ny P1X array[1].Y = ((fromY) - Convert.ToInt32(toX - fromX) / distanceToBondLine); //ny P1Y array[2].X = ((fromX) - Convert.ToInt32(toY - fromY) / distanceToBondLine); //ny P2X array[2].Y = ((fromY) + Convert.ToInt32(toX - fromX) / distanceToBondLine); //ny P2Y

return array;

}

Slutligen, koden inne i switch satsen i stället för de 32 if satserna. Det blev mycket enklare att läsa och överskådligare, men framför allt kommer alltid alla tänkbara fall av orienteringar av vinklar hos dubbelbindningarna att visas upp.

Point[] newCoordinates = addTheSecondDubbleBondLine(fromX, fromY, toX, toY, mymolecule); paper.DrawLine(bondPen, newCoordinates[0], newCoordinates[2]); paper.DrawLine(bondPen, newCoordinates[1], newCoordinates[3]);

Resultatet med bensen som exempel ses nedan. Har förstorat bilden så det lättare ses att dubbelbindningarna ritas vinkelräta i förhållande till bindningens riktning. Alla riktningar kommer att ritas ut och alla kommer att se likadana ut, ett stort framsteg sålunda. Har även med den tidigare ritade versionen från de tre if-satserna horisontell, vertikal eller övrig, samt typ exempel på hur der ser ut i andra ritprogram.

Figur 11. A molekyl ritad med linjäralgebra, B molekyl ritad med tre if-satser, C molekyl som den borde se ut.

Nu fanns med en metod som alltid kommer att visa upp alla dubbelbindningar med samma utseende. Det som kvarstår är att rita molekylen annorlunda om den är cyklist eller inte. Det som hittills utvecklats passar för acykliska strukturer. Det bör påpekas att alla med kemistutbildning ser att det är en absolut korrekt återgivning av bensen som molekyl A visar. Det finns inget fel i alla rita cykliska strukturer på detta sätt. Fram till utvecklingen av persondatorn ritades de flesta cykliska strukturer på detta sätt när tuschpennor och mallar användes. Dock är det numera vanligare att rita cykliska strukturer som kan ses i molekyl C. Där dubbelbindningen hamnar innanför ringen där alla yttre bindningar sitter ihop.

27

När utvecklingen nått hit lades den fortsatta utvecklingen av dubbelbindningar i cykliska system åt sidan. För att komma vidare måste en bindning veta om den ingår i en cykel samt att en atom i en dubbelbindning måste veta om dess bindningar pekar ut från cykeln eller om bindningen ingår i cykeln. Det var viktigare att se till att alla typer av bindningar kunde ritas ut. Detta finns kvar att utveckla.

Metoden för att parallell flytta en linje i en bindning hade gjorts med hänsyn tagen till att den skulle fungera även för andra typer av bindningar än dubbelbindningar. Det är möjligt tack vare det sista argumentet som avgör hur långt ifrån den ursprungliga linjen mellan atomerna som den nya kommer att hamna. När det var dags att lägga till trippelbindningar gick alltså att använda samma metoder som för dubbelbindningar men med annat värde på denna konstant, då trippelbindningar ligger närmare varandra. Koden har samma uppbyggnad som för dubbelbindningen med den skillnaden att även bindningen mellan de båda atomerna tas med. Visar inget kodexempel utan visar en molekyl direkt. Väl värt att notera är skillnader kring syre atomen där programmet ritat olika långa bindningar i jämförelse med den mindre molekylen.

Figur 12. Molekyl till vänster ritad av programmet och molekyl till höger ritat av kommersiell mjukvara

Det finns flera typer av bindningar vars utveckling inte tas upp här. Ett exempel på hur det kan se ut är bindningstypen enkelbindning eller dubbelbindning. Den representeras i programmet av en streckad linje, alltså väljs en annan penna för att rita ut denna typ. Bindningar till kirala atomer ritas ut på ett speciellt sätt. Det möjliggör för betraktaren att se om en bindning pekar ned eller upp ur planet. De representeras av en fylld triangel, (bindning pekar upp) eller en streckad triangel (bindning pekar ned). Bilden visar en molekyl med båda typerna av kirala bindningar. De båda likbenta trianglarna till höger visar att samma metod för att beräkna en punkt vinkelrät till vektorn mellan de båda atomerna bindningen består av, går att använda för att beräkna de två koordinaterna för de båda punkterna längst ned.

28

Figur 13. Två olika typer av kirala bindningar, samt de två trianglarna som används för att beräkna hur de ska ritas ut.

Det medför att koden för att rita de kirala bindningarna också blir väldigt enkel. Nedan visas koden för den fyllda triangeln, i fallet med den streckade körs en loop som ritar streck mellan de båda hörnpunkterna.

int lengthOfTheLines = 6; Point[] chiralBondPoints = calculateOrhtogonalDistancetAtToAtom(fromX, fromY, toX, toY, lengthOfTheLines); paper.FillPolygon(blackBrush, chiralBondPoints);

Andra delar av programmet som inte alls berörts är en klass för att hantera olika typer av fel. Det kastas olika typer av felmeddelanden beroende på vilken fas i programmet som inte fungerar. En effekt av att allt hänger ihop i den slutliga bilden är att det inte räckte med en skalningskonstant. En stor bindning kräver annat anstånd mellan dubbelbindningslinjerna än en liten. Därför har en extra avståndskonstant införts för att kunna trimma utseendet ytterligare. Alla klasser ligger också helt separerade och inga instansvariabler skickas mellan klasserna. När en metod skapar en ny klass eller anropar en annan metod skickas nödvändiga argument med för att inte lämna variabler kvar när programmet utfört en cykel. Tanken med detta är att arbetsminnet på datorn som använder programmet inte ska fyllas med överbliven information.

Nedan följer ett klassdiagram över alla klasser i det utvecklade ritprogrammet. Det finns inga hänvisningar till formulärklassen som använts under utvecklingen. Det som återstår av utvecklingen är att rita cykliska strukturer med homogen yttre bindning och eventuella dubbelbindningar innan för denna linje. I bilaga 3 finns komplett kod från alla klasserna. Denna kod innehåller delar av de funktioner som behövs för att rita cykliska strukturer rätt. Utvecklingen av ett färdigt system kommer att fortsätta efter examensarbetet är inlämnat.

29

Figur 14. Klassdiagram över programmet. Klassen MoleculeRender är controller klassen.

30 Analys och diskussion

Det går alltid att argumentera vad som är forskning och vad som är utveckling av ännu ett exempel på en IT-produkt. Definitionen av forskning är enligt Nationalencyklopedin

”process som genom systematiskt arbete kan frambringa nya kunskaper och ökat vetande.”

Det har argumenterats emot att utvecklingen av IT-system skulle vara forskning. Benbasat (Benbasat 1984) identifierar fallstudie, fältstudie, fältförsök, experiment utfört i laboratorier samt urvalsundersökning som forskningsmetoder. Inget exempel på forskningsmetod på utveckling av IT-system finns med. I en artikel av Gallier och Lands (Gallier och Lands 1897) argumenterar de emot traditionen att skriva vetenskapliga uppsatser på IT-systemutveckling baserad på forskningsmetodik utvecklad för att användas inom naturvetenskapens experimentella forskning. De refererar till flertal studier där forskarna delvis har gjort om forskningsmetoder för att passa utveckling av IT-system, vilket de hävdar omöjliggör jämförelse mellan olika publicerade resultat. Intressant nog ger de förslaget att studera IT-system på ett alternativt sätt till den stereotypa empiriska naturvetenskapliga metoden med teknikiskt fokus. Det som inte går att mäta går inte heller att undersöka med denna metod. Gallier och Land föreslår att forskning kring IT-system ska innehålla studier av beteenden och organisationer för att kunna förbättra kunskapen om IT- system. Detta kräver att forskaren är insatt i fler ämnen än de rent tekniska, samt att forskarvärlden accepterar forskning utförd mer nya metoder. Tyvärr kommer författarna inte med något förslag på ny forskningsmetod, men de öppnar för en diskussion om att det behövs ett alternativ.

Både kursboken (Oates 2006) och Nunamaker (Nunamaker, Chen och Purdin 1991) argumenterar för en alternativ forskningsmetod. Oates kallar den för design och skapande och Nunamaker ger inget namn men tanken är den samma i båda författarnas förslagna metoder I korthet kan deras alternativa metod beskrivas att ett problem identifieras följt av att forskaren presenteras en hypotes. Efter samlande av data och påföljande analys presenteras ett argument eller resultat. . Ett forskningsupplägg inom den experimentella delen av positivismens paradigm följer samma mönster som den metod de föreslår. Följer forskaren denna struktur är det alltså fullt acceptabelt att bedriva forskning inom systemutveckling. Där argumentet att utveckling av IT-system är forskning för att själva IT-produkten är beviset på att hypotesen stämmer. Det krävs inga experimentella data när själva produkten är bevis på att hypotesen stämde. Systemutvecklingsforskning kan alltså bedrivas när en IT-produkt produceras, vilken då räknas som en demonstration på att hypotesen fungerade.

31 Slutsatser

Det gick utmärkt att utveckla mjukvara utan att först göra en systemutveckling fas. En stor fördel är att mjukvaran skrevs och när ett problem dök upp löstes det och inte innan det fanns. Hade systemet analyserats innan kodningen började hade troligen tid lagts på att lösa sådant som inte skulle komma att användas eller på sådant som det visade sig skulle komma att kodas på annat sätt. Exempelvis har flera delar av koden skrivits om ett flertal gånger för att ursprungstanken inte visade sig att fungera. Detta går inte att förutse när utvecklaren är nybörjare på att skriva kod.

32 Källförteckning

Benbasat, I. (1984) An analysis of research methodologies. In The Information Systems Research Challenge, W.F. McFarlan, ed. Cambridge, MA: Harvard Business School Press, 47-85.

Dennis, A., Wixom, B. H. (2006) System Analysis Design. New Jersey, USA, John Wiley & Sons, Inc.

Galliers, R.D., Land, F.F. (1987) Choosing appropriate information systems research methodologies. Communications of the ACM, 30, 11, 900-902.

March, S., Smith, G. (1995) Design and natural science research on information technology, Design Support Systems, 15, 251-166.

Martin, R., C. (2003) Agile Software Development: Principles, Patterns and Practice. New Jersey, USA, Prentice Hall.

Nunamaker, J.F., Chen, M., Purdin T.D.M. (1991) System Development in Information Systems Research, Journal of Management Information Systems, 3(7), 89-106.

Oates, B. (2006) Researching Information Systems and Computing. London, United Kingdom: SAGE Publications Ltd.

Royce, W., W., (1970) Managing the Development of Large Software Systems: Concepts and Techniques, Proccedings of WESCON, agusti, pp. A1/1-A1/9.

Elektroniska källor Open Babel, http://openbabel.org/wiki/Main_Page, 2/1 2011.

33 Bilaga 1 lista över program för att rita molekyler

• ACD/ChemSketch: egenutvecklad, från ACD/Labs, finns både som kommersiell och som freeware version. Finns för Windows. • Ascalaph: egenutvecklad, finns både som kommersiell och som freeware version. Finns för Windows och . • ArgusLab: egenutvecklad, enbart freeware. Finns för Windows. • , open source. Baserad på Open Babel. • Ball View: open-source, (L)GPL, C++, Finns för Windows, Mac och Linux. • : open-source, EPL, + a GPL-exception, Java. Eclipse RPC baserad. • BKchem: open-source, GPL, Phyton, Phyton. Mestadels platform oberoende. • ChemDoodle: egenutvecklad from iChemLabs. Finns för Windows, Mac, och Linux. • ChemDraw: egenutvecklad from CambridgeSoft. Finns för Windows och Mac. • ICEDIT egenutvecklad from InfoChem. • ChemTool:gratis, skriven i C med hjälp av GTK Finns för och Linux. • ChemWindow egenutvecklad från Bio-Rad, del av KnowItAll mjukvarumiljön, freeware. • ISISDraw and Symyx Draw: egenutvecklad från MDL Information Systems och Symyx Technologies. • JChemPaint: open-source, (L)GPL, Java. • KnowItAll: egenutvecklad från Bio-Rad. freeware. • MarwinSketch: egenutvecklad från ChemAxon, finns både som kommersiell och som freeware version (Java Beans). • MarwinSpace: egenutvecklad från ChemAxon finns både som kommersiell och som freeware version. 3D (Java Beans). • Mobile Molecular DataSheet: egenutvecklad från Molecular Materials Informatics. BlackBerry smartphones. • molsKetch: open source, GPL, multiplatform, Qt4. • ODYSSEY, egenutvecklad från Wavefunction, Inc. Finns för Windows och Mac. • SketchEl: open source, GPL, Java. • Smormo-Ed: BSD license, C and GTK+, Finns för Windows och Linux. • SPARTAN, egenutvecklad från Wavefunction, Inc. Finns för Windows, Mac och Linux. • Str3Di32, egenutvecklad från Exorga, Inc. Finns för Windows. • XDrawChem: open-source software, (L)GPL, C++, bygger vidare på OpenBabel. Finns för Windowsv, Linux, Mac. • Zem: GPL, bygger vidare på OpenBabel. Finns för Windows, Linux och Mac.

34 Bilaga 2 CTFilen från Symyx som beskriver hur en molfil är uppbyggd Filen i sin helhet finns att ladda ned på Symyx hemsida efter det att man skapat ett konto. Det är enbart de delar som rör molfiler som finns med här i bilagan. Originaldokumentet heter CTFile Formats och skrevs 2007.

35

36

37 38

39

40

41

42 Bilaga 3 Komplett kod från programmet

Klassen Atom using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace MoleculeRenderer { ///

/// Atomclass, contains properties of the atoms /// internal class Atom : System.Object { //the private instance variables private int atomCount;//the numbering figure from the molfile private double xCoordinate; private double yCoordinate; private string atomSymbol; private int isotopeMass; private int charge; private int radical; private List bondslist = new List();

public static bool operator ==(Atom a, Atom b) { // If both are null, or both are same instance, return true. if (System.Object.ReferenceEquals(a, b)) { return true; }

// If one is null, but not both, return false. if (((object)a == null) || ((object)b == null)) { return false; }

// Return true if the fields match: return a.xCoordinate == b.xCoordinate && a.yCoordinate == b.yCoordinate; }

public static bool operator !=(Atom a, Atom b) { return !(a == b); }

public override bool Equals(object obj) { return base.Equals(obj); }

public override int GetHashCode() { return base.GetHashCode(); }

///

/// property - the list of bonds in the molecule

43 ///

public List BondsList { get { return this.bondslist; } set { this.bondslist = value; } }

///

/// property - the atomnumber identical the the number in the molfile /// public int AtomCount { get { return this.atomCount; } set { this.atomCount = value; } }

///

/// property - X-Coordinate of the atom /// public double XCoordinate { get { return this.xCoordinate; } set { this.xCoordinate = value; } }

///

/// property - Y-Coordinate of the atom /// public double YCoordiante { get { return this.yCoordinate; } set { this.yCoordinate = value; } }

///

/// property - the atomsymbol, the letter /// public string AtomSymbol

44 { get { return this.atomSymbol; } set { this.atomSymbol = value; } }

///

/// property - the isotopic mass of the atom /// public int IsotopeMass { get { return this.isotopeMass; } set { this.isotopeMass = value; } }

///

/// property - charge of the atom /// public int Charge { get { return this.charge; } set { this.charge = value; } }

///

/// property - radical type /// public int Radical { get { return this.radical; } set { this.radical = value; } }

} }

45 Klassen AtomRender using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Drawing; namespace MoleculeRenderer { ///

/// class that render all atoms of the molecule /// class AtomRender {

///

/// dictionary - to be used for look up number of bonds on heteroatoms /// Dictionary numberOfBondsDictionary;

///

/// constructor for the AtomRender class /// public AtomRender() { //used constructor to define the number of bonds to the heteroatoms //the heteroatoms with the number of bonds numberOfBondsDictionary = new Dictionary();

numberOfBondsDictionary.Add("B", 3); numberOfBondsDictionary.Add("Si", 4); numberOfBondsDictionary.Add("Ge", 4); numberOfBondsDictionary.Add("N", 3); numberOfBondsDictionary.Add("P", 3); numberOfBondsDictionary.Add("As", 3); numberOfBondsDictionary.Add("Sb", 3); numberOfBondsDictionary.Add("O", 2); numberOfBondsDictionary.Add("S", 2); numberOfBondsDictionary.Add("Se", 2); numberOfBondsDictionary.Add("Te", 2); numberOfBondsDictionary.Add("Po", 2);

}

///

/// method that draws the atoms on the paper /// /// the graphics objec /// the created molecule public void DrawTheAtoms(Graphics paper, Molecule mymolecule, int SCALINGFACTOR) {

Font atomFont = new Font(FontFamily.GenericSansSerif, SCALINGFACTOR / 3, FontStyle.Regular, GraphicsUnit.Pixel); Font isotopeFont = new Font(FontFamily.GenericSansSerif, SCALINGFACTOR / 5, FontStyle.Regular, GraphicsUnit.Pixel);

46 SolidBrush symbolBrush = new SolidBrush(Color.Black); SolidBrush backgroundBrush = new SolidBrush(Color.White);

//loop through the mylecule and draw each atom for (int i = 0; i < mymolecule.AtomList.Count; i++) {

//correction for the rectangle point of the atomSymbol as compared to the X/Y-Coordinate float xCorrection = 0; float yCorrection = 0;

float xCorrectionHeteroatomNumber = 0; float YCorrectionHeteroatomNumber = 0;

//pulls the Symbols from the atomlist string atomSymbol = mymolecule.AtomList[i].AtomSymbol; string isotopeSymbol = mymolecule.AtomList[i].IsotopeMass.ToString(); string chargeAsString = mymolecule.AtomList[i].Charge.ToString(); int radicalType = mymolecule.AtomList[i].Radical; string numberOfHydrogenOnHeteroatomAsString = ""; //number of hydrogen on heteroatom

//size of the text to be calculated SizeF atomSymbolSize = new SizeF();

//the background rectangle of the atomSymbol RectangleF atomRectangle = new RectangleF(); RectangleF isotopeRectangle = new RectangleF(); RectangleF chargeRectangle = new RectangleF();

// if atom not carbon show the atom or if the atom is carbon and contains charge or is isotope or is radical if (atomSymbol != "C" || ( (atomSymbol == "C" && isotopeSymbol != "0") || (atomSymbol == "C" && chargeAsString != "0") || (atomSymbol == "C" && radicalType != 0) ) ) {

//TODO kolla åt vilken sida bindningen går först //för att lägga alla rutor rätt i förhållande till bindningen //hantera de båda fallen olika helatiden.!! //

//only calculate number of hydrogen if the atom is a heteroatom and the heteroatom is shown with hydrogens if (atomSymbol != "C" && numberOfBondsDictionary.ContainsKey(atomSymbol)) { numberOfHydrogenOnHeteroatomAsString = returnNumberOfHydrogenOnHeteroAtom(mymolecule, i).ToString();

47

//if the bonds point to the RIGHT, add the hydrogen on the right side of the heteroatom if (mymolecule.AtomList[i].BondsList[0].FromAtom.XCoordinate <= mymolecule.AtomList[i].BondsList[0].ToAtom.XCoordinate) {

//add the hydrogens on the heteroatom if it should be any if (Convert.ToInt32(numberOfHydrogenOnHeteroatomAsString) >= 1) { if (Convert.ToInt32(numberOfHydrogenOnHeteroatomAsString) == 1) { atomSymbol += "H"; xCorrectionHeteroatomNumber = (float)mymolecule.AtomList[i].XCoordinate + xCorrection; }

//else if the heteroatom should have one than more hydrogen else { atomSymbol += "H" + numberOfHydrogenOnHeteroatomAsString; xCorrectionHeteroatomNumber = (float)mymolecule.AtomList[i].XCoordinate + xCorrection; } } }

//else the bond must point to the LEFT else { //add the hydrogens on the heteroatom if it should be any if (Convert.ToInt32(numberOfHydrogenOnHeteroatomAsString) >= 1) { //there is one hydrogen on the heteroatom if (Convert.ToInt32(numberOfHydrogenOnHeteroatomAsString) == 1) {

//add the hydrogen before the heteroatom atomSymbol = atomSymbol.Insert(0, "H"); xCorrectionHeteroatomNumber = (float)mymolecule.AtomList[i].XCoordinate - xCorrection; }

//there is more than one hydrogen else { //else add the hydrogens before the heteroatom atomSymbol = atomSymbol.Insert(0, "H" + numberOfHydrogenOnHeteroatomAsString); xCorrectionHeteroatomNumber = (float)mymolecule.AtomList[i].XCoordinate - xCorrection; } } }

48 //measures the size of the atomSymbol and the isotope symbol on the image object in pixels atomSymbolSize = paper.MeasureString(atomSymbol, atomFont);

//correction for the rectangle point of the atomSymbol, as compared to the X-coordinate xCorrection = atomSymbolSize.Width / 2; yCorrection = atomSymbolSize.Height / 2;

//**MeasureString method adds extra free space around the string therefore I multiply all xCorrections with 0.9 atomRectangle = new RectangleF(Convert.ToInt32(mymolecule.AtomList[i].XCoordinate - xCorrection * 0.9), Convert.ToInt32(mymolecule.AtomList[i].YCoordiante) - yCorrection, atomSymbolSize.Width, atomSymbolSize.Height);

//if the atom does not have the default mass value if (mymolecule.AtomList[i].IsotopeMass != 0) { //size of the isotope text SizeF isotopeSymbolSize = paper.MeasureString(mymolecule.AtomList[i].IsotopeMass.ToString(), isotopeFont);

//correction values to make the isotope box at the right place int xcoord = Convert.ToInt32(mymolecule.AtomList[i].XCoordinate - isotopeSymbolSize.Width - xCorrection * 0.9) + SCALINGFACTOR / 25; int ycoord = Convert.ToInt32(mymolecule.AtomList[i].YCoordiante - isotopeSymbolSize.Height);

//the rectangle in which the isotope value will be put isotopeRectangle = new RectangleF(xcoord, ycoord, isotopeSymbolSize.Width, isotopeSymbolSize.Height); }

//if the atom is charged show the charge value if (mymolecule.AtomList[i].Charge.ToString() != "0") { if (mymolecule.AtomList[i].Charge.ToString() != "4")//if the charge has value 4, the atom is a doublet radical { //add a + sign to the charge value if the charge is possitive if (mymolecule.AtomList[i].Charge > 0) { chargeAsString = "+" + mymolecule.AtomList[i].Charge.ToString(); }

else { chargeAsString = mymolecule.AtomList[i].Charge.ToString(); }

//measures the size i pixels of the charge string bulidning the white where to print the text SizeF chargeSymbolSize = paper.MeasureString(chargeAsString, isotopeFont);

//correction value to make the charge value white box at the right place-> at the top right side of the atomsmbol

49 int xcoord = Convert.ToInt32(mymolecule.AtomList[i].XCoordinate + xCorrection - SCALINGFACTOR / 25); int ycoord = Convert.ToInt32(mymolecule.AtomList[i].YCoordiante - chargeSymbolSize.Height);

chargeRectangle = new RectangleF(xcoord, ycoord, chargeSymbolSize.Width, chargeSymbolSize.Height); }

//to be sure add the radical information //the radical information might not be pressent in the properties block //only in the atom block else { mymolecule.AtomList[i].Radical = 2; } }

//if the atom is a radical show the radical dot representation if (mymolecule.AtomList[i].Radical.ToString() != "0") { //TODO, ger alltid radikal på samma plats //egentligen borde den ta hänsyn till bindningaran in mot atomen för att hitta en "ledig" plats //där den kan rita ut radikal typen. //alltså en rad ifatser som alla ger nya xCoordRad värden som används av draw metoden //TODO hela denna delen måste göras om med vektorer på fria platser kring atomen

SolidBrush radicalBrush = new SolidBrush(Color.Black); float xCoordRad = atomRectangle.X + atomRectangle.Width - 1; float yCoordRad = atomRectangle.Y + 5;

paper.FillRectangle(backgroundBrush, atomRectangle); //creates a transperante rectangle to draw the atomsymbol in paper.DrawString(mymolecule.AtomList[i].AtomSymbol, atomFont, symbolBrush, atomRectangle); //draws the atomsymbol in the white rectangle

//to draw the three different types of radicals if (mymolecule.AtomList[i].Radical == 1)//Radical type 1 => singlet-----two radical dots close { paper.FillEllipse(radicalBrush, xCoordRad, yCoordRad, 3, 3); paper.FillEllipse(radicalBrush, xCoordRad, yCoordRad + atomRectangle.Height / 3, 3, 3); }

if (mymolecule.AtomList[i].Radical == 2)//Radical type 2 => doublet-----one dot { paper.FillEllipse(radicalBrush, xCoordRad, yCoordRad, 3, 3);

}

if (mymolecule.AtomList[i].Radical == 3)//Radical type 3 => triplet-----two dots one on either side of the atomsymbol {

50 paper.FillEllipse(radicalBrush, xCoordRad, yCoordRad, 3, 3); paper.FillEllipse(radicalBrush, xCoordRad - (atomRectangle.Width + 2), yCoordRad, 3, 3); } }

//draw the background rectangle and the atomSymbol paper.FillRectangle(backgroundBrush, atomRectangle);//**creates a white rectangle to draw the atomsymbol paper.DrawString(atomSymbol, atomFont, symbolBrush, atomRectangle);//rectangle containing the atomsymbol

//TODO // kankse här att släppa in siffran på heteroatom //men är det ett väte inte siffra, bara om fler än ett //paper.DrawString(numberOfHydrogenOnHeteroAtom.ToString(), atomFont, symbolBrush, xCorrection, yCorrection+10);

//isotop if (isotopeSymbol != "0")//0 because when the int object is converted to string { paper.FillRectangle(backgroundBrush, isotopeRectangle);//a white rectangle under the isotope text paper.DrawString(isotopeSymbol, isotopeFont, symbolBrush, isotopeRectangle);//rectangle containing the isotopetext }

//charge if (chargeAsString != "0") { paper.FillRectangle(backgroundBrush, chargeRectangle);//background white rectangle paper.DrawString(chargeAsString, isotopeFont, symbolBrush, chargeRectangle);//charge text } }

} }

//to close the objects atomFont.Dispose(); isotopeFont.Dispose(); symbolBrush.Dispose(); backgroundBrush.Dispose(); }

///

/// calculates the number of hydrogens that a heteroatom should show /// /// /// index value from the render atom loop /// the number of hydrogens on the heteroatom private int returnNumberOfHydrogenOnHeteroAtom(Molecule mymolecule, int index) { int numberOfBonds = 0; int totalBonds = 0;//max number of bonds from a heteroatom

//the atomSymbol string atomSymbol = mymolecule.AtomList[index].AtomSymbol;

51

//loop through the bondslist to find total number of bonds to heteroatom for (int i = 0; i < mymolecule.AtomList[index].BondsList.Count; i++) { //adds the single/dubble/tripple bond as their coordination can take up more than one numberOfBonds += mymolecule.AtomList[index].BondsList[i].Type; }

//charge of the heteroatom is accounted for if (mymolecule.AtomList[index].Charge != 0) { //read charge value and use direct numberOfBonds -= mymolecule.AtomList[index].Charge; }

//reads number of bonds to the heteroatom from the dictionary totalBonds = numberOfBondsDictionary[atomSymbol];

//(total avail. coord. pos.) - (number of used coord. pos.) return totalBonds - numberOfBonds; }

}

}

Klassen Bond using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace MoleculeRenderer {

internal enum BondDirection { Right, Left }

///

/// Bond class contains, properties of the bonds /// internal class Bond : System.Object {

private int type; //singe, double, tripple, aromatic etc private int stereoType; //up, down, either, cis/trans private Atom fromAtom;//a bond starts at the fromAtom private Atom toAtom; //and ends at the toAtom

public static bool operator ==(Bond a, Bond b) { // If both are null, or both are same instance, return true. if (System.Object.ReferenceEquals(a, b)) { return true;

52 }

// If one is null, but not both, return false. if (((object)a == null) || ((object)b == null)) { return false; }

// Return true if the fields match: return a.fromAtom == b.fromAtom && a.toAtom == b.toAtom; }

public static bool operator !=(Bond a, Bond b) { return !(a == b); }

public override bool Equals(object obj) { return base.Equals(obj); } public override int GetHashCode() { return base.GetHashCode(); }

///

/// property - from which atom the bond starts /// public Atom FromAtom { get { return this.fromAtom; } set { this.fromAtom = value; } }

///

/// property - direction of the bond /// public BondDirection Direction { get { int iWeight = 0;

//Loop through the bonds in our fromAtom foreach (Bond b in fromAtom.BondsList) { if (b != this) { //Check if this bond points to the right or the left of us if (b.fromAtom == this.FromAtom) { //This is our atom. Dont look at it. }

53 else { //This is the interesting atom. Check if it is to the right of the other atom. //Then increment iWeight. Else decrement iWeight. if (b.fromAtom.XCoordinate > this.fromAtom.XCoordinate) { iWeight++; } else { iWeight--; } } } }

//Loop through the bonds in our toAtom foreach (Bond b in toAtom.BondsList) { if (b != this) { //Check if this bond points to the right or the left of us if (b.toAtom == this.toAtom) { //This is our atom. Dont look at it. } else { //This is the interesting atom. Check if it is to the right of the other atom. //Then increment iWeight. Else decrement iWeight. if (b.toAtom.XCoordinate > this.toAtom.XCoordinate) { iWeight++; } else { iWeight--; } } } }

if (iWeight >= 0) { return BondDirection.Right; } else { return BondDirection.Left; } } }

///

/// property - at which atom the bond ends /// public Atom ToAtom { get { return this.toAtom;

54 } set { this.toAtom = value; } }

///

/// property - type of bond /// single, double, tripple, aromatic etc /// public int Type { get { return this.type; } set { this.type = value; } }

///

/// property - stereotype of the bond /// public int StereoType { get { return this.stereoType; } set { this.stereoType = value; } } } }

Klassen BondRender using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Drawing; namespace MoleculeRenderer { ///

/// class that render all bonds of the molecule /// class BondRender { //moved scaling factors from MoleculeRender class //not to be changed in this class!!! private int SCALINGFACTOR; private int DISTANCEFROMTHEMIDDLEOFTHEBOND;

///

55 /// constructor of the BondRender class ///

public BondRender() {

}

///

/// method that draws the bonds on the paper /// /// the graphics object /// the created molecule public void DrawTheBonds(Graphics paper, Molecule mymolecule, int sCALINGFACTOR, int dISTANCEFROMTHEMIDDLEOFTHEBOND) { //takes the constants from MoleculeRender class and use them here SCALINGFACTOR = sCALINGFACTOR; DISTANCEFROMTHEMIDDLEOFTHEBOND = dISTANCEFROMTHEMIDDLEOFTHEBOND;

Pen bondPen = new Pen(Color.Black, SCALINGFACTOR / 40); SolidBrush blackBrush = new SolidBrush(Color.Black);

//loop through the molecules bonds array and draws all bonds for (int j = 0; j < mymolecule.BondList.Count; j++) { //pulls out the coordinates from atoms array to make the code clearer int fromX = Convert.ToInt32(mymolecule.AtomList[mymolecule.BondList[j].FromAtom.AtomCount - 1].XCoordinate); int fromY = Convert.ToInt32(mymolecule.AtomList[mymolecule.BondList[j].FromAtom.AtomCount - 1].YCoordiante); int toX = Convert.ToInt32(mymolecule.AtomList[mymolecule.BondList[j].ToAtom.AtomCount - 1].XCoordinate); int toY = Convert.ToInt32(mymolecule.AtomList[mymolecule.BondList[j].ToAtom.AtomCount - 1].YCoordiante);

if (mymolecule.BondList[j].StereoType == 0) //StereoType = 0 => not stereo center; draws a bond as a line or series of lines if the bond is not a chiral bond { //checks what type of bond it is and switches to the correct drawing representation of that bond switch (mymolecule.BondList[j].Type) { case 1: //Type = 1 => single paper.DrawLine(bondPen, fromX, fromY, toX, toY); break;

case 2: //Type = 2 => double bond

//Draws the dubblebonds in acyclic systems Point[] newCoordinates = addTheSecondDubbleBondLine(fromX, fromY, toX, toY, mymolecule); paper.DrawLine(bondPen, newCoordinates[0], newCoordinates[2]); paper.DrawLine(bondPen, newCoordinates[1], newCoordinates[3]);

56 var t = mymolecule.BondList[j].Direction.Equals("Left") ;

break;

case 3: //Type = 3 => tripple bond //**same idea as for the dubble bond

Point[] theCoordinatesForTheDubbleBond = new Point[4];//in the order fromX1 fromX2, toY1, toY2

//arrays containing the Points (from X1 X2 to x1 x2) of the tripplebond line Point[] newToPoint = new Point[3]; Point[] newFromPoint = new Point[3];

newToPoint = calculateOrhtogonalDistancetAtToAtom(fromX, fromY, toX, toY, DISTANCEFROMTHEMIDDLEOFTHEBOND/2); newFromPoint = calculateOrthogonalDistanceAtFromAtom(fromX, fromY, toX, toY, DISTANCEFROMTHEMIDDLEOFTHEBOND/2);

//draws the three bonds from the two new parallel lines and the original line paper.DrawLine(bondPen, newFromPoint[1], newToPoint[1]); paper.DrawLine(bondPen, fromX, fromY, toX, toY); paper.DrawLine(bondPen, newFromPoint[2], newToPoint[2]);

break;

case 4: //Type = 4 => aromatic see case 2 ------as for now goto case 2;

//Type = 5 => single or double Type = 6 => single or aromatic Type = 7 => double or aromatic Type 8 => any------to catch any other types of bonds for now------??????????????????? default: Pen alternativeBondPen = new Pen(Color.Black, 1); float[] dashValues = { 3, 2 }; alternativeBondPen.DashPattern = dashValues; paper.DrawLine(alternativeBondPen, fromX, fromY, toX, toY); break; } }

if (mymolecule.BondList[j].StereoType == 1) //StereoType = 1 => bond from atom is pointing Up. { int lengthOfTheLines = 6; Point[] chiralBondPoints = calculateOrhtogonalDistancetAtToAtom(fromX, fromY, toX, toY, lengthOfTheLines); paper.FillPolygon(blackBrush, chiralBondPoints); }

if (mymolecule.BondList[j].StereoType == 6) //StereoType = 6 => bond from atom is pointing Down. { Point[] shorterBondsArray = calculateShorterBondLength(fromX, fromY, toX, toY);

//used six steps as number of linjes to build the bond for (int i = 0; i < 6; i++) { int lengthOfTheLines = 6;

57 Point[] chiralBondPoints = calculateOrhtogonalDistancetAtToAtom(fromX, fromY, shorterBondsArray[i].X, shorterBondsArray[i].Y, lengthOfTheLines); //calculated the end points of the triangle to be used as coordinates in the drawing of the line paper.DrawLine(bondPen, chiralBondPoints[1].X, chiralBondPoints[1].Y, chiralBondPoints[2].X, chiralBondPoints[2].Y); }

}

if (mymolecule.BondList[j].StereoType == 4) //StereoType = 4 => single bond either up or down. {//draws a zig-zag bond Point[] shorterBondsArray = calculateShorterBondLength(fromX, fromY, toX, toY);

//used five steps as number of linjes to build the bond for (int i = 0; i < 5; i++) { int lengthOfTheLines = 4; Point pointFrom;//to make the code clearer Point pointTo;

//calculate chiralBondspoint metod returs two coordinates on either side of the bond. //to get the zig-zag pattern one point on the "right" side of the bond must be combinde with a "left" side point Point[] chiralBondPoints = calculateOrhtogonalDistancetAtToAtom(fromX, fromY, shorterBondsArray[i].X, shorterBondsArray[i].Y, lengthOfTheLines); //calculated the end points of the triangle to be used as coordinates in the drawing of the line Point[] chiralBondPointNextOne = calculateOrhtogonalDistancetAtToAtom(fromX, fromY, shorterBondsArray[i + 1].X, shorterBondsArray[i + 1].Y, lengthOfTheLines);

if (i % 2 == 0)//checks if i is an even or odd number, to make the zig.zag { pointFrom = chiralBondPoints[2]; pointTo = chiralBondPointNextOne[1];

if (i == 0) //so that the zig-zag ends at the end of the bond { pointFrom.X = toX; pointFrom.Y = toY; } }

else { pointFrom = chiralBondPoints[1]; pointTo = chiralBondPointNextOne[2]; }

paper.DrawLine(bondPen, pointFrom, pointTo); }

//this part is to make the last short zig-zag come back to where the bond starts Point[] lastPointArray; int lengthOfTheLinesLast = 4; lastPointArray = calculateOrhtogonalDistancetAtToAtom(fromX, fromY, shorterBondsArray[5].X, shorterBondsArray[5].Y, lengthOfTheLinesLast);

58 paper.DrawLine(bondPen, fromX, fromY, lastPointArray[1].X, lastPointArray[1].Y);

}

if (mymolecule.BondList[j].StereoType == 3) //StereoType = 3 => dubble bond either Cis or Trans. { //shows the eitherbond as two lines crossing //same idea as for the dubble bond

Point[] theCoordinatesForTheDubbleBond = new Point[4];//in the order fromX1 fromX2, toY1, toY2

//arrays containing the Points (from X1 X2 to x1 x2) of the tripplebond line Point[] newToPoint = new Point[3]; Point[] newFromPoint = new Point[3];

newToPoint = calculateOrhtogonalDistancetAtToAtom(fromX, fromY, toX, toY, DISTANCEFROMTHEMIDDLEOFTHEBOND); newFromPoint = calculateOrthogonalDistanceAtFromAtom(fromX, fromY, toX, toY, DISTANCEFROMTHEMIDDLEOFTHEBOND);

//draws the bond from the two new parallel lines and crosses them paper.DrawLine(bondPen, newFromPoint[1], newToPoint[2]); paper.DrawLine(bondPen, newFromPoint[2], newToPoint[1]); }

} //dispose the pens and brushes bondPen.Dispose(); blackBrush.Dispose(); }

///

/// metod to calculate the points of a perpendicular line as compared /// to the bond line. /// /// /// /// /// /// /// poins-array of coordinates [0]-fromCoord; [1]-leftCoord; [2]- rightCoord if looking along bond private Point[] calculateOrhtogonalDistancetAtToAtom(int fromX, int fromY, int toX, int toY, int distanceToBondLine) { //**divided the chiralbond in to two straight triangles with one katether as the bond length and the other as 1/6th of the bondlength //**used the straigt line eq. y= k*x + C => k = ( (ToY - FromY) / (ToX - FromX) ) to find the coeff. //**and the fact that two orthogonal lines has k1*k2 = -1 to find the perpendicular lines coeff. //**That gives that the "base triangle line" has the formyla y2 = - (ToX - FromX ) / ToY - FromY)*X + C2 //**C2 = ToY/ToX + ( (ToX-FromX)/(ToY-FromY) ) //**=> Y2 = -(ToX-FromX)/(ToY-FromY)*X + ( ToY/ToX + ( (ToX-FromX)/(ToY-FromY) )

59 //**with linear algebra the end of the triangle has the coord P1 (P1X, P1Y) = ( (ToX-FromX) + (ToY-FromY)/6 ), ( (ToY-FromY) + (ToX-FromX)/6 ) //**and the other triangle P2 (P2X, P2Y) = ( (ToX-FromX) - (ToY-FromY)/6 ), ( (ToY-FromY) - (ToX-FromX)/6 )

Point[] array = new Point[3];

array[0].X = fromX; //tip of bond triangle X-coord array[0].Y = fromY; //tip of bond triangle Y-coord //explanation (add the coord to where the bond point) + (correction for the triangle coordinate as explained at top) array[1].X = ((toX) + Convert.ToInt32(toY - fromY) / distanceToBondLine); //new P1X array[1].Y = ((toY) - Convert.ToInt32(toX - fromX) / distanceToBondLine); //new P1Y array[2].X = ((toX) - Convert.ToInt32(toY - fromY) / distanceToBondLine); //new P2X array[2].Y = ((toY) + Convert.ToInt32(toX - fromX) / distanceToBondLine); //new P2Y

return array;

}

///

/// metod to calculate the points of a perpendicular line as compared /// to the bond line. /// /// /// /// /// /// /// poins-array of coordinates [0]-fromCoord; [1]-leftCoord; [2]- rightCoord if looking along bond private Point[] calculateOrthogonalDistanceAtFromAtom(int fromX, int fromY, int toX, int toY, int distanceToBondLine) { //**divided the chiralbond in to two straight triangles with one katether as the bond length and the other as 1/6th of the bondlength //**used the straigt line eq. y= k*x + C => k = ( (ToY - FromY) / (ToX - FromX) ) to find the coeff. //**and the fact that two orthogonal lines has k1*k2 = -1 to find the perpendicular lines coeff. //**That gives that the "base triangle line" has the formyla y2 = - (ToX - FromX ) / ToY - FromY)*X + C2 //**C2 = ToY/ToX + ( (ToX-FromX)/(ToY-FromY) ) //**=> Y2 = -(ToX-FromX)/(ToY-FromY)*X + ( ToY/ToX + ( (ToX-FromX)/(ToY-FromY) ) //**with linear algebra the end of the triangle has the coord P1 (P1X, P1Y) = ( (ToX-FromX) + (ToY-FromY)/6 ), ( (ToY-FromY) + (ToX-FromX)/6 ) //**and the other triangle P2 (P2X, P2Y) = ( (ToX-FromX) - (ToY-FromY)/6 ), ( (ToY-FromY) - (ToX-FromX)/6 )

Point[] array = new Point[3];

array[0].X = fromX; //tip of bond triangle X-coord array[0].Y = fromY; //tip of bond triangle Y-coord //explanation (add the coord to where the bond point) + (correction for the triangle coordinate as explained at top) array[1].X = ((fromX) + Convert.ToInt32(toY - fromY) / distanceToBondLine); //new P1X

60 array[1].Y = ((fromY) - Convert.ToInt32(toX - fromX) / distanceToBondLine); //new P1Y array[2].X = ((fromX) - Convert.ToInt32(toY - fromY) / distanceToBondLine); //new P2X array[2].Y = ((fromY) + Convert.ToInt32(toX - fromX) / distanceToBondLine); //new P2Y

return array;

}

///

/// method that calculates a point array of new coordinate for 6 points on the bond /// /// starting X-Coord /// starting Y-Coord /// last X-Coord /// last Y-Coord /// a points array[6] with longest point first and then shorter and shorter length of the bond private Point[] calculateShorterBondLength(int fromX, int fromY, int toX, int toY) { //used linear algebra, to make the bond shorter by 1/6th for each time //same as for the method calculateChiralBondpoints // //the lines equation (X,Y) = (ToX-FromX, ToY-FromY) + t*(ToX-FromX,ToY-FromY) // or as each component; // x = FromX + t*(ToX-FromX) // y = FromY + t*(ToY-FromY) //To be sure the new coordinates are calculated on the same bondlength all coordinates are calculated at the same time

Point[] array = new Point[6];

//the maximum lenght of the bond's coordinates, before it starts to get shorter array[0].X = toX; array[0].Y = toY;

//loop to make the bond shorter for (int i = 1; i < 6; i++) { //explanation (the coord where the bond start) + (correction for the triangle coordinate as explained at top) array[i].X = ((toX) - i * Convert.ToInt32(toX - fromX) * 1 / 6); array[i].Y = ((toY) - i * Convert.ToInt32(toY - fromY) * 1 / 6); }

return array; }

///

/// method that calculates the coordinates for the second line (the of center one) in the dubblebond /// /// /// /// /// ///

61 /// /// private Point[] addTheSecondDubbleBondLine(int fromX, int fromY, int toX, int toY, Molecule mymolecule) { //****************************************************************** //***used linear algebra once more //same method as before to get the points perpendicular to the bond line between the atoms of the dubble bond //this gives nice dubblebonds in acyclic systems //*******************************************************************

Point[] theCoordinatesForTheDubbleBond = new Point[4];//in the order fromX1 fromX2, toY1, toY2

//arrays containing the Points of the dubbleBond new dubble line Point[] newToPoint = new Point[3]; Point[] newFromPoint = new Point[3];

newToPoint = calculateOrhtogonalDistancetAtToAtom(fromX, fromY, toX, toY, DISTANCEFROMTHEMIDDLEOFTHEBOND); newFromPoint = calculateOrthogonalDistanceAtFromAtom(fromX, fromY, toX, toY, DISTANCEFROMTHEMIDDLEOFTHEBOND);

theCoordinatesForTheDubbleBond[0] = newFromPoint[1]; theCoordinatesForTheDubbleBond[1] = newFromPoint[2]; theCoordinatesForTheDubbleBond[2] = newToPoint[1]; theCoordinatesForTheDubbleBond[3] = newToPoint[2];

return theCoordinatesForTheDubbleBond; }

} }

Klassen som har tre exceptions i sig using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace MoleculeRenderer { ///

/// exception when the molfile could not be read /// public class ExceptionErrorInMolfile : Exception { //doubtfull if this ever will happen. //any file will be read to something public override string Message { get

62 { return "Could not read the molfile"; } } }

///

/// exeption when molecule could not be created /// public class ExceptionInCreatingMolecule : Exception { public override string Message { get { return "Could not create molecule"; } } }

///

/// exception when the molfile is in the wrong formate /// public class ExceptionNotValidMolfileFormat : Exception { public override string Message { get { return "This is not a valid molfileformat"; } }

} }

Klassen Molecule using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Globalization; namespace MoleculeRenderer {

///

///this class creates a molecule from the molfile //the molecule is stored as one atomList and one bondsList /// internal class Molecule {

//the two lists that stores the atoms and bonds private List atomList = new List(); private List bondList = new List();

///

/// constructor of the Molecule class

63 /// which creates the molecule

/// public Molecule(string[] molfilelines) { //create the atomList this.createAtoms(numberofArrays(0, molfilelines), molfilelines);

//create the bondList this.createBonds(numberofArrays(4, molfilelines), molfilelines);

}

///

/// Atoms of the molecule /// public List AtomList { get { return this.atomList; } }

///

/// Bonds of the molecule /// public List BondList { get { return this.bondList; } }

///

/// generic method to find the number of atoms or bonds in the molecule /// /// integer where in the molfile the information is located /// the molfile /// size of the array private int numberofArrays(int position, string[] molfile) { int number; number = Convert.ToInt32(molfile[1].Substring(position, 3));

return number; }

///

/// method that adds the atoms of the molecule to the atomlist /// /// the molfile as a string[] private void createAtoms(int numerOfAtoms, string[] molfilelines) { for (int i = 0; i < numerOfAtoms; i++) { Atom myatom = new Atom(); myatom.AtomCount = i + 1;

64 myatom.XCoordinate = double.Parse(molfilelines[2 + i].Substring(0, 10).Trim(), CultureInfo.InvariantCulture);//hade problem med att omvandla med decimalpunkt, detta löste problemet myatom.YCoordiante = double.Parse(molfilelines[2 + i].Substring(10, 10).Trim(), CultureInfo.InvariantCulture); myatom.AtomSymbol = molfilelines[2 + i].Substring(30, 3).Trim(); myatom.Charge = Convert.ToInt32(molfilelines[2 + i].Substring(35, 3).Trim());

atomList.Add(myatom); }

//if the molecule contains isotopes these are added to the molecule if (isIsotope(molfilelines)) //used this way since not all molecules contain isotopes. { List isotopeList = createIsotopeArray(molfilelines);

for (int k = 0; k < isotopeList.Count; k = k + 2) { atomList[isotopeList[k] - 1].AtomCount = isotopeList[k]; atomList[isotopeList[k] - 1].IsotopeMass = isotopeList[k + 1]; } }

//if the molecule is charged add that information to the atom array if (isCharged(molfilelines)) { for (int i = 0; i < molfilelines.Length; i++) { if (molfilelines[i].Substring(0, 6) == "M CHG") { int numberOfChargedAtoms = int.Parse(molfilelines[i].Substring(6, 3).Trim());

for (int j = 0; j < numberOfChargedAtoms; j++) { int atomNumberOfChargedAtom = int.Parse(molfilelines[i].Substring(9 + j * 8, 4).Trim()); int chargeValue = Convert.ToInt32( molfilelines[i].Substring(13 + j * 8, 4).Trim() ); atomList[atomNumberOfChargedAtom - 1].Charge = chargeValue; //'atomNumber - 1' because the molfile starts on atomnumber 1 and the atom array I create start at zero. } } } }

//if the molecule contains radicals add them to the atoms array if (isRadical(molfilelines)) { for (int i = 0; i < molfilelines.Length; i++) { if (molfilelines[i].Substring(0, 6) == "M RAD") { int numberOfRadicals = int.Parse(molfilelines[i].Substring(6, 3).Trim());

for (int j = 0; j < numberOfRadicals; j++) { int atomNumberOfRadical = int.Parse(molfilelines[i].Substring(9 + j * 8, 4).Trim());

65 int radicalType = int.Parse(molfilelines[i].Substring(13 + j * 8, 4).Trim()); atomList[atomNumberOfRadical - 1].Radical = radicalType; //'atomNumber - 1' because the molfile starts on atomnumber 1 and the atom array I create start at zero. } } } } }

///

/// metod that creates an array of atomnumber/isotopes /// /// /// atomnumber1:isotope1 atomnumber2:isotope2 etc private List createIsotopeArray(string[] molfilelines) { //used this way, because the isotope information can be placed at two different places in the molfile. //I have not seen the MarwinDraw uses the designated dd position in the file, but rather put the difference information at the end of the file in the properties block, //int[] isotopeArray; int numberOfIsotopes; List isotopeList = new List();

for (int i = 0; i < molfilelines.Length; i++) { if (molfilelines[i].Substring(0, 6) == "M ISO") { numberOfIsotopes = int.Parse(molfilelines[i].Substring(6, 3).Trim());

for (int j = 0; j < numberOfIsotopes * 2; j++) { isotopeList.Add(int.Parse(molfilelines[i].Substring(9 + j * 4, 4).Trim())); } } }

return isotopeList; }

///

/// method which checks if the molecule contains isotopes in the properies block /// /// the molfile array /// true/false private bool isIsotope(string[] molfilelines) { bool isotope = false;

for (int i = 0; i < molfilelines.Length; i++) { if (molfilelines[i].Substring(0, 6) == "M ISO") { isotope = true; } } return isotope; }

66 ///

/// method that checks if the molecule contains charged elements in the properies block /// /// the molfile split in the an array /// true/false private bool isCharged(string[] molfilelines) { bool isCharged = false;

for (int i = 0; i < molfilelines.Length; i++) { if (molfilelines[i].Substring(0, 6) == "M CHG") { isCharged = true; } } return isCharged;

}

///

/// method that checks if the molecule contains a radical element in the properites block /// /// the molfile split in the an array /// true/false private bool isRadical(string[] molfilelines) { bool isRadical = false;

for (int i = 0; i < molfilelines.Length; i++) { if (molfilelines[i].Substring(0, 6) == "M RAD") { isRadical = true; } } return isRadical; }

///

/// method that adds the bonds of the molecule to the bondslist /// /// the molfile array private void createBonds(int numberOfBonds, string[] molfilelines) { int numberOfAtoms = atomList.Count;//to get the number of atomlines to skip

for (int i = 0; i < numberOfBonds; i++) { Bond mybond = new Bond(); int From = Convert.ToInt32(molfilelines[2 + numberOfAtoms + i].Substring(0, 3).Trim());//the atomCount of the atom the bond's start int To = Convert.ToInt32(molfilelines[2 + numberOfAtoms + i].Substring(3, 3).Trim());//the atomCount of the atom the bond ends mybond.Type = Convert.ToInt32(molfilelines[2 + numberOfAtoms + i].Substring(6, 3).Trim()); mybond.StereoType = Convert.ToInt32(molfilelines[2 + numberOfAtoms + i].Substring(9, 3).Trim());

67

//this creates the connection between the atoms mybond.FromAtom = this.atomList[From - 1]; mybond.FromAtom.BondsList.Add(mybond); mybond.ToAtom = this.atomList[To - 1]; mybond.ToAtom.BondsList.Add(mybond);

bondList.Add(mybond); }

} } }

Klassen MoleculeRender using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.IO; using System.Drawing; using System.Drawing.Imaging;//för metafile test TODO

//Only tested with the V2000 Connection table // version 2_1_1 med Eriks nya Atom och Bond klasser samt städade andra klasser.

//om heteroatom kommer väte på rätt sida om heteroatomen, men på samma höjd, dvs inte nedsänkt //testar att ha två fall, antigen åt höger eller åt vänster binding, räknar allt från dessa två lägen //Changed to lists instead of arrays and some more code cleaning // // //version 2.0, started to clean the code //En try/catch around Render methode to catch any problems with the program //+deleted the methods transfer of atoms and bonds arrays //+changed the transform coordinates method so that the molecule does not render up side down //+moved the increase size of molecule and transformYCoord methods to moleculeRender class //+added a method checkSizeOfMolecule to determine the size of the image object and added a constant to draw the molecule with margins on all sides //+backgroud colour of the box where the atom symbol was changed to transparent //+changed the charge property from string to int //+added a constant for easy change the size of the molecule //+deleted the properties to and from in the class bond and changed the code

//2.03 method that draws dubble bonds nice in acyclic systems, using linear algebra and changed the calculateChiralBondsPoints method to take an argumnet for the length //2.04 can show tripple bonds //2.0.5 changed the scalingfactor to work better with dubble/tripplebonds, also added a const for the distance between the dubble bond lines (since this is not scalable) //Changed draw either cis/trans bond with same method as dubblebonds //better looking isotope/charge text, added a white box under the isotope/charge text //2.0.7 Remade atom render to be able to have both charge/isotope/heteroatomWithHydrogens //2_0_8 Show heteroatoms with hydrogens when approp. But not as subscript

68 //2_0_9 refactoring, changed the code that RenderAtom and RenderBond classes draws the atoms and bonds respectively--Added exceptions with classen for own message //

//----to do look in the the white boxes for drawing atomsymbols and isotopes to be sure their are expandable, or as a common method??++dealt with //- to do, make isotopelabel figures look nice, used same font for the charge. Both looks uggly //_=generally increase size of the image a crop before it leaves???to reduce alaising, +++dealt with //- cut the draw bonds method in to one metod for each bond type????????????how to deal with fromX toX etc, ????????? //-the scaling of bonds and atoms not perfect, +++dealt with much better //---add the atom-list of charge,isotope, chiral etc....Now only work from properties part. Read What is needed //---radicals not good enough //---hydrogen on heteroatoms not as subscript // //______ATT GÖRA ANORLUNDA______// //(gjort---göra om molekylklassen så att dess konstruktor tar ett argument som är mollines arrayen, molekylklassen skapar i så fall en molekyl ifrån raderna och moleculerender skapar arrayen och får tillbaka en molekyl. man slipper tranferOfArray metoderna //klippa upp drawTheBonds metoden i drawTheBondsofType1 etc???hur flyttar man med alla fromX etc,som argument eller som instnavarabler? // //möjligen att ha en generisk metod bool metod för att kolla radikal/laddning/isotop med argument???? //göra om dubbelbinding som cis/trans binding, vilken är bäst? // //de både metoderna checkNextCoordiante som en metod som ger båda listorna tillbaka på något sätt samtidigt

//(gjort)skapa egna exceptions //------frågor------// // //Detta med laddning, det som står i ctfile stämmer inte med det MarwinD genererar. //det ska till en omvandlings mellan siffror och laddning enligt filen, men det behövs inte enligt mol filen som skapas //Det fungerar som det är skrivet av mig, men det krävs en diskusion...... //alla mina bindingar som fångs upp i default satsen, duger detta? //------FEL------// //(fixat---- -OH shows as -O, on all heteroatoms // //**************earlier versions*************************** //version 1.1--Has the atoms in a white box and at the correct place //version 1.2--The different types (single/double/tripple) of bonds and how to show them are sorted, to do needs more twisting to make them look nice. //version 1.3--Isotopes are included but would prefer them with thinner lines in the font + the dubble/tripple bonds look better //version 1.4--Changed the renderMolecule method in to drawAtoms and drawBonds to make it easier to read. + remade the atom position in it's box to measured properies to have less constants + sorted the earlier versions so I can see what was done in each version //version 1.5--Chiral bonds pointing up is working (type1) //version 1.6--Chiral bonds pointing down is working (type6) //version 1.7--Bond is up or bond is down is working(type4) //version 1.8--Charge is added //version 1.9--Either cis or trans bond is added(type3) + Radicals(three types) are included

69 namespace MoleculeRenderer { ///

/// controller class for the whole program /// public class MoleculeRender {

///

/// constant for scaling purposes /// private const int SCALINGFACTOR = 100;

///

/// how far appart the dubble bonds will be /// can be changed when SCALINGFACTOR is changed to make dubble bonds look nicer /// private const int DISTANCEFROMTHEMIDDLEOFTHEBOND = 13;

//*********** //It is not a linear relation of the distance between the lines in dubble/tripple //bonds and the size of the molecule //Therefore two constants are used //As a suggestion regarding the two constants above; //SCALINGFACTOR 200 and DISTANCEFROMTHEMIDDLEOFTHEBOND 13 looks good //SCALINGFACTOR 100 and DISTANCEFROMTHEMIDDLEOFTHEBOND 14 looks good

///

/// how far inside on the paper the molecule will be drawn /// private int placeMoleculeFurtherInsideThePictureBox = SCALINGFACTOR / 2;

public MoleculeRender() { }

///

/// Renders the received molfile as an Image /// The access Point of the program /// Molfile contents /// Image public Image Render(string molfile) { Molecule mymolecule; string[] lines;

try {

//splits the molfile in to an array of strings, one for each line in the molfile lines = molfile.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);

if (isThisNotAMolfile(lines)) { throw new ExceptionNotValidMolfileFormat(); } } catch {

70 throw new ExceptionNotValidMolfileFormat(); } try { //creates the molecule mymolecule = new Molecule(lines);

increaseTheSizeOfTheMolecule(SCALINGFACTOR, mymolecule); this.transformCoordinates(mymolecule);

Image myImage = new Bitmap(checkSizeOfMolecule(mymolecule.AtomList)[0] + placeMoleculeFurtherInsideThePictureBox, checkSizeOfMolecule(mymolecule.AtomList)[1] + placeMoleculeFurtherInsideThePictureBox);

Graphics paper = Graphics.FromImage(myImage); this.RenderMolecule(paper, mymolecule);

//TODO behövs städning av arrayer??????? nej, de finns bara inom denna metoden

return myImage; } catch { throw new ExceptionInCreatingMolecule(); }

}

///

/// method that checks if the file to be read is a molfile /// /// /// false/true private bool isThisNotAMolfile(string[] lines) { bool result = true;

//not sure if this is the best way. //the last line in the molfile starts like this 'M END' this method checks for that pattern if (lines.ElementAt(lines.Length - 1) == "M END") { result = false; } return result; }

///

/// method that checks the size of the molecule in pixels /// /// the atoms array /// int[X-size and Y-size] private int[] checkSizeOfMolecule(List atomsArray) { int[] sizeArray = new int[2]; double largestXCoord = 0; double largestYCoord = 0;

for (int i = 0; i < atomsArray.Count; i++) {

71 if (atomsArray[i].XCoordinate > largestXCoord) { largestXCoord = atomsArray[i].XCoordinate; }

if (atomsArray[i].YCoordiante > largestYCoord) { largestYCoord = atomsArray[i].YCoordiante; } }

sizeArray[0] = Convert.ToInt32(largestXCoord); sizeArray[1] = Convert.ToInt32(largestYCoord);

return sizeArray; }

///

/// method to increase the size of the molecule /// /// the scaling factor private void increaseTheSizeOfTheMolecule(int scalingFactor, Molecule mymolecule) { for (int i = 0; i < mymolecule.AtomList.Count; i++) { mymolecule.AtomList[i].XCoordinate = (mymolecule.AtomList[i].XCoordinate * scalingFactor); mymolecule.AtomList[i].YCoordiante = (mymolecule.AtomList[i].YCoordiante * scalingFactor); } }

///

/// metod that transforms the coordinates to the positive part of the coordinatesystem and changes the Y-coordinate to work in winForm Coord System /// private Molecule transformCoordinates(Molecule mymolecule) { double mostNegativeXCoordinate = 0; double mostNegativeYCoordinate = 0; double mostPossitiveYCoordinate = 0;

//to find the most negative values of X- and Y-Coordinates for (int i = 0; i < mymolecule.AtomList.Count; i++) {

if (mymolecule.AtomList[i].XCoordinate < mostNegativeXCoordinate) { mostNegativeXCoordinate = mymolecule.AtomList[i].XCoordinate; }

if (mymolecule.AtomList[i].YCoordiante < mostNegativeYCoordinate) { mostNegativeYCoordinate = mymolecule.AtomList[i].YCoordiante; } }

//change X/Y-coordinates to positive values for (int j = 0; j < mymolecule.AtomList.Count; j++) { mymolecule.AtomList[j].XCoordinate = (mymolecule.AtomList[j].XCoordinate - mostNegativeXCoordinate + placeMoleculeFurtherInsideThePictureBox);

72 mymolecule.AtomList[j].YCoordiante = (mymolecule.AtomList[j].YCoordiante - mostNegativeYCoordinate); }

//find the largest Y-coord to be used as zero value for (int k = 0; k < mymolecule.AtomList.Count; k++) { if (mymolecule.AtomList[k].YCoordiante > mostPossitiveYCoordinate) { mostPossitiveYCoordinate = mymolecule.AtomList[k].YCoordiante; } }

//change Y-coordinates so that Y get bigger downwards for (int l = 0; l < mymolecule.AtomList.Count; l++) { mymolecule.AtomList[l].YCoordiante = mostPossitiveYCoordinate - mymolecule.AtomList[l].YCoordiante + placeMoleculeFurtherInsideThePictureBox; } return mymolecule; }

///

/// Method that draws the molecule on the paper /// /// the Graphics objec /// the molecule private void RenderMolecule(Graphics paper, Molecule mymolecule) { //create a new instance of the BondRender class and render the bonds BondRender myBondRender = new BondRender(); myBondRender.DrawTheBonds(paper, mymolecule, SCALINGFACTOR, DISTANCEFROMTHEMIDDLEOFTHEBOND);

//create a new instance of the AtomRender class and render the atoms AtomRender myAtomRender = new AtomRender(); myAtomRender.DrawTheAtoms(paper, mymolecule, SCALINGFACTOR); }

} }

73