com.actelion.research.util.Sketch Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of openchemlib Show documentation
Show all versions of openchemlib Show documentation
Open Source Chemistry Library
package com.actelion.research.util;
import com.actelion.research.chem.Molecule;
import com.actelion.research.chem.StereoMolecule;
import com.actelion.research.chem.reaction.Reaction;
import java.awt.*;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
/**
*
* Title: Actelion Java Library
* Description:
* Copyright: Copyright (c) 2002
* Company: Actelion Ltd.
* @author Christian Rufener
* @version 1.0
*/
public class Sketch
{
private static final boolean debug_ = false;
///////////////////////////////////////////////////////////////////////////////
// A sketch unit is one decipoint. One decipoint is 1/10 of a point; one point is
// 1/72 of a inch. The origin of the coordinate axes is at the top left corner of the
// screen. The x coordinates increase fro left to right, and the y coordinates
// increase from top to bottom of the screen.
// - ISIS Sketch File Format
///////////////////////////////////////////////////////////////////////////////
public static final double PI = 3.1415926;
public static final int MAXMOLS = 12;
public static final byte $Version = (byte)1;
public static final byte $Totobjs = (byte)2;
public static final byte $Obj = (byte)3;
public static final byte $Locked = (byte)4;
public static final byte $Pen_width = (byte)5;
public static final byte $Pen_style = (byte)6;
public static final byte $Pen_color = (byte)7;
public static final byte $Transparent = (byte)8;
public static final byte $Fill_style = (byte)9;
public static final byte $Fill_color = (byte)10;
public static final byte $Font = (byte)11;
public static final byte $Parent = (byte)12;
public static final byte $Obj_coords = (byte)13;
public static final byte $Crop_coords = (byte)14;
public static final byte $Roundrect_curve = (byte)15;
public static final byte $Arc_endpts = (byte)16;
public static final byte $Poly_points = (byte)17;
public static final byte $Poly_smoothed = (byte)18;
public static final byte $Begsketch = (byte)19;
public static final byte $Endsketch = (byte)20;
public static final byte $MDLEditText = (byte)21;
public static final byte $Atom_coords = (byte)22;
public static final byte $Atom_type = (byte)23;
public static final byte $Atom_list = (byte)24;
public static final byte $Atom_alias = (byte)25;
public static final byte $Atom_number = (byte)26;
public static final byte $Atom_chg = (byte)27;
public static final byte $Atom_rad = (byte)28;
public static final byte $Atom_msdif = (byte)29;
public static final byte $Atom_valence = (byte)30;
public static final byte $Atom_rbcount = (byte)31;
public static final byte $Atom_substcount = (byte)32;
public static final byte $Atom_stereo_care = (byte)33;
public static final byte $Atom_h0bit = (byte)34;
public static final byte $Atom_unsat = (byte)35;
public static final byte $Atom_value = (byte)36;
public static final byte $Atom_dispflags = (byte)37;
public static final byte $Atom_hpos = (byte)38;
public static final byte $Bond_atoms = (byte)39;
public static final byte $Bond_type = (byte)40;
public static final byte $Bond_stereo_type = (byte)41;
public static final byte $Bond_topo = (byte)42;
public static final byte $Bond_qtopo = (byte)43;
public static final byte $Bond_rxn_center = (byte)44;
public static final byte $Bond_stereo_care = (byte)45;
public static final byte $Bond_dbl_side = (byte)46;
public static final byte $Bond_dbl_width = (byte)47;
public static final byte $RxnAtch = (byte)48;
public static final byte $RGroupNo = (byte)49;
public static final byte $RLogic = (byte)50;
public static final byte $SGroupAtch = (byte)51;
public static final byte $SGroupName = (byte)52;
public static final byte $Atom_rgroupAtch = (byte)53;
public static final byte $SGroupType = (byte)54;
public static final byte $SGroupLinkVal = (byte)55;
public static final byte $ArrowDir = (byte)56;
public static final byte $ArrowStyle = (byte)57;
public static final byte $MetaData = (byte)58;
public static final byte $Mol_type = (byte)59;
public static final byte $Abbrev_atch = (byte)60;
public static final byte $SGroupAtchPt = (byte)61;
public static final byte $Atom_npos = (byte)62;
public static final byte $Atom_aamapped = (byte)63;
public static final byte $Name = (byte)64;
public static final byte $Comment = (byte)65;
public static final byte $Atom_fixed = (byte)66;
public static final byte $3D_num_basis_objs = (byte)67;
public static final byte $3D_basis_objs = (byte)68;
public static final byte $3D_name = (byte)69;
public static final byte $3D_minval = (byte)70;
public static final byte $3D_maxval = (byte)71;
public static final byte $3D_tolerance = (byte)72;
public static final byte $3D_point_dist = (byte)73;
public static final byte $3D_dihed_chiral = (byte)74;
public static final byte $3D_exclus_radius = (byte)75;
public static final byte $3D_point_dir = (byte)76;
public static final byte $3D_atom_query = (byte)77;
public static final byte $Atom_zcoord = (byte)78;
public static final byte $Atom_exact_change = (byte)79;
public static final byte $Atom_rxn_stereo = (byte)80;
public static final byte $Bond_crossed = (byte)81;
public static final byte $Bond_alt_stereo = (byte)82;
public static final byte $3D_exclus_ignore = (byte)83;
public static final byte $Pen_style_token = (byte)84;
public static final byte $BigMetaData = (byte)85;
public static final byte $Pen_RGB2Color = (byte)88;
public static final byte $Fill_RGB2Color = (byte)89;
public static final byte $Atom_tplatchpt = (byte)91;
public static final byte $Is_A_Model = (byte)92;
public static final byte $Atom_aamap_num = (byte)96;
public static final byte $Atom_hshow = (byte)98;
public static final byte $SGroupNewAtch = (byte)164;
public static final byte $SGroupContext = (byte)202;
public static final byte $Nostruct_label = (byte)203;
public static final byte $Nostruct_regno = (byte)204;
public static final byte $Circ_Arc_Points = (byte)205;
public static final byte $Bond_hash_spacing = (byte)206;
public static final byte $Bond_bond_spacing = (byte)207;
public static final byte $Atom_margin_width = (byte)208;
public static final byte $ArrowType = (byte)220;
public static final byte $Atom_Orig_coords = (byte)223;
public static final byte $Atom_AttachLen = (byte)224;
public static final byte $Atom_DotWidth = (byte)225;
public static final byte $SGroupBracketLen = (byte)226;
public static final byte $3D_marker_rf = (byte)227;
public static final byte $Model_Rotated = (byte)228;
public static final byte $ArrowSize = (byte)229;
public static final byte $ArrowShaftSp = (byte)230;
public static final byte $Pen_widthUnit = (byte)231;
public static final byte $Atom_symbol = (byte)232;
public static final byte $Atom_NumSize = (byte)233;
public static final byte $3D_exclus_dradius = (byte)234;
public static final byte $Atom_can_reverse = (byte)235;
public static final byte $3D_point_ddist = (byte)236;
public static final byte $Obj_Mol = (byte)12;
public static final byte $Obj_Chiral = (byte)19;
public static final byte $Obj_Bond = (byte)32;
public static final byte $Obj_Atom = (byte)33;
public static final int MSDIFF_OFFSET = 19;
private static String DefaultFontName = "Arial";
// private static final int MAXMFCOMMENT = 5000;
private static final int BYTESIZE = 1;
private static final int WORDSIZE = 2;
private static final int MOLSIZE = 1440;
private static final int REACTIONSIZE = 4000;
private static final int PLUSSIZE = 20;
// public static final int TAGNAMELEN (sizeof("MDLSK")/sizeof(char)+1);
public static boolean createMolFromSketchFile(StereoMolecule mol, String szFileName)
throws IOException
{
File f = new File(szFileName);
byte[] buffer = new byte[(int)f.length()];
FileInputStream is = new FileInputStream(f);
is.read(buffer);
is.close();
return createMolFromSketchBuffer(mol, buffer);
}
public static boolean createMolFromSketchBuffer(StereoMolecule mol, byte[] buffer)
throws IOException
{
ByteArrayInputStream b = new ByteArrayInputStream(buffer);
LittleEndianDataInputStream fp = new LittleEndianDataInputStream(b);
return getMolObjects(mol, fp);
}
public static boolean writeMolSketchFile(Molecule mol, String filename)
throws IOException
{
FileOutputStream os = new FileOutputStream(filename);
boolean ok = writeMolSketchFile(mol,os);
os.close();
return ok;
}
public static boolean writeMolSketchFile(Molecule mol, OutputStream os)
throws IOException
{
boolean ok = false;
byte[] buffer = createSketchFromMol(mol);
if (buffer != null) {
os.write(buffer);
ok = true;
}
return ok;
}
public static boolean createReactionFromSketchFile(Reaction rxn, String szFileName)
throws IOException
{
File f = new File(szFileName);
byte[] buffer = new byte[(int)f.length()];
FileInputStream is = new FileInputStream(f);
is.read(buffer);
is.close();
ByteArrayInputStream b = new ByteArrayInputStream(buffer);
LittleEndianDataInputStream fp = new LittleEndianDataInputStream(b);
return createReactionFromSketchBuffer(rxn, fp);
}
public static boolean createReactionFromSketchBuffer(Reaction rxn, byte[] buffer)
throws IOException
{
ByteArrayInputStream b = new ByteArrayInputStream(buffer);
LittleEndianDataInputStream fp = new LittleEndianDataInputStream(b);
return createReactionFromSketchBuffer(rxn, fp);
}
public static boolean writeReactionSketchFile(Reaction rxn, String filename)
throws IOException
{
boolean ok = false;
FileOutputStream os = new FileOutputStream(filename);
if (writeReactionSketchFile(rxn,os)) {
os.flush();
os.close();
ok = true;
}
return ok;
}
public static boolean writeReactionSketchFile(Reaction rxn, OutputStream os)
throws IOException
{
boolean ok = false;
byte[] buffer = createSketchFromReaction(rxn);
if (buffer != null) {
os.write(buffer);
ok = true;
}
return ok;
}
/*
private int getMolObjectsOl(StereoMolecule mol,LittleEndianDataInputStream fp,int molAtoms[]) throws IOException
{
byte buff[] = new byte[1024];
byte c;
byte bval;
short at1,at2,AtomType=0;
int i;
short t,val = 0,num=0;
DblPoint dblCoords = new DblPoint();
int ObjectCount=0;
int MolObject=-1,BondObject=-1,AtomObject=-1;
String s;
do {
c = fp.readByte();
switch (c) {
case $Version:
ObjectCount=0;
MolObject = -1;
t = fp.readShort();
bval = fp.readByte();
break;
case $Totobjs:
t = fp.readShort();
val = fp.readShort();
ObjectCount = -1; AtomObject=-1;BondObject=-1;
break;
case $Obj:
t = fp.readShort();
bval = fp.readByte();
switch(bval) {
case $Obj_Mol:
MolObject++;AtomObject=-1;BondObject=-1;
AtomType = 0;
ObjectCount = -1;
break;
case $Obj_Chiral:
// mol.SetChiral(true);
mol.setChiral(true);
ObjectCount++;
break;
case $Obj_Bond:
BondObject++;
//System.out.println("mol.vectBonds.insert(mol.vectBonds.end(),bond);");
// mol.vectBonds.insert(mol.vectBonds.end(),bond);
ObjectCount++;
AtomType = 0;
break;
case $Obj_Atom:
AtomObject++;
//CXR ChemDraw Problem C atoms are not defined with symbol and number!
// strcpy(atom.Symbol,"C");
// atom.Number = 6;
// mol.vectAtoms.insert(mol.vectAtoms.end(),atom);
mol.addAtom("C");
ObjectCount++;
AtomType = 0;
break;
default:
ObjectCount++;
break;
}
if (MolObject > 1) {
System.err.println("Sorry Only One Molecule supported\n");
return 1;
}
break;
case $Begsketch: // $BeginSketch
t = fp.readShort();
fp.readFully(buff,0,(int)t-2);
// fp.BufRead(buff,t-2);
break;
case $Endsketch: //$EndSketch
t = fp.readShort();
// return 0;
break;
case $Atom_coords: //$Atom_coords
t = fp.readShort();
dblCoords.x = fp.readFloat();
dblCoords.y = fp.readFloat();
// fp.BufRead(&dblCoords,sizeof(dblCoords));
mol.setAtomX(AtomObject,dblCoords.x);
// mol.vectAtoms[AtomObject].x = dblCoords.x;
mol.setAtomY(AtomObject,dblCoords.y);
// mol.vectAtoms[AtomObject].y = dblCoords.y;
mol.setAtomZ(AtomObject,0);
// mol.vectAtoms[AtomObject].z = 0;
molAtoms[AtomObject] = ObjectCount;
// mol.vectAtoms[AtomObject].Number = ObjectCount;
// mol.vectAtoms[AtomObject].Charge = 0;
break;
case $Atom_type: //$Atom Type
t = fp.readShort();
AtomType = fp.readShort();
// fp.BufRead(&AtomType,2);
break;
case $Atom_symbol:
t = fp.readShort();
fp.readFully(buff,0,t-2);
// fp.BufRead(buff,t-2);
s = P2CStr(buff);
System.out.println("Atom Symbol Length " + t + " sym = " + P2CStr(buff));
// %d Sym=[%s]\n",t,(char *)P2CStr(buff));
switch (AtomType) {
case 270:
System.out.println("Atom list");
// strcpy(mol.vectAtoms[AtomObject].Symbol,"L");
break;
case 271:
System.out.println("Atom not list");
// mol.vectAtoms[AtomObject].bNotList = TRUE;
// strcpy(mol.vectAtoms[AtomObject].Symbol,"L");
break;
default:
System.out.println("Atom Symbol ");
mol.setAtomicNo(AtomObject,Molecule.getAtomicNoFromLabel(s));
// strcpy(mol.vectAtoms[AtomObject].Symbol,P2CStr(buff));
break;
}
break;
case $Atom_list: //$Atom_List
t = fp.readShort();
num = fp.readShort();
// fp.BufRead(&num,1); // read number of atoms in list
for (i = 0; i < num ; i++) {
val = fp.readShort(); // read atoms in list
// mol.vectAtoms[AtomObject].List[i] = (int)val;
//TRACE("Atom %d in List\n",(int)val);
}
// mol.vectAtoms[AtomObject].List[i] = -1;
break;
case $Atom_alias: //$Atom Alias
t = fp.readShort();
fp.readFully(buff,0,t-2);
// fp.BufRead(buff,t-2);
break;
case $Atom_number: //Atom_number
t = fp.readShort();
val = fp.readShort();
break;
case $Atom_chg: //$Atom_Charge
t = fp.readShort();
bval = fp.readByte();
mol.setAtomCharge(AtomObject,((int)bval)-16);
// System.out.println(" mol.vectAtoms[AtomObject].Charge = ((int)bval)-16;");
// mol.vectAtoms[AtomObject].Charge = ((int)bval)-16;
break;
case $Atom_rad: //Atom_rad
t = fp.readShort();
bval = fp.readByte();
//TRACE("Atom Radical %d \n",bval);
break;
case $Atom_msdif: //$Atom_msdif
t = fp.readShort();
bval = fp.readByte();
//TRACE("Atom Mass Diff %d \n",bval);
break;
case $Atom_valence: //$Atom_valence
t = fp.readShort();
bval = fp.readByte();
//TRACE("Atom Valence %d \n",bval);
break;
case $Bond_atoms: //$Bond_atoms
t = fp.readShort();
at1 = fp.readShort();
at2 = fp.readShort();
mol.addBond(at1,at2,0);
// fp.BufRead(&at1,2);
// fp.BufRead(&at2,2);
// System.out.println(" mol.vectBonds[BondObject].Atoms[0] = at1;");
// mol.vectBonds[BondObject].Atoms[0] = at1;
// mol.vectBonds[BondObject].Atoms[1] = at2;
// mol.vectBonds[BondObject].Type = 1; // Default
break;
case $Bond_type: //$Bond_type
t = fp.readShort();
bval = fp.readByte();
setBondFeatures(mol,BondObject,(int)bval,0,0);
// mol.setBondType(BondObject,getBondType((int)bval));
// System.out.println("mol.vectBonds[BondObject].Type = (int)bval;");
// mol.vectBonds[BondObject].Type = (int)bval;
break;
case $Bond_stereo_type: //Bond_stereo_type
t = fp.readShort();
bval = fp.readByte();
setBondFeatures(mol,BondObject,0,(int)bval,0);
// System.out.println(" mol.vectBonds[BondObject].Stereo = (int)bval;");
// mol.vectBonds[BondObject].Stereo = (int)bval;
break;
case $Atom_aamap_num: // Atom_aamap_num
t = fp.readShort();
val = fp.readShort();
// System.out.println("mol.vectAtoms[AtomObject].AtomMap = (int)val;");
mol.setAtomMapNo(AtomObject,(int)val,false);
// mol.vectAtoms[AtomObject].AtomMap = (int)val;
break;
case -1:
break;
default:
t = fp.readShort();
fp.readFully(buff,0,t-2);
// fp.BufRead(buff,t-2);
break;
}
} while (c != -1);
CorrectBondAtomNumbering(mol,AtomObject+1, BondObject+1,molAtoms);
return 1;
}
*/
private static boolean getMolObjects(StereoMolecule molMain, LittleEndianDataInputStream fp)
throws IOException
{
byte c;
byte bval;
short t;
int ObjectCount = 0;
boolean ok = false;
byte[] buff = new byte[1024];
boolean chiralFound = false;
do {
;
c = fp.readByte();
switch (c) {
case $Version:
ObjectCount = 0;
t = fp.readShort();
bval = fp.readByte();
break;
case $Totobjs:
t = fp.readShort();
fp.readShort(); // val
ObjectCount = -1;
break;
case $Font:
t = fp.readShort();
bval = fp.readByte(); // String length
fp.readFully(buff, 0, (int)bval); // Font name
fp.readShort(); // font size in decipoints
fp.readByte(); // emphasis
break;
case $Endsketch: //$EndSketch
try {
t = fp.readShort();
} catch (IOException e) {
System.err.println("Error found $Endsketch, but no data...");
}
break;
case $Obj:
t = fp.readShort();
bval = fp.readByte();
switch (bval) {
case $Obj_Mol:
StereoMolecule m = new StereoMolecule();
if (!getMolObject(m, fp)) {
return false;
}
molMain.addMolecule(m);
if (m.isFragment()) {
molMain.setFragment(true);
}
ok = true;
break;
case $Obj_Chiral:
// Maybe needs a FIX by doing also setChirality(cChiralityPure) ???
chiralFound = true;
ObjectCount++;
break;
default:
System.out.println(
"Warning: Sketch.getMolObjects() Object not supported: " + bval);
ObjectCount++;
break;
}
break;
case 0:
// System.out.println("Read a byte of value '0'");
break;
default:
t = fp.readShort();
if (t > 2) {
fp.readFully(buff, 0, (int)t - 2);
}
break;
}
} while ((c != -1) && (fp.in.available() > 0));
if (!chiralFound)
molMain.setToRacemate();
return ok;
}
private static boolean createReactionFromSketchBuffer(
Reaction rxn, LittleEndianDataInputStream fp) throws IOException
{
int numObjects;
String strText;
StereoMolecule mol = null;
Rect rect = new Rect();
Arrow arrow = new Arrow();
ArrayList v = new ArrayList();
// Read Header
numObjects = readSketchHeader(fp);
if (numObjects > 1) {
for (int i = 0; i < numObjects; i++) {
switch (getNextObject(fp)) {
case 9: // Text
strText = getTextObject(rect, fp);
if (strText == "+") {
}
break;
case $Obj_Mol: // Mol
mol = new StereoMolecule();
getMolObject(mol, fp);
mol.ensureHelperArrays(Molecule.cHelperNeighbours);
v.add(mol);
break;
case 14: //Arrow
getArrowObject(arrow, fp);
break;
case 4: //LineArrow
getLineArrowObject(arrow, fp);
break;
default:
System.err.println(
"Warning: Sketch.createReactionFromSketchBuffer(): UnKnown Object");
break;
}
}
buildReaction(rxn, v, arrow);
return true;
} else {
return false;
}
}
private static void buildReaction(Reaction r, ArrayList v, Arrow a)
{
int size = v.size();
Point pt;
StereoMolecule m = null;
for (int i = 0; i < size; i++) {
m = v.get(i);
pt = getMoleculeCenter(m);
if (a.left > pt.x) {
r.addReactant(m);
} else {
r.addProduct(m);
}
}
}
private static Rect getBoundingRect(Molecule m)
{
Rect r = new Rect(Short.MAX_VALUE, Short.MAX_VALUE, Short.MIN_VALUE, Short.MIN_VALUE);
int atoms = m.getAllAtoms();
float x;
float y;
for (int i = 0; i < atoms; i++) {
x = (short)m.getAtomX(i);
y = (short)m.getAtomY(i);
r.left = (short)Math.min(x, r.left);
r.top = (short)Math.min(y, r.top);
r.right = (short)Math.max(x, r.right);
r.bottom = (short)Math.max(y, r.bottom);
}
debug("Bounding Rect for Molecule " + m + " is " + r.left + ","
+ r.top + "," + r.right + "," + r.bottom);
return r;
}
private static Rect2D getBoundingRectangle(Molecule m)
{
Rect2D r = new Rect2D(
Double.MAX_VALUE, Double.MAX_VALUE, Double.MIN_VALUE, Double.MIN_VALUE);
int atoms = m.getAllAtoms();
double x;
double y;
for (int i = 0; i < atoms; i++) {
x = m.getAtomX(i);
y = m.getAtomY(i);
r.left = Math.min(x, r.left);
r.top = Math.min(y, r.top);
r.right = Math.max(x, r.right);
r.bottom = Math.max(y, r.bottom);
}
debug("Bounding Rect2D for Molecule " + m + " is " + r.left + ","
+ r.top + "," + r.right + "," + r.bottom);
return r;
}
private static Rect2D getBoundingRectangle(Reaction rxn)
{
Rect2D r = new Rect2D(
Double.MAX_VALUE, Double.MAX_VALUE, Double.MIN_VALUE, Double.MIN_VALUE);
Rect2D rm = null;
int mols = rxn.getMolecules();
for (int i = 0; i < mols; i++) {
rm = getBoundingRectangle(rxn.getMolecule(i));
r.left = Math.min(r.left, rm.left);
r.top = Math.min(r.top, rm.top);
r.right = Math.max(r.right, rm.right);
r.bottom = Math.max(r.bottom, rm.bottom);
}
debug("Bounding Rect2D for Reaction " + rxn + " is " + r.left + ","
+ r.top + "," + r.right + "," + r.bottom);
return r;
}
private static Point getMoleculeCenter(Molecule m)
{
Rect r = getBoundingRect(m);
return getCenter(r);
}
private static Point getCenter(Rect r)
{
int dx = r.right - r.left;
int dy = r.bottom - r.top;
return new Point(r.left + (dx / 2), r.top + (dy / 2));
}
private static boolean getMolObject(StereoMolecule mol, LittleEndianDataInputStream fp)
throws IOException
{
byte[] buff = new byte[1024];
byte c;
byte bval;
short at1;
short at2;
short AtomType = 0;
int i;
short t;
short val = 0;
short num = 0;
DblPoint dblCoords = new DblPoint();
int ObjectCount = 0;
int MolObject = -1;
int BondObject = -1;
int AtomObject = -1;
String s;
int[] molAtoms = new int[mol.getMaxAtoms()];
boolean chiralFound = false;
do {
c = fp.readByte();
// System.out.println("GetMolObject " + (int)c);
switch (c) {
case $Totobjs:
t = fp.readShort();
val = fp.readShort();
ObjectCount = -1;
AtomObject = -1;
BondObject = -1;
break;
case $Obj:
t = fp.readShort();
bval = fp.readByte();
switch (bval) {
/*
case $Obj_Mol:
MolObject++;AtomObject=-1;BondObject=-1;
AtomType = 0;
ObjectCount = -1;
break;
*/
case $Obj_Bond:
BondObject++;
ObjectCount++;
break;
case $Obj_Atom:
AtomObject++;
mol.addAtom("C");
ObjectCount++;
AtomType = 0;
break;
case $Obj_Chiral:
ObjectCount++;
chiralFound = true;
AtomType = 0;
break;
default:
ObjectCount++;
break;
}
if (MolObject > 1) {
System.err.println("Sorry Only One Molecule supported\n");
throw new RuntimeException("Only One Molecule supported");
}
break;
case $Begsketch: // $BeginSketch
t = fp.readShort();
fp.readFully(buff, 0, (int)t - 2);
// fp.BufRead(buff,t-2);
break;
case $Endsketch: //$EndSketch
t = fp.readShort();
correctBondAtomNumbering(mol, AtomObject + 1, BondObject + 1, molAtoms);
if (!chiralFound)
mol.setToRacemate();
return true;
case $Atom_coords: //$Atom_coords
t = fp.readShort();
dblCoords.x = fp.readFloat();
dblCoords.y = fp.readFloat();
mol.setAtomX(AtomObject, dblCoords.x);
mol.setAtomY(AtomObject, dblCoords.y);
mol.setAtomZ(AtomObject, 0);
molAtoms[AtomObject] = ObjectCount;
// System.out.println("R #" + AtomObject + " " + dblCoords.x + " " + dblCoords.y);
break;
case $Atom_type: //$Atom Type
t = fp.readShort();
AtomType = fp.readShort();
break;
case $Atom_symbol:
t = fp.readShort();
fp.readFully(buff, 0, t - 2);
s = convertPascalString(buff);
mol.setAtomicNo(AtomObject, Molecule.getAtomicNoFromLabel(s));
break;
case $Atom_list: //$Atom_List
boolean bNotList = false;
if (AtomType == 271) {
bNotList = true;
}
// Number of list entries
t = fp.readShort();
num = fp.readByte();
int[] v = new int[num];
for (i = 0; i < num; i++) {
val = fp.readShort(); // read atoms in list
v[i] = (int)val;
}
mol.setAtomList(AtomObject, v, bNotList);
mol.setFragment(true);
break;
case $Atom_alias: //$Atom Alias
t = fp.readShort();
fp.readFully(buff, 0, t - 2);
break;
case $Atom_number: //Atom_number
t = fp.readShort();
val = fp.readShort();
break;
case $Atom_chg: //$Atom_Charge
t = fp.readShort();
bval = fp.readByte();
mol.setAtomCharge(AtomObject, ((int)bval) - 16);
break;
case $Atom_rad: //Atom_rad
t = fp.readShort();
bval = fp.readByte();
mol.setAtomRadical(AtomObject, getRadicalType(bval));
//System.out.println("Atom Radical %d \n",bval);
break;
case $Atom_msdif: //$Atom_msdif
t = fp.readShort();
bval = fp.readByte();
// ignore is if msdiff is 0
if (bval != 0) {
int iso = bval - MSDIFF_OFFSET;
int atomicNo = mol.getAtomicNo(AtomObject);
mol.setAtomMass(AtomObject, Molecule.cRoundedMass[atomicNo] + iso);
}
break;
case $Atom_valence: //$Atom_valence
t = fp.readShort();
bval = fp.readByte();
//System.out.println("Atom Valence %d \n",bval);
break;
case $Bond_atoms: //$Bond_atoms
t = fp.readShort();
at1 = fp.readShort();
at2 = fp.readShort();
mol.addBond(at1, at2, Molecule.cBondTypeSingle);
break;
case $Bond_type: //$Bond_type
t = fp.readShort();
bval = fp.readByte();
setBondFeatures(mol, BondObject, (int)bval, 0, 0);
break;
case $Bond_stereo_type: //Bond_stereo_type
t = fp.readShort();
bval = fp.readByte();
setBondFeatures(mol, BondObject, 0, (int)bval, 0);
break;
case $Atom_aamap_num: // Atom_aamap_num
t = fp.readShort();
val = fp.readShort();
mol.setAtomMapNo(AtomObject, (int)val, false);
break;
case -1:
break;
default:
t = fp.readShort();
if (t > 2) {
fp.readFully(buff, 0, t - 2);
}
break;
}
} while (c != -1);
return false;
}
///////////////////////////////////////////////////////////////////////////////
// Function: FindAtomIndex
// Purpose: This function is used by the CorrectBondAtomNumbering Function
// and finds the 0 based index of the Atoms in the Molecule based
// on the sketch internal numbering
// Parameters: Molecule & to operate on
// int Sketch Atom number
//
// Return Val: 0 based Atom index
///////////////////////////////////////////////////////////////////////////////
private static int findAtomIndex(StereoMolecule mol, int number, int[] molAtoms)
{
int i;
int cnt = molAtoms.length;
for (i = 0; i < cnt; i++) {
if (molAtoms[i] == number) {
return i;
}
}
return -1;
}
private static boolean correctBondAtomNumbering(
StereoMolecule mol, int atoms, int bonds, int[] molAtoms)
{
int i;
int cnt = bonds;
int a1;
int a2;
for (i = 0; i < cnt; i++) {
a1 = findAtomIndex(mol, mol.getBondAtom(0, i), molAtoms);
// a1 = FindAtomIndex(mol,mol.vectBonds[i].Atoms[0],int molAtoms[]);
a2 = findAtomIndex(mol, mol.getBondAtom(1, i), molAtoms);
// a2 = FindAtomIndex(mol,mol.vectBonds[i].Atoms[1],int molAtoms[]);
if ((a1 == -1) || (a2 == -1)) {
return false;
}
mol.setBondAtom(0, i, a1);
// mol.vectBonds[i].Atoms[0] = a1;
mol.setBondAtom(1, i, a2);
// mol.vectBonds[i].Atoms[1] = a2;
}
cnt = atoms;
for (i = 0; i < cnt; i++) {
// mol.vectAtoms[i].Number = mol.GetAtomNumberFromSymbol(i);
}
return true;
}
private static String convertPascalString(byte[] pstr)
{
if ((pstr == null) || (pstr[0] > 255)) {
return null;
}
return new String(pstr, 1, pstr[0], StandardCharsets.UTF_8);
}
private static void setBondFeatures(
StereoMolecule mMol, int bond, int bondType, int stereo, int topology)
{
int realBondType = 0;
boolean isAtomESRAnd = false;
switch (stereo) {
case 1:
realBondType = Molecule.cBondTypeUp;
break;
case 3:
realBondType = Molecule.cBondTypeCross;
break;
case 4:
realBondType = Molecule.cBondTypeUp;
isAtomESRAnd = true;
break;
case 6:
realBondType = Molecule.cBondTypeDown;
break;
default:
switch (bondType) {
case 1:
realBondType = Molecule.cBondTypeSingle;
break;
case 2:
realBondType = Molecule.cBondTypeDouble;
break;
case 3:
realBondType = Molecule.cBondTypeTriple;
break;
case 4:
realBondType = Molecule.cBondTypeDelocalized;
break;
}
break;
}
if (realBondType > 0) {
mMol.setBondType(bond, realBondType);
}
if (isAtomESRAnd)
mMol.setAtomESR(mMol.getBondAtom(0, bond), Molecule.cESRTypeAnd, -1);
int queryFeatures = 0;
if (bondType > 4) {
switch (bondType) {
case 5:
queryFeatures |= (Molecule.cBondQFSingle | Molecule.cBondQFDouble);
break;
case 6:
queryFeatures |= (Molecule.cBondQFSingle | Molecule.cBondQFDelocalized);
break;
case 7:
queryFeatures |= (Molecule.cBondQFDouble | Molecule.cBondQFDelocalized);
break;
case 8:
queryFeatures |= Molecule.cBondQFBondTypes;
break;
}
}
if (topology == 1) {
queryFeatures |= Molecule.cBondQFRing;
}
if (topology == 2) {
queryFeatures |= Molecule.cBondQFNotRing;
}
if (queryFeatures != 0) {
mMol.setBondQueryFeature(bond, queryFeatures, true);
}
}
private static int getRadicalType(int rad)
{
int radical = 0;
switch (rad) {
case 1:
radical = Molecule.cAtomRadicalStateS;
break;
case 2:
radical = Molecule.cAtomRadicalStateD;
break;
case 3:
radical = Molecule.cAtomRadicalStateT;
break;
default:
radical = 0;
break;
}
return radical;
}
private static int readSketchHeader(LittleEndianDataInputStream fp)
throws IOException
{
byte[] buff = new byte[1024];
byte c;
short t;
short val;
while ((c = fp.readByte()) != -1) {
switch (c) {
case $Version: //Version
t = fp.readShort();
fp.readByte(); // bval
break;
case $Totobjs: // TotObjects
t = fp.readShort();
val = fp.readShort();
//System.out.println("Total Objects %d\n",(int)val);
return (int)val;
case $Comment:
case $Name:
t = fp.readShort();
fp.readFully(buff, 0, t - 2);
break;
default:
t = fp.readShort();
fp.readFully(buff, 0, t - 2);
return 0;
}
}
return 0;
}
private static int getNextObject(LittleEndianDataInputStream fp)
throws IOException
{
byte[] buff = new byte[1024];
byte c;
byte bval;
short t;
while ((c = fp.readByte()) != -1) {
switch (c) {
case $Obj:
t = fp.readShort();
bval = fp.readByte();
return bval;
default:
t = fp.readShort();
fp.readFully(buff, 0, t - 2);
break;
}
}
return 0;
}
private static String getTextObject(Rect rect, LittleEndianDataInputStream fp)
throws IOException
{
String s = null;
byte[] buff = new byte[1024];
byte c;
short t;
fp.in.mark(1);
while ((c = fp.readByte()) != -1) {
switch (c) {
case $Obj_coords: //$Obj_coords
t = fp.readShort();
rect.left = fp.readShort();
rect.top = fp.readShort();
rect.right = fp.readShort();
rect.bottom = fp.readShort();
break;
case $MDLEditText: //$MDLEditText
//TRACE("CODE %u ",(unsigned int)c);
t = fp.readShort();
//fp.readFully(buff,0,t-2);
s = getMDLText(fp);
break;
case $Transparent:
case $Crop_coords:
case $RxnAtch:
case $Pen_RGB2Color:
//TRACE("CODE %u ",(unsigned int)c);
t = fp.readShort();
fp.readFully(buff, 0, t - 2);
break;
default:
//TRACE("End of Text...\n");
// fp.PutBack(1);
// return nFound;
fp.in.reset();
return s;
}
}
return s;
}
private static String getMDLText(LittleEndianDataInputStream fp)
throws IOException
{
byte[] buff = new byte[1024];
byte[] text = null;
short nNumChar;
fp.readShort(); // len
fp.readFully(buff, 0, 12); // ignore 12 bytes
nNumChar = fp.readShort();
fp.readShort(); //ignore //2
fp.readShort(); //ignore num styles //2
fp.readShort(); //ignore //2
fp.readInt(); //ignore outline //4
fp.readShort(); //ignore //2
fp.readFully(buff, 0, 32); // ignore 32 bytes Font //32
fp.readByte(); //ignore font styles //1
fp.readShort(); //ignore Font size; //2
text = new byte[nNumChar];
for (int i = 0; i < nNumChar; i++) {
text[i] = fp.readByte();
fp.readByte(); // Jump over the attribute!!
}
return new String(text, StandardCharsets.UTF_8);
}
private static boolean getArrowObject(Arrow arrow, LittleEndianDataInputStream fp)
throws IOException
{
int maxcount = 12;
int count = 0;
boolean bFound = false;
byte c;
short t;
byte[] buff = new byte[1024];
fp.in.mark(1);
while ((c = fp.readByte()) != -1) {
switch (c) {
case $Obj_coords: //$Obj_coords
t = fp.readShort();
//fp.BufRead(&rc,sizeof(Rect));
arrow.left = fp.readShort();
arrow.top = fp.readShort();
arrow.right = fp.readShort();
arrow.bottom = fp.readShort();
bFound = true;
break;
case $Pen_width:
case $Crop_coords:
case $ArrowDir:
case $ArrowStyle:
case $Pen_RGB2Color:
case $Pen_style_token:
case $ArrowType:
case $ArrowSize:
case $ArrowShaftSp:
case $Pen_widthUnit:
t = fp.readShort();
fp.readFully(buff, 0, t - 2);
break;
case $RxnAtch:
t = fp.readShort();
arrow.attach[0] = fp.readShort();
arrow.attach[1] = fp.readShort();
break;
default:
//TRACE("End of Arrow...\n");
fp.in.reset();
return bFound;
}
if (++count >= maxcount) {
return bFound;
}
}
return bFound;
}
private static boolean getLineArrowObject(Arrow arrow, LittleEndianDataInputStream fp)
throws IOException
{
int maxcount = 13;
int count = 0;
boolean bFound = false;
byte c;
short t;
byte[] buff = new byte[1024];
fp.in.mark(1);
while ((c = fp.readByte()) != -1) {
switch (c) {
case $Obj_coords: //$Obj_coords
t = fp.readShort();
//fp.BufRead(&rc,sizeof(Rect));
arrow.left = fp.readShort();
arrow.top = fp.readShort();
arrow.right = fp.readShort();
arrow.bottom = fp.readShort();
bFound = true;
break;
case $Pen_width:
case $Fill_style:
case $ArrowDir:
case $ArrowStyle:
case $Pen_style_token:
case $Pen_RGB2Color:
case $Fill_RGB2Color:
case $ArrowType:
case $ArrowSize:
case $ArrowShaftSp:
case $Pen_widthUnit:
t = fp.readShort();
fp.readFully(buff, 0, t - 2);
break;
case $RxnAtch:
t = fp.readShort();
arrow.attach[0] = fp.readShort();
arrow.attach[1] = fp.readShort();
break;
default:
//TRACE("End of Arrow...\n");
fp.in.reset();
return bFound;
}
if (++count >= maxcount) {
return bFound;
}
}
return bFound;
}
// Write interface
private static int writeSketchHeader(LittleEndianDataOutputStream fp, int nObjects)
throws IOException
{
byte c;
writeProperty(fp, $Version, (byte)4); // $Version
writeProperty(fp, $Totobjs, (short)nObjects); // $TotObjects
Write97(fp);
c = $Pen_RGB2Color; // $Pen_RGB2Color
fp.writeByte(c);
fp.writeShort((short)(3 + WORDSIZE));
c = 0;
fp.writeByte(c);
fp.writeByte(c);
fp.writeByte(c);
c = $Fill_RGB2Color; // $Fill_RGB2Color
fp.writeByte(c);
fp.writeShort((short)(3 + WORDSIZE));
c = 0;
fp.writeByte(c);
fp.writeByte(c);
fp.writeByte(c);
//System.out.println("@TODO: The font handling in the sketch needs a fix");
int nLen = DefaultFontName.length();
fp.writeByte($Font);
fp.writeShort((short)(nLen + 4 + WORDSIZE)); // len + sizeof(byte) + sizeof(short) + sizeof(byte)
// string size + length byte + font size + emphasis
fp.writeByte((byte)nLen);
fp.writeBytes(DefaultFontName);
fp.writeShort((short)120);
fp.writeByte((byte)0);
return 1;
}
private static int writeProperty(LittleEndianDataOutputStream fp, byte c, byte d)
throws IOException
{
fp.writeByte(c);
fp.writeShort((short)(1 + WORDSIZE)); // sizeof(byte)
fp.writeByte(d);
return 4;
}
private static int writeProperty(LittleEndianDataOutputStream fp, byte c, String sz)
throws IOException
{
int nLen = sz.length();
fp.writeByte(c);
fp.writeShort((short)(nLen + 1 + WORDSIZE)); // sizeof(byte) + strlen()
fp.writeByte((byte)nLen);
fp.writeBytes(sz);
return nLen + 5; // 3 byte + 1 short
}
private static int writeProperty(LittleEndianDataOutputStream fp, byte c, short d)
throws IOException
{
fp.writeByte(c);
fp.writeShort((short)(WORDSIZE + WORDSIZE)); // sizeof(short)
fp.writeShort(d);
return 5; // 2 short + byte
}
private static int writeProperty(LittleEndianDataOutputStream fp, byte c, DblPoint d)
throws IOException
{
fp.writeByte(c);
fp.writeShort((short)(8 + WORDSIZE)); // 4 byte per float
fp.writeFloat(d.x);
fp.writeFloat(d.y);
return 8 + 3;
}
private static int writeProperty(LittleEndianDataOutputStream fp, byte c, Rect r)
throws IOException
{
fp.writeByte(c);
fp.writeShort((short)((4 * WORDSIZE) + WORDSIZE)); // sizeof(Rect) = 4 * short
fp.writeShort(r.left);
fp.writeShort(r.top);
fp.writeShort(r.right);
fp.writeShort(r.bottom);
return 8 + 3;
}
private static int writeProperty(LittleEndianDataOutputStream fp, byte c)
throws IOException
{
fp.writeByte(c);
fp.writeShort((short)WORDSIZE);
return 3;
}
private static int Write97(LittleEndianDataOutputStream fp)
throws IOException
{
char c = 97;
fp.writeByte((byte)c);
fp.writeShort((short)3);
c = 0;
fp.writeByte((byte)c);
return 4;
}
private static void translateMolecule(Molecule m)
{
Rect2D rc = getBoundingRectangle(m);
double dx = 0;
double dy = 0;
if (rc.left < 0) {
dx = 0 - rc.left;
}
if (rc.top < 0) {
dy = 0 - rc.top;
}
translateMolecule(m, dx, dy);
}
private static void translateMolecule(Molecule m, double dx, double dy)
{
int atoms = m.getAllAtoms();
debug("Translate molecule " + dx + " " + dy);
for (int i = 0; i < atoms; i++) {
m.setAtomX(i, m.getAtomX(i) + dx);
m.setAtomY(i, m.getAtomY(i) + dy);
}
}
private static void translateReaction(Reaction m)
{
Rect2D rc = getBoundingRectangle(m);
double dx = 0;
double dy = 0;
if (rc.left < 0) {
dx = 0 - rc.left;
}
if (rc.top < 0) {
dy = 0 - rc.top;
}
int mols = m.getMolecules();
for (int i = 0; i < mols; i++) {
translateMolecule(m.getMolecule(i),dx,dy);
}
}
private static void scaleMolecule(Molecule m, int maxsize)
{
Rect2D rc = getBoundingRectangle(m);
double dx = Math.abs(rc.right - rc.left);
double dy = Math.abs(rc.bottom - rc.top);
double scale = (double)maxsize / Math.max(dx, dy);
scaleMolecule(m, scale);
}
private static void scaleMolecule(Molecule m, double scale)
{
int atoms = m.getAllAtoms();
for (int i = 0; i < atoms; i++) {
m.setAtomX(i, m.getAtomX(i) * scale);
m.setAtomY(i, m.getAtomY(i) * scale);
m.setAtomZ(i, m.getAtomZ(i) * scale);
}
}
private static double getScaleFactor(Reaction m)
{
Rect2D rc = getBoundingRectangle(m);
double scale = REACTIONSIZE / (rc.right - rc.left);
return scale;
}
private static void scaleReaction(Reaction m, double scale)
{
debug("Scaling Reaciton at " + scale);
int mols = m.getMolecules();
for (int i = 0; i < mols; i++) {
scaleMolecule(m.getMolecule(i),scale);
}
}
public static byte[] createSketchFromMol(Molecule m)
{
byte[] buffer = null;
StereoMolecule mol = new StereoMolecule(m);
try {
if (mol.getAllAtoms() > 0) {
ByteArrayOutputStream b = new ByteArrayOutputStream();
LittleEndianDataOutputStream fp = new LittleEndianDataOutputStream(b);
// Rect rc = new Rect((short)0,(short)0,(short)2000,(short)2000);
writeSketchHeader(fp, 1);
translateMolecule(mol);
scaleMolecule(mol, MOLSIZE);
writeMoleculeToSketch(fp, mol, 1);
writeProperty(fp, $Endsketch); // EndSketch
buffer = b.toByteArray();
}
} catch (IOException e) {
System.err.println("Sketch.SketchFromMol() " + e);
}
return buffer;
}
public static byte[] createSketchFromReaction(Reaction r)
throws IOException
{
Reaction rxn = new Reaction(r);
int reactants = rxn.getReactants();
int prods = rxn.getProducts();
int cnt = prods + reactants;
ByteArrayOutputStream b = new ByteArrayOutputStream();
LittleEndianDataOutputStream fp = new LittleEndianDataOutputStream(b);
translateReaction(rxn);
double scale = getScaleFactor(rxn);
scaleReaction(rxn,scale);
writeSketchHeader(fp, ((cnt + reactants) - 1 + prods) - 1 + 1);
for (int i = 0; i < reactants; i++) {
Molecule mol = rxn.getMolecule(i);
Rect rc = getBoundingRect(mol);
debug("Drawing reactant " + i + " @ " + rc.left + "," + rc.top + "," + rc.right + "," + rc.bottom);
writeMoleculeToSketch(fp, mol, (short)-1, (short)-1, 1);
if (i < (reactants - 1)) {
Rect rn = getBoundingRect(rxn.getMolecule(i + 1));
Rect rp = calcMiddleRect(rc, rn, PLUSSIZE);
debug("Writing plus at " +
rp.left + "," + rp.top + "," + rp.right + "," + rp.bottom);
writePlus(fp, rp, -1, -1);
}
}
if ((reactants > 0) && (prods > 0)) {
Rect rr = getBoundingRect(rxn.getMolecule(reactants - 1));
Rect rp = getBoundingRect(rxn.getMolecule(reactants));
Rect ra = calcMiddleRect(rr, rp, 1);
int dx = (rp.left - rr.right) / 4;
ra.left -= dx;
ra.right += dx;
writeArrowObject(fp, ra, -1, -1);
}
for (int i = reactants; i < cnt; i++) {
Molecule mol = rxn.getMolecule(i);
Rect rc = getBoundingRect(mol);
debug("Drawing prod " + i + " @ " + rc.left + "," + rc.top + "," + rc.right + "," + rc.bottom);
writeMoleculeToSketch(fp, mol, (short)-1, (short)-1, 1);
if (i < (cnt - 1)) {
Rect rn = getBoundingRect(rxn.getMolecule(i + 1));
Rect rp = calcMiddleRect(rc, rn, PLUSSIZE);
writePlus(fp, rp, -1, -1);
}
}
writeProperty(fp, $Endsketch); // EndSketch
return b.toByteArray();
}
public static byte[] createSketchFromReactionOLd(Reaction rxn)
throws IOException
{
int reactants = rxn.getReactants();
int prods = rxn.getProducts();
int cnt = prods + reactants;
float factor;
ByteArrayOutputStream b = new ByteArrayOutputStream();
LittleEndianDataOutputStream fp = new LittleEndianDataOutputStream(b);
// for (int i = 0; i < cnt ; i++) {
// Molecule mol = rxn.getMolecule(i);
// scaleMolecule(mol,MOLSIZE/cnt);
// }
Rect2D rxnRect = getBoundingRectangle(rxn);
factor = (float)(rxnRect.right - rxnRect.left);
factor = 5000 / factor;
writeSketchHeader(fp, ((cnt + reactants) - 1 + prods) - 1 + 1);
for (int i = 0; i < reactants; i++) {
Molecule mol = rxn.getMolecule(i);
Rect rc = getBoundingRect(mol);
writeMoleculeToSketch(fp, mol, (short)-1, (short)-1, factor);
if (i < (reactants - 1)) {
Rect rn = getBoundingRect(rxn.getMolecule(i + 1));
Rect rp = calcMiddleRect(rc, rn, 5);
scaleRect(rp, factor);
writePlus(fp, rp, -1, -1);
}
}
if ((reactants > 0) && (prods > 0)) {
Rect rr = getBoundingRect(rxn.getMolecule(reactants - 1));
Rect rp = getBoundingRect(rxn.getMolecule(reactants));
Rect ra = calcMiddleRect(rr, rp, 1);
int dx = (rp.left - rr.right) / 4;
ra.left -= dx;
ra.right += dx;
scaleRect(ra, factor);
writeArrowObject(fp, ra, -1, -1);
}
for (int i = reactants; i < cnt; i++) {
Molecule mol = rxn.getMolecule(i);
Rect rc = getBoundingRect(mol);
writeMoleculeToSketch(fp, mol, (short)-1, (short)-1, factor);
if (i < (cnt - 1)) {
Rect rn = getBoundingRect(rxn.getMolecule(i + 1));
Rect rp = calcMiddleRect(rc, rn, 5);
scaleRect(rp, factor);
writePlus(fp, rp, -1, -1);
}
}
writeProperty(fp, $Endsketch); // EndSketch
return b.toByteArray();
}
private static void scaleRect(Rect rc, double scale)
{
rc.left *= scale;
rc.top *= scale;
rc.right *= scale;
rc.bottom *= scale;
}
private static int writeMoleculeToSketch(
LittleEndianDataOutputStream fp, Molecule mol, double scale)
throws IOException
{
writeProperty(fp, $Obj, (byte)$Obj_Mol); // Mol Object
writeSubSketch(fp, mol, scale);
return 1;
}
private static int writeMoleculeToSketch(
LittleEndianDataOutputStream fp, Molecule mol, short wFrom, short wTo, double scale)
throws IOException
{
byte c;
writeProperty(fp, $Obj, (byte)$Obj_Mol); // Mol Object
c = $RxnAtch; // RxnAtch
fp.writeByte(c);
fp.writeShort((short)((2 * 2) + 2));
fp.writeShort(wFrom);
fp.writeShort(wTo);
writeSubSketch(fp, mol, scale);
return 1;
}
private static void writeSubSketch(LittleEndianDataOutputStream fp, Molecule mol, double scale)
throws IOException
{
int nAtoms = mol.getAllAtoms();
int nBonds = mol.getAllBonds();
byte c;
writeProperty(fp, $Begsketch); // BeginSketch
writeProperty(fp, $Totobjs, (short)(nAtoms + nBonds)); // $TotObjects
Write97(fp);
for (int i = 0; i < nAtoms; i++) {
writeAtom(fp, mol, i, scale);
if (mol.getAtomCharge(i) != 0) {
writeAtomCharge(fp, mol, i);
}
if (!mol.isNaturalAbundance(i)) {
writeAtomIsotope(fp, mol, i);
}
if (mol.getAtomRadical(i) != 0) {
writeAtomRadical(fp, mol, i);
}
if (mol.getAtomList(i) != null) {
writeAtomList(fp, mol, i);
}
}
for (int j = 0; j < nBonds; j++) {
writeProperty(fp, $Obj, (byte)$Obj_Bond); // Bond Object
writeProperty(fp, $Pen_width, (byte)7); // Pen Width
c = $Bond_atoms; // Bond_Atoms
fp.writeByte(c);
fp.writeShort((short)((2 * 2) + 2));
fp.writeShort((short)mol.getBondAtom(0, j));
fp.writeShort((short)mol.getBondAtom(1, j));
writeProperty(fp, $Bond_type, (byte)getMDLBondType(mol.getBondType(j), false)); // Bond Type
writeProperty(fp, $Bond_stereo_type, (byte)getMDLBondType(mol.getBondType(j), true)); // Bond Stereo
}
if (mol.getChirality() != Molecule.cChiralityNotChiral
&& mol.getChirality() != Molecule.cChiralityMeso
&& mol.getChirality() != Molecule.cChiralityRacemic
&& mol.getChirality() != Molecule.cChiralityUnknown) {
writeChiral(fp, mol, scale);
}
writeProperty(fp, $Endsketch); // EndSketch
}
private static void writeAtom(
LittleEndianDataOutputStream fp, Molecule mol, int atom, double scale)
throws IOException
{
DblPoint pt = new DblPoint();
pt.x = (float)(mol.getAtomX(atom) * scale);
pt.y = (float)(mol.getAtomY(atom) * scale);
// debug("W # " + atom + " " + mol.getAtomX(atom) + " " + mol.getAtomY(atom));
writeProperty(fp, $Obj, (byte)$Obj_Atom); // Atom Object
writeProperty(fp, $Atom_coords, pt); // Coords
writeProperty(fp, $Atom_symbol, mol.getAtomLabel(atom)); // Symbol
writeProperty(fp, $Atom_number, (short)(atom + 1)); // Atom Number within Molecule
writeProperty(fp, $Atom_dispflags, (short)0x0004); // Disp Flags
writeProperty(fp, $Atom_margin_width, (short)15); // Atom Space
if (mol.getAtomMapNo(atom) != 0)
writeProperty(fp, $Atom_aamap_num, (short)mol.getAtomMapNo(atom)); // Atom-Atom Map
}
private static void writeChiral(LittleEndianDataOutputStream fp, Molecule mol, double scale)
throws IOException
{
Rect r = getBoundingRect(mol);
scaleRect(r, scale);
writeProperty(fp, $Obj, (byte)$Obj_Chiral);
r.left = (short)(r.right + 10);
r.right = (short)(r.left + 300);
r.top -= (short)50;
r.bottom = (short)(r.top + 40);
writeProperty(fp, $Obj_coords, r);
}
private static void writeAtomCharge(LittleEndianDataOutputStream fp, Molecule mol, int atom)
throws IOException
{
int charge = mol.getAtomCharge(atom);
writeProperty(fp, $Atom_chg, (byte)(charge + 16)); // Atom charge
}
private static void writeAtomIsotope(LittleEndianDataOutputStream fp, Molecule mol, int atom)
throws IOException
{
int mass = mol.getAtomMass(atom);
int atomicNo = mol.getAtomicNo(atom);
int diff = mass - Molecule.cRoundedMass[atomicNo];
int iso = diff + MSDIFF_OFFSET;
writeProperty(fp, $Atom_msdif, (byte)(iso)); // Atom isotope
}
private static void writeAtomRadical(LittleEndianDataOutputStream fp, Molecule mol, int atom)
throws IOException
{
int rad = 0;
switch (mol.getAtomRadical(atom)) {
case Molecule.cAtomRadicalStateS:
rad = 1;
break;
case Molecule.cAtomRadicalStateD:
rad = 2;
break;
case Molecule.cAtomRadicalStateT:
rad = 3;
break;
default:
break;
}
writeProperty(fp, $Atom_rad, (byte)(rad)); // Atom radical
}
private static void writeAtomList(LittleEndianDataOutputStream fp, Molecule mol, int atom)
throws IOException
{
int[] atomList = mol.getAtomList(atom);
if (atomList != null) {
boolean notlist = (mol.getAtomQueryFeatures(atom) & Molecule.cAtomQFAny) == Molecule.cAtomQFAny;
int size = atomList.length;
if (size > 0) {
writeProperty(fp, $Atom_type, (short)(notlist ? 271 : 270));
writeProperty(fp, $Atom_list, (byte)size);
for (int i = 0; i < size; i++) {
int atomno = atomList[i];
fp.writeShort(atomno);
}
}
}
}
private static int writeArrowObject(
LittleEndianDataOutputStream fp, Rect coords, int wFrom, int wTo)
throws IOException
{
// Write Object Type
writeProperty(fp, $Obj, (byte)4); // Arrow Object
// Write Pen Width
writeProperty(fp, $Pen_width, (byte)7); // Pen Width
// Write Arrow Direction
writeProperty(fp, $ArrowDir, (byte)1);
// Write Arrow Style
writeProperty(fp, $ArrowStyle, (byte)1);
// Write Arrow Size
fp.writeByte($ArrowSize);
fp.writeShort(((2 * WORDSIZE) + (2 * BYTESIZE) + WORDSIZE)); // word + word + byte + byte
fp.writeShort(10);
fp.writeShort(10);
fp.writeByte(2);
fp.writeByte(2);
// Write RxnAttch
fp.writeByte($RxnAtch);
fp.writeShort(((2 * WORDSIZE) + WORDSIZE));
fp.writeShort(wFrom);
fp.writeShort(wTo);
// Write Object Coords
writeProperty(fp, $Obj_coords, coords);
return 1;
}
static int writeMDLText(LittleEndianDataOutputStream buf, String szSource)
throws IOException
{
int i;
int nFontLen = DefaultFontName.length();
int TextLen = szSource.length();
int TotalLen = (TextLen * 2) + 63;
buf.writeShort((TotalLen - 2));
for (i = 0; i < 12; i++)
buf.writeByte(0);
buf.writeShort(TextLen);
buf.writeShort(0);
buf.writeShort(1);
buf.writeShort(0);
buf.writeInt(0xC000); // fixed
buf.writeShort(0x11); // Left | Top
buf.writeBytes(DefaultFontName); //
for (i = 0; i < (32 - nFontLen); i++)
buf.writeByte(0);
buf.writeByte(0); // Normal
buf.writeShort(120); // Size
for (i = 0; i < TextLen; i++) {
buf.writeByte((byte)szSource.charAt(i));
buf.writeByte(0);
}
return TotalLen;
}
static int writePlus(LittleEndianDataOutputStream fp, Rect rect, int wFrom, int wTo)
throws IOException
{
String s = "+";
// LPSTR szMDLText = new char [strlen(s) * 2 + 100];
// int iTextLen = CreateMDLText(fp,s);
int TextLen = s.length();
int TotalLen = (TextLen * 2) + 63;
// Write Object Type
writeProperty(fp, $Obj, (byte)9); // Text
// Write Transparent Tag
writeProperty(fp, $Transparent); // $Transparent
// Write RxnAttch
fp.writeByte($RxnAtch);
fp.writeShort(((2 * WORDSIZE) + WORDSIZE));
fp.writeShort(wFrom);
fp.writeShort(wTo);
// Write ObjectCoords
writeProperty(fp, $Obj_coords, rect);
// $Mdl Text
fp.writeByte($MDLEditText);
fp.writeShort((TotalLen + 2));
writeMDLText(fp, s);
// fp.BufWrite((void *)szMDLText,iTextLen);
// delete [] szMDLText;
return 0;
}
private static Rect calcMiddleRect(Rect rc, Rect rn, int size)
{
Rect rp = new Rect(rc.right, rc.top, rn.left, rc.bottom);
Point p = getCenter(rp);
return new Rect(
(short)(p.x - (size / 2)), (short)(p.y - (size / 2)), (short)(p.x + (size / 2)),
(short)(p.y + (size / 2)));
}
static int getMDLBondType(int btype, boolean bstereo)
{
int order = 1;
int stereo = 0;
switch (btype) {
case Molecule.cBondTypeSingle:
order = 1;
stereo = 0;
break;
case Molecule.cBondTypeDouble:
order = 2;
stereo = 0;
break;
case Molecule.cBondTypeTriple:
order = 3;
stereo = 0;
break;
case Molecule.cBondTypeDown:
order = 1;
stereo = 6;
break;
case Molecule.cBondTypeUp:
order = 1;
stereo = 1;
break;
/* case Molecule.cBondTypeMix: // discontinued bond type
order = 1;
stereo = 4;
break; */
case Molecule.cBondTypeCross:
order = 2;
stereo = 3;
break;
case Molecule.cBondTypeDelocalized:
order = 4;
stereo = 0;
break;
default:
order = 1;
stereo = 0;
break;
}
if (bstereo) {
return stereo;
} else {
return order;
}
}
private static void debug(String s)
{
if (debug_)
System.out.println(s);
}
}
class DblPoint
{
float x;
float y;
}
class Rect
{
public Rect(short l, short t, short r, short b)
{
left = l;
top = t;
right = r;
bottom = b;
}
public Rect()
{
}
short left;
short top;
short right;
short bottom;
}
;
class Rect2D
{
public Rect2D(double l, double t, double r, double b)
{
left = l;
top = t;
right = r;
bottom = b;
}
public Rect2D()
{
}
double left;
double top;
double right;
double bottom;
}
;
class Arrow extends Rect
{
int[] attach = new int[2];
}