HTML App to Colorize Black & White Images


I had previously posted a file in which a b&w image was colorized with jsDraw2d. That, however was an ad hoc file for just that one image.

This is a general app that can easily do it for all b&w (grayscale) images.

It can also be used for colored images, in which case colors can be modified, as well as allowing for freehand drawing.

This is the Control Panel:

Here is the image with selection points visible:

with the skin colored:

and with the lips colored:

It is of course personal, but I think an opacity of .3 to .4 works best.

Since the color is blended, it is better to have more saturation than you would want in the final product.

If any step is unsatisfactory, an Undo button reverts the drawing.

Clicking load with a new image chosen changes the embedded image and clicking load with Freehand checked removes the image to allow drawing rather then tracing.

Here is the code with most of the point arrays removed, since they would add length to this post, with no enlightenment:

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”&gt;
<html xmlns=”http://www.w3.org/1999/xhtml”&gt;
<head profile=”http://gmpg.org/xfn/11″&gt;
<title>Colorize</title>
<style>
body {margin-left:0;margin-right:0;font:normal normal normal 15px Arial;}
a{ text-decoration: }:link { color: rgb(0, 0, 255) }:visited {color :rgb(100, 0,100) }:hover { }:active { }
#frame, #frame2{position: absolute; left:0 ; top:30px ; width:100% ; height: 650px; }
#form1 {position: absolute; width: 1000px; left: 50%; margin-left: -500px; height: 30px}
#files{position: relative; width: 100px; font:normal normal 700 15px Arial}
#limt, #pts, #opac{position: relative; width: 25px; font:normal normal 700 15px Arial}
#opac{width: 40px}
</style>
</head>
<body>
<form id=”form1″ >
<center><input type=”button” id=”b1″ name=”b1″ value=”New” onclick=’nw();’/>
<input type=”button” id=”b3″ name=”b3″ value=”Undo” onclick=’document.getElementById(“frame”).innerHTML = bk;’ />
<input type=”file” id=”files” name=”files” />
<input type=”button” id=”b2″ name=”b2″ value=”Load” onclick=’ld();’ />
<input type=’color’ id=’color’ value= simple color />
# pts
<input id = “limt” type=”text” name=”limt” value=”0″ />
<input id = “pts” type=”text” name=”pts” value=”” />
<input id = “opac” type=”text” name=”opac” value=”Opac” />
<input type=”checkbox” id=”ch1″ style = “width: 15px” name=”ch1″ value=” ” />Freehand
</center>
</form>
<di id=’frame’><img id=”img1″ style=”position: absolute; top: 40px” src= “Karen3.jpg”height=”600px” /> </di>
<di id=’frame2′></di>
<di id = “instruc” style= “position:absolute ; width:500px ; height: ; left:50% ; margin-left: -250px; top: ; font: normal normal 600 15px Arial; color: #000000; background: white; border: 3px solid black; padding: 10px” onclick=’document.getElementById(“instruc”).style.visibility=”hidden”;’>
<center>Click Here to Close This Box</center><br />
If you do not want the default image, choose and load an image from the same folder as the app<br />
Choose the number of points, opacity, color<br />
For a Circle set the point number at 2<br />
Trace part of the image with clicks. The number of expired clicks will be indicated and dots will be drawn. When the entered number is reached, the selection will be filled or drawn and the dots will be removed<br />
Click New to start a new area<br />
Checking Freehand and clicking Load removes the image<br />
If not satisfied with any step click Undo<br />
</di>

http://jsDraw2D.js

if (document.getElementById(“ch1”).checked ) document.getElementById(“frame”).innerHTML = “”;
var IE = document.all?true:false;
document.onclick = getMouse2;
var tempX = 0;
var tempY = 0;
var lim = 0;
var xoff = 0;
var yoff = 0;
var cl = “”;
var cnt = 0; var cnt2 = 0;
var fname = “”;
var tmpstr = “”;
var px = new Array(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
var py = new Array(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
var xmin; var xmax; var ymin; var ymax; var el; var done = false; var bk = document.getElementById(“frame”).innerHTML;
var str2 = ‘ ‘; var bk2 = “”;
var str2a = ‘ ‘;
var gr= new jsGraphics(document.getElementById(“frame2”));

function nw() {
tempX = 0; tempY = 0; lim = 0; xoff = 0; yoff = 0; cl = “”; done = false; cnt = 0; fname = “”; tmpstr = “”; px = new Array(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); py = new Array(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); xmin = 0; xmax = 0; ymin = 0; ymax = 0;
document.getElementById(“limt”).value = “0”; document.getElementById(“pts”).value = “0”;
for (var i = 1; i -1) fil = fil.substr(12);
document.getElementById(“frame”).innerHTML = ‘‘;
if (document.getElementById(“ch1”).checked ) document.getElementById(“frame”).innerHTML = “”;
}

function getMouse2(e) {
cl = document.getElementById(“color”).value;

if (cnt 50) {
cnt ++;
document.getElementById(“pts”).value = cnt;
px[cnt] = tempX;
py[cnt]= tempY;
gr.fillCircle(new jsColor(“blue”),new jsPoint(px[cnt],py[cnt] – 30) , 3);

if (cnt == 1) {
xmin = px[1];
xmax = px[1];
ymin = py[1];
ymax = py[1];
}
if (cnt > 1) {
if (px[cnt] xmax) xmax = px[cnt];
if (py[cnt] ymax) ymax = py[cnt];
}
}

if (cnt == lim && cnt >= 2 && ! done) {
bk = document.getElementById(“frame”).innerHTML;
cnt2 ++;
var wd = xmax – xmin;
var ht = ymax – ymin;

for (var i = 1; i ‘;

gr[el2] = new jsGraphics(document.getElementById(“canvas”+ cnt2. toString() ));

if (cnt == 4) var points = new Array(new jsPoint( px[1], py[1]), new jsPoint( px[2], py[2]), new jsPoint( px[3], py[3]), new jsPoint( px[4], py[4]));

if (cnt == 30) var points = new Array(new jsPoint( px[1], py[1]), new jsPoint( px[2], py[2]), new jsPoint( px[3], py[3]), new jsPoint( px[4], py[4]), new jsPoint( px[5], py[5]), new jsPoint( px[6], py[6]), new jsPoint( px[7], py[7]), new jsPoint( px[8], py[8]), new jsPoint( px[9], py[9]), new jsPoint( px[10], py[10]), new jsPoint( px[11], py[11]), new jsPoint( px[12], py[12]), new jsPoint( px[13], py[13]), new jsPoint( px[14], py[14]), new jsPoint( px[15], py[15]), new jsPoint( px[16], py[16]), new jsPoint( px[17], py[17]), new jsPoint( px[18], py[18]), new jsPoint( px[19], py[19]), new jsPoint( px[20], py[20]), new jsPoint( px[21], py[21]), new jsPoint( px[22], py[22]), new jsPoint( px[23], py[23]), new jsPoint( px[24], py[24]), new jsPoint( px[25], py[25]), new jsPoint( px[26], py[26]), new jsPoint( px[27], py[27]), new jsPoint( px[28], py[28]), new jsPoint( px[29], py[29]), new jsPoint( px[30], py[30]));

gr[el2].fillClosedCurve(new jsColor(cl), points) ;
document.getElementById(“frame2”).innerHTML = “”;
done = true;
}
}

</body></html>


The app uses a listener to get get the screen location of clicks:
if (IE) {
tempX = event.clientX + document.body.scrollLeft;
tempY = event.clientY + document.body.scrollTop;
}
else {
tempX = e.pageX;
tempY = e.pageY;
}
if (tempX < 0){tempX = 0;}
if (tempY < 0){tempY = 0;}

Every time the screen is clicked, a variable cnt is incremented and its value placed in a box. This is how the number of clicks is tracked:
if (tempY > 50) {
cnt ++;
document.getElementById(“pts”).value = cnt;

also, the values of arrays px and py are set:
px[cnt] = tempX;
py[cnt]= tempY;

A small dot is placed at the point of the click:
var gr= new jsGraphics(document.getElementById(“frame2”));
gr.fillCircle(new jsColor(“blue”),new jsPoint(px[cnt],py[cnt] – 30) , 3);

There was a problem at this point.
I wanted the dots removed on filling the selections, but the method usually used would not work. Since the dots are drawn before the area is filled, a reversion to an earlier version would either remove the fill as well as the dots, or leave the dots and remove only the fill.

I resolved this by creating an identical background free layer on top of the base layer, drawing the dots in this layer. When the base layer is filled with a tinting layer, the dots can be removed from the second layer without affecting the drawing:
gr[el2].fillClosedCurve(new jsColor(cl), points) ;
document.getElementById(“frame2”).innerHTML = “”;

As in the previous colorizing post, each fill needed its own layer because of the transparency requirement.

The coordinates for the dots are drawn in the space of the base layer, while the fills require daughter layer coordinates.

A method to convert from one space to another was needed. This is how it was done:
if (cnt == 1) {
xmin = px[1];
xmax = px[1];
ymin = py[1];
ymax = py[1];
}

On the first click I set minimum and maximum x and y values.

if (cnt > 1) {
if (px[cnt] < xmin) xmin = px[cnt];
if (px[cnt] > xmax) xmax = px[cnt];
if (py[cnt] < ymin) ymin = py[cnt];
if (py[cnt] > ymax) ymax = py[cnt];
}

Each subsequent click did a comparison with the minimum and maximum values and replaced them if outside the limits.

if (cnt == lim && cnt >= 2 && ! done) {
cnt2 ++;
var wd = xmax – xmin;
var ht = ymax – ymin;

for (var i = 1; i <= lim; i ++) {
px[i] -= xmin;
py[i] -= (ymin + 30);
}

if (document.getElementById(“opac”).value != “Opac” ) var opc = document.getElementById(“opac”).value;
var el2 = cnt2 + 1;

document.getElementById(“frame”).innerHTML += ‘<di id = “canvas’ + cnt2 + ‘” style = “position: absolute; left:’ + xmin + ‘px; top:’ + ymin + ‘px; width:’ + wd + ‘px; height:’ + ht + ‘px; opacity:’ + opc + ‘” ></di> ‘;

If the number of clicks was reached, the layer dimensions were calculated:
var wd = xmax – xmin;
var ht = ymax – ymin;

the position of each click was placed in the daughter layer’s space:
for (var i = 1; i <= lim; i ++) {
px[i] -= xmin;
py[i] -= (ymin + 30);
}

and the layer was created with the assigned transparency.

A graphic had to be created for the new layer:
gr[el2] = new jsGraphics(document.getElementById(“canvas”+ cnt2. toString() ));

An array, points had to be created for the number of set clicks:
if (cnt == 4) var points = new Array(new jsPoint( px[1], py[1]), new jsPoint( px[2], py[2]), new jsPoint( px[3], py[3]), new jsPoint( px[4], py[4]));

and fillClosedCurve was used:
gr[el2].fillClosedCurve(new jsColor(cl), points) ;

Fly-In Transition with Image


I had previously a text fly-in transition. This post expands it to an image.

This is how it looks:

To try it click here

Here is the code:

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”&gt;
<html xmlns=”http://www.w3.org/1999/xhtml”&gt;
<head profile=”http://gmpg.org/xfn/11″&gt;
<title>test</title>
<style>
body {margin-left:0;margin-right:0;font:normal normal normal 15px Arial; background: #bbbbbb}
a{ text-decoration: }:link { color: rgb(0, 0, 255) }:visited {color :rgb(100, 0,100) }:hover { }:active { }

</style>
</head>
<body onload=’contrl();’>
<di id=”test” style= “position:absolute ; width: 100% ; height:100% ; ; top: ; “>
<img id= “im1” src= “images/aloe.png” style= “position:absolute ; width: 1px ; left: 100% ” />
</di>

var si; var sz = 1;

function contrl() {si = setInterval(“lrg()”, 50);}

function lrg() {
sz += 1;
if (sz
</body></html>


A timer calls the function ltg(), which resizes and moves the image:
function contrl() {si = setInterval(“lrg()”, 50);}

function lrg() {
sz += 1;
if (sz <= 100) {
document.getElementById(“im1”).style.width = (4*sz) + “px”;
document.getElementById(“im1”).style.left = (100 – sz/2 – sz/8) + “%”;
document.getElementById(“im1”).style.top = (sz/10) + “%”;
}
}

A HTML App to Draw Chemical Structures


My training was not in Computer Science.

My degrees are in Chemistry, and I have done both Chemical and Biochemical research.

I used to write articles on the Biochemistry of Nutrition, so it was handy to be able to draw chemical structures from time to time. I therefore wrote applications to draw chemical structures in both C and Java, which could enable me to work offline.

I therefore decided to create a html app which could draw structures on any pc or mobile device, either online or off.

The code is a little long to post (56KB), so if you want to see the code or try it here is a link

There is a Help button on the Control Panel if you need use instructions.

I will discuss selected sections of the code.

Here is how the Control Panel looks:

Here is something I was able to create with the app:

The code is largely object oriented.

The source for most rings is Cycxlohexane:


function cyclohexane() {
txtold = document.getElementById(“svg1”).innerHTML;
if (document.getElementById(“iniang”).value == “f”) {
cnt2 ++;
if (cnt2 == 1) {
px[1] = posX;
py[1] = posY – 10;
}
if (cnt2 == 2) {
px[2] = posX;
py[2] = posY – 10;
lngth = Math.pow (py[2] – py[1],2);
lngth += Math.pow (px[2] – px[1],2);
lngth = Math.sqrt(lngth);
ang = Math.asin( (py[2] – py[1]) / lngth);
if (prompt(“Do you want to add counterclockwise?”,”n”) == “y”) {
anginc = -Math.PI/3;
rev = true;
} else {
anginc = Math.PI/3;
}
for (var i = 3; i <= 6; i++) {
ang += anginc;
px[i] = Math.round(px[i-1] + (Math.cos(ang))* lngth);
py[i] = Math.round(py[i-1] + (Math.sin(ang))* lngth);
cx = (px[2] + px[5])/2;
cy = (py[2] + py[5])/2;
}
document.getElementById(“svg1″).innerHTML += ‘<polygon points=”‘ + px[1] + ‘,’ + py[1] + ‘ ‘ + px[2] + ‘,’ + py[2] + ‘ ‘ + px[3] + ‘,’ + py[3] + ‘ ‘ + px[4] + ‘,’ + py[4] + ‘ ‘ + px[5] + ‘,’ + py[5] + ‘ ‘ + px[6] + ‘,’ + py[6] +'” style=”fill:none;stroke:black;stroke-width:2 ; ” />’;
document.getElementById(“s1”).value = “Rings”;
}
} else {
if (document.getElementById(“iniang”).value != “initial angle”)ang = 2*Math.PI / parseFloat(document.getElementById(“iniang”).value);
if (! r2) {
ang -= Math.PI/3;
r2 = true;
}
anginc = Math.PI/3;
for (var i = 1; i <= 6; i++) {
ang += anginc;
px[i] = Math.round(cx+(Math.sin(ang))*lngth);
py[i] = Math.round(cy-(Math.cos(ang))*lngth);
}
document.getElementById(“svg1″).innerHTML += ‘<polygon points=”‘ + px[1] + ‘,’ + py[1] + ‘ ‘ + px[2] + ‘,’ + py[2] + ‘ ‘ + px[3] + ‘,’ + py[3] + ‘ ‘ + px[4] + ‘,’ + py[4] + ‘ ‘ + px[5] + ‘,’ + py[5] + ‘ ‘ + px[6] + ‘,’ + py[6] + ‘” style=”fill:none;stroke:black;stroke-width:2 ; ” />’;
}
}

Benzene is two concentric Cyclohexanes with alternating lines in the smaller ring:

function benzene() {
txtold = document.getElementById(“svg1”).innerHTML;
if (document.getElementById(“iniang”).value == “f”) {
cyclohexane();
if (cnt2 == 2) {

ang += Math.PI/6;
anginc = Math.PI/3;

for (var i = 1; i <= 6; i++) {
ang += anginc;
px[i] = Math.round(cx+(Math.sin(ang))* .8 * lngth);
py[i] = Math.round(cy-(Math.cos(ang))* .8 * lngth);
}
document.getElementById(“svg1″).innerHTML += ‘<polygon points=”‘ + px[1] + ‘,’ + py[1] + ‘ ‘ + px[2] + ‘,’ + py[2] + ‘ ‘ + px[3] + ‘,’ + py[3] + ‘ ‘ + px[4] + ‘,’ + py[4] + ‘ ‘ + px[5] + ‘,’ + py[5] + ‘ ‘ + px[6] + ‘,’ + py[6] + ‘” style=”fill:none;stroke:white;stroke-width:2 ; ” />’;
if (! rev) {
document.getElementById(“svg1”).innerHTML += ‘<line x1 = “‘ + px[3] + ‘” y1 = “‘ + py[3] + ‘” x2 = “‘ + px[4] + ‘” y2 = “‘ + py[4] + ‘” style=” stroke: black; stroke-width: 3″ />’;
document.getElementById(“svg1”).innerHTML += ‘<line x1 = “‘ + px[5] + ‘” y1 = “‘ + py[5] + ‘” x2 = “‘ + px[6] + ‘” y2 = “‘ + py[6] + ‘” style=” stroke: black; stroke-width: 3″ />’;
} else if (rev) {
document.getElementById(“svg1”).innerHTML += ‘<line x1 = “‘ + px[2] + ‘” y1 = “‘ + py[2] + ‘” x2 = “‘ + px[3] + ‘” y2 = “‘ + py[3] + ‘” style=” stroke: black; stroke-width: 3″ />’;
document.getElementById(“svg1”).innerHTML += ‘<line x1 = “‘ + px[4] + ‘” y1 = “‘ + py[4] + ‘” x2 = “‘ + px[5] + ‘” y2 = “‘ + py[5] + ‘” style=” stroke: black; stroke-width: 3″ />’;
}
}
} else {
cyclohexane();
anginc = Math.PI/3;
for (var i = 1; i <= 6; i++) {
ang += anginc;
px[i] = Math.round(cx+(Math.sin(ang))* .8 * lngth);
py[i] = Math.round(cy-(Math.cos(ang))* .8 * lngth);
}
document.getElementById(“svg1″).innerHTML += ‘<polygon points=”‘ + px[1] + ‘,’ + py[1] + ‘ ‘ + px[2] + ‘,’ + py[2] + ‘ ‘ + px[3] + ‘,’ + py[3] + ‘ ‘ + px[4] + ‘,’ + py[4] + ‘ ‘ + px[5] + ‘,’ + py[5] + ‘ ‘ + px[6] + ‘,’ + py[6] + ‘” style=”fill:none;stroke:black;stroke-width:2 ; ” />’;
document.getElementById(“svg1”).innerHTML += ‘<line x1 = “‘ + px[1] + ‘” y1 = “‘ + py[1] + ‘” x2 = “‘ + px[2] + ‘” y2 = “‘ + py[2] + ‘” style=” stroke: white; stroke-width: 3″ />’;
document.getElementById(“svg1”).innerHTML += ‘<line x1 = “‘ + px[3] + ‘” y1 = “‘ + py[3] + ‘” x2 = “‘ + px[4] + ‘” y2 = “‘ + py[4] + ‘” style=” stroke: white; stroke-width: 3″ />’;
document.getElementById(“svg1”).innerHTML += ‘<line x1 = “‘ + px[5] + ‘” y1 = “‘ + py[5] + ‘” x2 = “‘ + px[6] + ‘” y2 = “‘ + py[6] + ‘” style=” stroke: white; stroke-width: 3″ />’;
}
}

There are two methods of benzene creation:
If it is being fused to another ring, the junction points are clicked to set the angle of the first bond, which is the first part of the function.
If the Benzene is standing by itself, the second part is used.

This is Napthalene:

function naphthalene() {
txtold = document.getElementById(“svg1”).innerHTML;
tetralin();
anginc = Math.PI/3;
for (var i = 1; i <= 6; i++) {
ang += anginc;
px[i] = Math.round(cx+(Math.sin(ang))* .8 * lngth);
py[i] = Math.round(cy-(Math.cos(ang))* .8 * lngth);
}
document.getElementById(“svg1″).innerHTML += ‘<polygon points=”‘ + px[1] + ‘,’ + py[1] + ‘ ‘ + px[2] + ‘,’ + py[2] + ‘ ‘ + px[3] + ‘,’ + py[3] + ‘ ‘ + px[4] + ‘,’ + py[4] + ‘ ‘ + px[5] + ‘,’ + py[5] + ‘ ‘ + px[6] + ‘,’ + py[6] + ‘” style=”fill:none;stroke:white;stroke-width:2 ; ” />’;
document.getElementById(“svg1”).innerHTML += ‘<line x1 = “‘ + px[1] + ‘” y1 = “‘ + py[1] + ‘” x2 = “‘ + px[2] + ‘” y2 = “‘ + py[2] + ‘” style=” stroke: black; stroke-width: 3″ />’;
document.getElementById(“svg1”).innerHTML += ‘<line x1 = “‘ + px[3] + ‘” y1 = “‘ + py[3] + ‘” x2 = “‘ + px[4] + ‘” y2 = “‘ + py[4] + ‘” style=” stroke: black; stroke-width: 3″ />’;
}

function tetralin() {
txtold = document.getElementById(“svg1”).innerHTML;
benzene();
cx = cxhold + 1.732*lngth;
cxhold = cx;
cy = cyhold;
cyclohexane();
}

It is made from Tetralin which was made from Benzene and Cyclohexane.

This is Phenanthrene:

function phenanthrene() {
txtold = document.getElementById(“svg1”).innerHTML;
naphthalene();
r2 = false;
cx += .9*lngth;
cy -= 1.5*lngth;
cyclohexane();
anginc = Math.PI/3;
for (var i = 1; i <= 6; i++) {
ang += anginc;
px[i] = Math.round(cx+(Math.sin(ang))* .8 * lngth);
py[i] = Math.round(cy-(Math.cos(ang))* .8 * lngth);
}
document.getElementById(“svg1″).innerHTML += ‘<polygon points=”‘ + px[1] + ‘,’ + py[1] + ‘ ‘ + px[2] + ‘,’ + py[2] + ‘ ‘ + px[3] + ‘,’ + py[3] + ‘ ‘ + px[4] + ‘,’ + py[4] + ‘ ‘ + px[5] + ‘,’ + py[5] + ‘ ‘ + px[6] + ‘,’ + py[6] + ‘” style=”fill:none;stroke:white;stroke-width:2 ; ” />’;
document.getElementById(“svg1”).innerHTML += ‘<line x1 = “‘ + px[1] + ‘” y1 = “‘ + py[1] + ‘” x2 = “‘ + px[2] + ‘” y2 = “‘ + py[2] + ‘” style=” stroke: black; stroke-width: 3″ />’;
document.getElementById(“svg1”).innerHTML += ‘<line x1 = “‘ + px[3] + ‘” y1 = “‘ + py[3] + ‘” x2 = “‘ + px[4] + ‘” y2 = “‘ + py[4] + ‘” style=” stroke: black; stroke-width: 3″ />’;
}

made from Naphthalene and Cyclohexane.

Other basic objects are chair

and boat

forms of cyclohexane.

Adamantane:

function adamantane() {
txtold = document.getElementById(“svg1”).innerHTML;
chair();
px[7] = px[2];
py[7] = py[2] – 1.5*lngth;
document.getElementById(“svg1″).innerHTML += ”;
px[8] = px[6];
py[8] = py[7] + .75*lngth;
document.getElementById(“svg1″).innerHTML += ”;
ang = 6*Math.PI/24;
px[9] = Math.round(px[7]+(Math.sin(ang))*3*lngth);
py[9] = Math.round(py[7]-(Math.cos(ang))*lngth);
document.getElementById(“svg1″).innerHTML += ”;
document.getElementById(“svg1″).innerHTML += ”;
px[10] = px[4];
py[10] = (py[7] + py[8])/2;
document.getElementById(“svg1″).innerHTML += ”;
document.getElementById(“svg1″).innerHTML += ”;
}

which is made from the chair form

The following two are made from the boat form:

The following rings can be drawn with a single clicks:

Other ring systems must be made by combining existing ones.

Elements, such as N or O as well as numerous radicals can be added by clicking the insertion point with a selection from a second list:

A third list has side chains and bonds that can be added:

The Control Panel is hidden when a structure is drawn. To retrieve the Control Panel, the ESC key is pressed. Since this will not work for touchscreen devices, a “mobile” checkbox is provided. When a structure is drawn, if it is checked, the Control Panel disappears but a button pops up that serves the same function as the ESC key.

The Add Text button allows the addition of captioning.

If a mistake is made, Clicking Undo removes the last step. This is done in the following fashion:
function adamantane() {
txtold = document.getElementById(“svg1″).innerHTML;

The first line of every function has the same code, which places the innerHTML of the SVG in a variable, before any changes are made.

On clicking the Undo button, the innerHTML is set back to the text of that variable.

I mentioned there are two ways of creating rings, either ab initio or by fusing with an existing ring system.
The Fuse button toggles between the two methods. Clicking when it says Fuse, forces into the two click mode of fusion, and changes the button to New. Clicking when it says New reverses everything.

There are two ways of saving:
The Hide Grid button is also a toggle, alternating between hiding and showing the grid. Clicking it when it says Hide Grid, leaves a white background from which the ring structure can be made into an image with screen capture.

Alternately, clicking Save opens a file dialog in which the generating code can be saved as an html file:

<html><svg xmlns=”http://www.w3.org/2000/svg&#8221; version=”1.1″ id = “svg1″ width=”100%” height=”100%”>

<polygon points=”559,237 589,192 649,213 709,204 679,249 619,228″ style=”fill:none;stroke:black;stroke-width:2 ; “></polygon><line x1=”589″ y1=”192″ x2=”589″ y2=”147″ style=” stroke: black; stroke-width: 3″></line><line x1=”619″ y1=”228″ x2=”619″ y2=”169.5″ style=” stroke: black; stroke-width: 3″></line><line x1=”589″ y1=”147″ x2=”653″ y2=”126″ style=” stroke: black; stroke-width: 3″></line><line x1=”619″ y1=”169.5″ x2=”653″ y2=”126″ style=” stroke: black; stroke-width: 3″></line><line x1=”709″ y1=”158.25″ x2=”709″ y2=”204″ style=” stroke: black; stroke-width: 3″></line><line x1=”709″ y1=”158.25″ x2=”653″ y2=”126″ style=” stroke: black; stroke-width: 3″></line></svg></html>

is the code for Adamantane.html, from which the displayed Adamantane image was obtained by screen capture. The html file is only 783 bytes.

A New Animated Border – Update


This post expands on the app with the flashing red and green lights.

It is contenteditable with the font, color and flashing light color being interactive. Additionally, the object can be resized and moved.

Here is the Control Panel Appearance:

The first two elements set the light colors, the next five elements set the font, the next element sets the background color and the Draw button immediately updates everything.

Here is the code:

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”&gt;
<html xmlns=”http://www.w3.org/1999/xhtml”&gt;
<head profile=”http://gmpg.org/xfn/11″&gt;
<title>test</title>
<style>
body {margin-left:0;margin-right:0;font:normal normal normal 15px Arial;}
a{ text-decoration: }:link { color: rgb(0, 0, 255) }:visited {color :rgb(100, 0,100) }:hover { }:active { }

</style>
</head>
<body>
<di id=”frame” style= “position:absolute ; 100% ; 100% ; left:0 ; top:0 ;”>
<di id=”text” style= “position:absolute ; width:350px ; height:350px ; left:500px ; top:100px ; padding: 15px; font: normal normal 800 18px Georgia; color: #000000; background: white ” contenteditable = “true” onkeyup=’getText();’></di>
</di>
<textarea id=”ta2″ name=”ta2″ style=”position: absolute; visibility: hidden; left: 20px; top: 20px; width: 70%; height: 60%”></textarea>
<center><table id= “tab1″ style=”white-space: pre-wrap”>
<tr>
<td><select id=”color1″ name=”color1″>
<option>green</option><option>red</option><option>blue</option><option>yellow</option><option>orange</option><option>magenta</option><option>black</option><option>pink</option><option>cyan</option><option>lime</option><option>purple</option><option>teal</option><option>aqua</option><option>brown</option><option>gray</option><option>aqua</option><option>olive</option><option>gold</option><option>tan</option><option>crimson</option><option>navy</option><option>lavender</option><option>beige</option>
</select> </td>
<td><input type=’color’ id=’color2′ value= simple color /></td>
<td> Font: <select id=”fnt” name=”fnt”>
<option>Arial</option><option>Arial Black</option><option>Times New Roman</option><option>Courier New</option><option>Comic Sans MS</option><option>Verdana</option><option>Georgia</option><option>Impact</option> </select> <select id=”styl” name=”styl”>
<option>normal</option><option>italic</option><option>oblique</option> </select> <input type=’color’ id=’fcolor’ value= simple color /> <input type=”text” id=”weight” name=”weight” style=”width: 80px” value=”700″ /> <input type=”text” id=”sze” style=”width: 80px” value=”18px” /></td><td> Background: <input type=’color’ id=’bk’ value= simple color /></td><td> <input type=”button” id=”b1″ name=”b1″ value=”Draw” onclick=’comd();’ /></td>
</tr>
</table></center>

var clr = “green”; var clr2 = “red”; var lft = 500; var tp = 70; var cnt = 0; var si; var mv = false; var txtb = “”;
var posX;var posY; var tp2 = “”; var lft2 = “”; var wd2 = “”; var ht2 = “”; var lim1 = 0; var lim2 = 0; var txt = “”; var txt2 = “”; var cnt2 = 0;
alert(“Instructions\nLight Colors, Font and Background are adjustable, but there are defaults.\nChanges are reflected immediately on hitting the Draw button\nArrow keys are used for resizing\nTo move: Double Click upper left corner of object and move mouse (dragging not necessary).\nCP is hidden when resizing or moving\nAlt Key: It has two functions. The first displays the CP, the second displays a popup with the code for the object, which can be copied and pasted into a web page. Clicking Alt alternates between the two.\nHitting Esc reloads the page to start a new object.\nText can be added and edited like a word processor, with automatic wrapping on resizing. Text can be edited anytime. “)
document.getElementById(“color2”).value = “#ff0000”;
document.getElementById(“bk”).value = “#fff3bb”;

var txta = ‘\n’ ;

var IE = document.all?true:false;
if (!IE) document.captureEvents(Event.MOUSEMOVE)
document.ondblclick = getMouse2;
document.onmousemove = getMouseXY;

function getMouse2(e) {
if (mv) {
cnt2 = 0;
mv = false;
} else {
mv = true;
}
}

function getMouseXY(e) {
if (mv) {
cnt2 = 0;
document.getElementById(“tab1”).style.visibility = “hidden”;
if (IE) {
posX = event.clientX + document.body.scrollLeft;
posY = event.clientY + document.body.scrollTop;
}
else {
posX = e.pageX ;
posY = e.pageY ;
}
clearInterval(si);
cnt = 0;
document.getElementById(“frame”).innerHTML = ”;
document.getElementById(“text”).style.left = posX + “px”;
document.getElementById(“text”).style.top = (posY) + “px”;
document.getElementById(“text”).style.width = (wd2 – 10) + “px”;
document.getElementById(“text”).style.height = (ht2 – 10) + “px”;
comd();
}
}

document.onkeyup = key;

function key(e) {

if (e.keyCode == 27) {
location.reload();
}

if (e.keyCode == 18) {
cnt2 ++;
if (cnt2 == 1) {
document.getElementById(“tab1”).style.visibility = “visible”;
} else {
txt2 = document.getElementById(“text”).innerHTML;
txta += txt2 + “”;
txta = txta.replace(“350px”, document.getElementById(“text”).style.width);
txta = txta.replace(“350px”, document.getElementById(“text”).style.height);
txta= txta.replace(“500px”, document.getElementById(“text”).style.left);
txta = txta.replace(“100px”, document.getElementById(“text”).style.top);
txta = txta.replace(“normal normal 800 18px Georgia”, document.getElementById(“styl”).value + ” normal ” + document.getElementById(“weight”).value + ” ” + document.getElementById(“sze”).value + ” ” + document.getElementById(“fnt”).value);
txta = txta.replace(“white”, document.getElementById(“bk”).value);
txta = txta.replace(“#000000”, document.getElementById(“fcolor”).value);

document.getElementById(“ta2”).style.visibility = “visible”;
document.getElementById(“ta2”).value = txta + txtb;
mv = false;
document.getElementById(“ta2”).focus();
document.getElementById(“ta2”).select();
cnt2 = 0;
}
}

if (e.keyCode == 39) {
document.getElementById(“tab1”).style.visibility = “hidden”;
clearInterval(si);
cnt = 0;
cnt2 = 0;
document.getElementById(“frame”).innerHTML = ”;
document.getElementById(“text”).style.width = (wd2- 10) + “px”;
document.getElementById(“text”).style.left = lft2 + “px”;
document.getElementById(“text”).style.height = (ht2- 10) + “px”;
document.getElementById(“text”).style.top = tp2 + “px”;
wd2 += 30;
lft2 -= 15;
document.getElementById(“text”).style.width = (wd2 – 10) + “px”;
document.getElementById(“text”).style.left = lft2 + “px”;
comd();
}

if (e.keyCode == 37) {
document.getElementById(“tab1”).style.visibility = “hidden”;
clearInterval(si);
cnt = 0;
cnt2 = 0;
document.getElementById(“frame”).innerHTML = ”;
document.getElementById(“text”).style.width = (wd2- 10) + “px”;
document.getElementById(“text”).style.left = lft2 + “px”;
document.getElementById(“text”).style.height = (ht2- 10) + “px”;
document.getElementById(“text”).style.top = tp2 + “px”;
wd2 -= 30;
lft2 += 15;
document.getElementById(“text”).style.width = (wd2-10) + “px”;
document.getElementById(“text”).style.left = lft2 + “px”;
comd();
}

if (e.keyCode == 40) {
document.getElementById(“tab1”).style.visibility = “hidden”;
clearInterval(si);
cnt = 0;
cnt2 = 0;
document.getElementById(“frame”).innerHTML = ”;
document.getElementById(“text”).style.width = (wd2- 10) + “px”;
document.getElementById(“text”).style.left = lft2 + “px”;
document.getElementById(“text”).style.height = (ht2- 10) + “px”;
document.getElementById(“text”).style.top = tp2 + “px”;
ht2 -= 30;
tp2 += 15;
document.getElementById(“text”).style.height = (ht2-10) + “px”;
document.getElementById(“text”).style.top = tp2 + “px”;
comd();
}

if (e.keyCode == 38) {
document.getElementById(“tab1”).style.visibility = “hidden”;
clearInterval(si);
cnt = 0;
cnt2 = 0;
document.getElementById(“frame”).innerHTML = ”;
document.getElementById(“text”).style.width = (wd2- 10) + “px”;
document.getElementById(“text”).style.left = lft2 + “px”;
document.getElementById(“text”).style.height = (ht2- 10) + “px”;
document.getElementById(“text”).style.top = tp2 + “px”;
ht2 += 30;
tp2 -= 15;
document.getElementById(“text”).style.height = (ht2-10) + “px”;
document.getElementById(“text”).style.top = tp2 + “px”;
comd();
}
}

function getText() {
txt2 = document.getElementById(“text”).innerHTML;
}

function comd() {
clearInterval(si);
clr = document.getElementById(“color1”).value;
clr2 = document.getElementById(“color2”).value;
document.getElementById(“text”).style.fontFamily = document.getElementById(“fnt”).value;
document.getElementById(“text”).style.fontSize = document.getElementById(“sze”).value;
document.getElementById(“text”).style.fontStyle = document.getElementById(“styl”).value;
document.getElementById(“text”).style.backgroundColor = document.getElementById(“bk”).value;
document.getElementById(“text”).style.color = document.getElementById(“fcolor”).value;

txtb = ‘\n\nvar clr = “green”; var lft = 500; var tp = 70; var cnt = 0; var si; var mv = false;\nvar posX;var posY; var tp2 = “”; var lft2 = “”; var wd2 = “”; var ht2 = “”; var lim1 = 0; var lim2 = 0; var txt = “”; var txt2 = “”;\ntp2 = document.getElementById(“text”).style.top; lft2 = document.getElementById(“text”).style.left;\nwd2 = document.getElementById(“text”).style.width; ht2 = document.getElementById(“text”).style.height;\ntp2 = parseInt(tp2.substr(0, tp2.length – 2)) ;\nlft2 = parseInt(lft2.substr(0, lft2.length – 2)) ;\nwd2 = parseInt(wd2.substr(0, wd2.length – 2)) + 10 ;\nlim1 = wd2/30 – 1;\nht2 = parseInt(ht2.substr(0, ht2.length – 2)) + 10 ;\nlim2 = ht2/30 + 1;\nfor (var i=0; i \’;\n}\n for (var i=0; i \’;\n}\nfor (var i= lim1; i >= 0; i –) {\ncnt ++;\nlft = (30*i + lft2 + 10) + “px”;\ndocument.getElementById(“frame”).innerHTML += \’\’;\n}\nfor (var i= lim2; i >= 0; i –) {\ncnt ++;\ntp = (30*i + tp2 – 20) + “px”;\ndocument.getElementById(“frame”).innerHTML += \’\’;\n}\nfor (var i = 1; i ‘;

tp2 = document.getElementById(“text”).style.top; lft2 = document.getElementById(“text”).style.left;
wd2 = document.getElementById(“text”).style.width; ht2 = document.getElementById(“text”).style.height;
tp2 = parseInt(tp2.substr(0, tp2.length – 2)) ;
lft2 = parseInt(lft2.substr(0, lft2.length – 2)) ;
wd2 = parseInt(wd2.substr(0, wd2.length – 2)) + 10 ;
lim1 = wd2/30 – 1;
ht2 = parseInt(ht2.substr(0, ht2.length – 2)) + 10 ;
lim2 = ht2/30 + 1;
document.getElementById(“text”).innerHTML = txt2;
for (var i=0; i ‘;
}
for (var i=0; i ‘;
}
for (var i= lim1; i >= 0; i –) {
cnt ++;
lft = (30*i + lft2 + 10) + “px”;
document.getElementById(“frame”).innerHTML += ”;
}
for (var i= lim2; i >= 0; i –) {
cnt ++;
tp = (30*i + tp2 – 20) + “px”;
document.getElementById(“frame”).innerHTML += ”;
}
for (var i = 1; i
</body></html>


The resizing is done with the arrow keys:
document.onkeyup = key;

function key(e) {

if (e.keyCode == 40) {
document.getElementById(“tab1”).style.visibility = “hidden”;
clearInterval(si);
cnt = 0;
cnt2 = 0;
document.getElementById(“frame”).innerHTML = ‘<di id=”text” style= “position:absolute ; left:500px ; top:100px ; width:350px ; height:350px; padding: 15px; font: normal normal 800 18px Georgia; color: #000000; background: white ” contenteditable = “true” onkeyup=”getText();”></di>’;
document.getElementById(“text”).style.width = (wd2- 10) + “px”;
document.getElementById(“text”).style.left = lft2 + “px”;
document.getElementById(“text”).style.height = (ht2- 10) + “px”;
document.getElementById(“text”).style.top = tp2 + “px”;
ht2 -= 30;
tp2 += 15;
document.getElementById(“text”).style.height = (ht2-10) + “px”;
document.getElementById(“text”).style.top = tp2 + “px”;
comd();
}

This key reduces the height:
ht2 -= 30;
and to retain the relative position the top must be increased by half the amount:
tp2 += 15;

A mousemove canges the position:
document.onmousemove = getMouseXY;

function getMouseXY(e) {
if (mv) {
cnt2 = 0;
document.getElementById(“tab1”).style.visibility = “hidden”;
if (IE) {
posX = event.clientX + document.body.scrollLeft;
posY = event.clientY + document.body.scrollTop;
}
else {
posX = e.pageX ;
posY = e.pageY ;
}
clearInterval(si);
cnt = 0;
document.getElementById(“frame”).innerHTML = ‘<di id=”text” style= “position:absolute ; width:350px ; height:350px ; left:500px ; top:100px ; padding: 15px; font: normal normal 800 18px Georgia; color: #000000; background: white ” contenteditable = “true” onkeyup=”getText();”></di>’;
document.getElementById(“text”).style.left = posX + “px”;
document.getElementById(“text”).style.top = (posY) + “px”;
document.getElementById(“text”).style.width = (wd2 – 10) + “px”;
document.getElementById(“text”).style.height = (ht2 – 10) + “px”;
comd();
}
}

Ultimately the comd() function is called, which is the main function:
function comd() {
clearInterval(si);
clr = document.getElementById(“color1”).value;
clr2 = document.getElementById(“color2”).value;
document.getElementById(“text”).style.fontFamily = document.getElementById(“fnt”).value;
document.getElementById(“text”).style.fontSize = document.getElementById(“sze”).value;
document.getElementById(“text”).style.fontStyle = document.getElementById(“styl”).value;
document.getElementById(“text”).style.backgroundColor = document.getElementById(“bk”).value;
document.getElementById(“text”).style.color = document.getElementById(“fcolor”).value;

txtb = ‘\n<script type=”text/javascript”>\nvar clr = “green”; var lft = 500; var tp = 70; var cnt = 0; var si; var mv = false;\nvar posX;var posY; var tp2 = “”; var lft2 = “”; var wd2 = “”; var ht2 = “”; var lim1 = 0; var lim2 = 0; var txt = “”; var txt2 = “”;\ntp2 = document.getElementById(“text”).style.top; lft2 = document.getElementById(“text”).style.left;\nwd2 = document.getElementById(“text”).style.width; ht2 = document.getElementById(“text”).style.height;\ntp2 = parseInt(tp2.substr(0, tp2.length – 2)) ;\nlft2 = parseInt(lft2.substr(0, lft2.length – 2)) ;\nwd2 = parseInt(wd2.substr(0, wd2.length – 2)) + 10 ;\nlim1 = wd2/30 – 1;\nht2 = parseInt(ht2.substr(0, ht2.length – 2)) + 10 ;\nlim2 = ht2/30 + 1;\nfor (var i=0; i <= lim1; i ++) {\ncnt ++;\nlft = (30*i + lft2 + 10) + “px”;\ndocument.getElementById(“frame”).innerHTML += \'<di id=”b\’ + cnt + \'” style= “position:absolute ; width:30px ; height:30px ; left:\’ + lft + \’ ; top:\’ + (tp2 – 20) + \’px ; background:green; border-radius: 30px”></di>\’;\n}\n for (var i=0; i <= lim2; i ++) {\ncnt ++;\ntp = (30*i + tp2 – 20) + “px”;\ndocument.getElementById(“frame”).innerHTML += \'<di id=”b\’ + cnt + \'” style= “position:absolute ; width:30px ; height:30px ; left:\’ + (lft2 + wd2 + 10) + \’px ; top:\’ + tp + \’ ; background:green; border-radius: 30px”></di>\’;\n}\nfor (var i= lim1; i >= 0; i –) {\ncnt ++;\nlft = (30*i + lft2 + 10) + “px”;\ndocument.getElementById(“frame”).innerHTML += \'<di id=”b\’ + cnt + \'” style= “position:absolute ; width:30px ; height:30px ; left:\’ + lft + \’ ; top:\’ + (tp2 + ht2 + 10 ) + \’px ; background:green; border-radius: 30px”></di>\’;\n}\nfor (var i= lim2; i >= 0; i –) {\ncnt ++;\ntp = (30*i + tp2 – 20) + “px”;\ndocument.getElementById(“frame”).innerHTML += \'<di id=”b\’ + cnt + \'” style= “position:absolute ; width:30px ; height:30px ; left:\’ + (lft2 – 20) + \’px ; top:\’ + tp + \’ ; background:green; border-radius: 30px”></di>\’;\n}\nfor (var i = 1; i <= cnt; i ++) {\nif (i % 2 == 0) {\ndocument.getElementById(“b” + i).style.backgroundColor = “‘ + clr2 + ‘”;\n} else {\ndocument.getElementById(“b” + i).style.backgroundColor = “‘ + clr + ‘”;\n}\n}\nsi = setInterval(“chnge()”, 150);\n\nfunction chnge() {\nfor (var i = 1; i <= cnt; i ++) {\nif (document.getElementById(“b” + i).style.backgroundColor == “‘ + clr +'”) {\ndocument.getElementById(“b” + i).style.backgroundColor = “‘ + clr2 + ‘”;\n} else {\ndocument.getElementById(“b” + i).style.backgroundColor = “‘ + clr + ‘”;\n}\n}\n}\n<\/script> ‘;

tp2 = document.getElementById(“text”).style.top; lft2 = document.getElementById(“text”).style.left;
wd2 = document.getElementById(“text”).style.width; ht2 = document.getElementById(“text”).style.height;
tp2 = parseInt(tp2.substr(0, tp2.length – 2)) ;
lft2 = parseInt(lft2.substr(0, lft2.length – 2)) ;
wd2 = parseInt(wd2.substr(0, wd2.length – 2)) + 10 ;
lim1 = wd2/30 – 1;
ht2 = parseInt(ht2.substr(0, ht2.length – 2)) + 10 ;
lim2 = ht2/30 + 1;
document.getElementById(“text”).innerHTML = txt2;
for (var i=0; i <= lim1; i ++) {
cnt ++;
lft = (30*i + lft2 + 10) + “px”;
document.getElementById(“frame”).innerHTML += ‘<di id=”b’ + cnt + ‘” style= “position:absolute ; width:30px ; height:30px ; left:’ + lft + ‘ ; top:’ + (tp2 – 20) + ‘px ; background:’ + clr + ‘; border-radius: 30px”></di>’;
}
for (var i=0; i <= lim2; i ++) {
cnt ++;
tp = (30*i + tp2 – 20) + “px”;
document.getElementById(“frame”).innerHTML += ‘<di id=”b’ + cnt + ‘” style= “position:absolute ; width:30px ; height:30px ; left:’ + (lft2 + wd2 + 10) + ‘px ; top:’ + tp + ‘ ; background:’ + clr + ‘; border-radius: 30px”></di>’;
}
for (var i= lim1; i >= 0; i –) {
cnt ++;
lft = (30*i + lft2 + 10) + “px”;
document.getElementById(“frame”).innerHTML += ‘<di id=”b’ + cnt + ‘” style= “position:absolute ; width:30px ; height:30px ; left:’ + lft + ‘ ; top:’ + (tp2 + ht2 + 10 ) + ‘px ; background:’ + clr + ‘; border-radius: 30px”></di>’;
}
for (var i= lim2; i >= 0; i –) {
cnt ++;
tp = (30*i + tp2 – 20) + “px”;
document.getElementById(“frame”).innerHTML += ‘<di id=”b’ + cnt + ‘” style= “position:absolute ; width:30px ; height:30px ; left:’ + (lft2 – 20) + ‘px ; top:’ + tp + ‘ ; background:’ + clr + ‘; border-radius: 30px”></di>’;
}
for (var i = 1; i <= cnt; i ++) {
if (i % 2 == 0) {
document.getElementById(“b” + i).style.backgroundColor = clr2;
} else {
document.getElementById(“b” + i).style.backgroundColor = clr;
}
}
si = setInterval(“chnge()”, 150);
}