/*
* ==================================================
* jsMolEditor: JavaScript based molecule strucutre editor
* ==================================================
* Project Info: http://chemhack.com/jsmoleditor
*
* Copyright (C) 2009. Duan Lian
*
* This library is free software; you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Foundation;
* either version 3 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
* Boston, MA 02111-1307, USA.
*/
package com.chemhack.jsMolEditor.client.renderer;
import com.chemhack.jsMolEditor.client.model.Molecule;
import com.chemhack.jsMolEditor.client.model.Bond;
import com.chemhack.jsMolEditor.client.model.Atom;
import com.chemhack.jsMolEditor.client.jre.emulation.java.awt.geom.Rectangle2D;
import com.chemhack.jsMolEditor.client.jre.emulation.java.awt.geom.Point2D;
import com.chemhack.jsMolEditor.client.widget.ExtendedCanvas;
import com.chemhack.jsMolEditor.client.controller.EditorController;
import com.chemhack.jsMolEditor.client.renderer.color.BasicColorScheme;
public class CanvasRenderer {
EditorController controller;
RendererModel rendererModel;
ExtendedCanvas canvas;
CordTransformer transformer;
BasicColorScheme colorScheme;
public CanvasRenderer(ExtendedCanvas canvas) {
this(null, canvas);
}
public CanvasRenderer(EditorController controller, ExtendedCanvas canvas) {
this.controller = controller;
this.canvas = canvas;
rendererModel = new RendererModel();
colorScheme = new BasicColorScheme();
transformer = new CordTransformer(54, 0, 0, -54, -12660, 8196, 3); //Hardcoded default transform. `
}
public void paintNewMolecule(Molecule molecule) {
createScaleTransform(createRectangle2D(molecule), new Rectangle2D(0, 0, canvas.getWidth(), canvas.getHeight()));
paintMolecule(molecule);
}
public void paintMolecule(Molecule molecule) {
canvas.setBackgroundColor(rendererModel.getBackColor());
canvas.clear();
canvas.save();
this.paintBonds(molecule);
this.paintAtoms(molecule);
canvas.restore();
}
private void paintBonds(Molecule molecule) {
canvas.save();
canvas.setLineWidth(rendererModel.getBondWidth());
for (int i = 0; i < molecule.countBonds(); i++) {
Bond currentBond = molecule.getBond(i);
if (rendererModel.getHighlightedBonds().contains(currentBond)) {
canvas.setStrokeStyle("red");
} else {
canvas.setStrokeStyle(rendererModel.getForeColor());
}
Point2D pointSource = transformer.transform(new Point2D(currentBond.getSource().getX(), currentBond.getSource().getY()), null);
Point2D pointTarget = transformer.transform(new Point2D(currentBond.getTarget().getX(), currentBond.getTarget().getY()), null);
double x1 = pointSource.getX();
double y1 = pointSource.getY();
double x2 = pointTarget.getX();
double y2 = pointTarget.getY();
switch (currentBond.getType()) {
case 1:
canvas.beginPath();
canvas.moveTo(x1, y1);
canvas.lineTo(x2, y2);
canvas.stroke();
break;
case 2:
double[] coords2 = distanceCalculator(new double[]{x1, y1, x2, y2}, rendererModel.getBondDistance());
canvas.beginPath();
canvas.moveTo(coords2[0], coords2[1]);
canvas.lineTo(coords2[6], coords2[7]);
canvas.stroke();
canvas.beginPath();
canvas.moveTo(coords2[2], coords2[3]);
canvas.lineTo(coords2[4], coords2[5]);
canvas.stroke();
break;
case 3:
double[] coords3 = distanceCalculator(new double[]{x1, y1, x2, y2}, 4);
canvas.beginPath();
canvas.moveTo(x1, y1);
canvas.lineTo(x2, y2);
canvas.stroke();
canvas.beginPath();
canvas.moveTo(coords3[0], coords3[1]);
canvas.lineTo(coords3[6], coords3[7]);
canvas.stroke();
canvas.beginPath();
canvas.moveTo(coords3[2], coords3[3]);
canvas.lineTo(coords3[4], coords3[5]);
canvas.stroke();
break;
}
}
canvas.restore();
}
private void paintAtoms(Molecule molecule) {
for (int i = 0; i < molecule.countAtoms(); i++) {
Atom currentAtom = molecule.getAtom(i);
paintAtom(currentAtom);
}
}
private void paintAtom(Atom currentAtom) {
Point2D point = transformer.transform(new Point2D(currentAtom.getX(), currentAtom.getY()), null);
double x = point.getX();
double y = point.getY();
boolean drawAtomLabel = false;
String symbolText = currentAtom.getSymbol();
if (!symbolText.equals("C")) {
drawAtomLabel = true;
} else if (currentAtom.countNeighbors() == 0) {
drawAtomLabel = true;
} else if (currentAtom.getCharge() != 0) {
drawAtomLabel = true;
}
String fontColor = colorScheme.getColor(symbolText);
boolean highlighted = rendererModel.getHighlightedAtoms().contains(currentAtom);
if (drawAtomLabel || highlighted) {
canvas.save();
if (highlighted) {
canvas.setFillStyle(rendererModel.getSelectedAtomBackColor());
} else {
canvas.setFillStyle(rendererModel.getBackColor());
}
double symbolFontSize = 12;
double chargeFontSize = symbolFontSize * 0.6;
double radius = symbolFontSize / 2 * 1.414;
// currentAtom.setCharge(1);
String chargeText = "";
if (currentAtom.getCharge() != 0) {
if (currentAtom.getCharge() == 1) chargeText = "+";
else if (currentAtom.getCharge() == -1) chargeText = "-";
else if (currentAtom.getCharge() > 0) chargeText = currentAtom.getCharge() + "+";
else if (currentAtom.getCharge() < 0) chargeText = Math.abs(currentAtom.getCharge()) + "-";
radius += 5;
}
double textWidth = canvas.measure(symbolText, symbolFontSize);
canvas.beginPath();
canvas.rect(x - textWidth / 2, y - symbolFontSize / 2, textWidth, symbolFontSize);
// canvas.arc(x, y, radius, 0, Math.PI * 2, true);
canvas.fill();
canvas.setStrokeStyle(rendererModel.getForeColor());
canvas.setStrokeStyle(fontColor);
canvas.drawText(symbolText, x - textWidth / 2, y - symbolFontSize / 2, symbolFontSize);
if (!chargeText.equals("")) {
canvas.drawText(chargeText, x + textWidth / 2, y - symbolFontSize, chargeFontSize);
}
// canvas.drawTextCenter(currentAtom.getSymbol(), x, y, fontSize);
canvas.restore();
}
}
public void createScaleTransform(final Rectangle2D contextBounds, final Rectangle2D rendererBounds) {
double factor = rendererModel.getZoomFactor() * (1.0 - rendererModel.getMargin() * 2.0);
double scaleX = factor * rendererBounds.getWidth() / contextBounds.getWidth();
double scaleY = factor * rendererBounds.getHeight() / contextBounds.getHeight();
double scale;
transformer = new CordTransformer();
if (scaleX > scaleY) {
// System.out.println("Scaled by Y: " + scaleY);
// FIXME: should be -X: to put the origin in the lower left corner
transformer.scale(scaleY, -scaleY);
scale = scaleY;
} else {
// System.out.println("Scaled by X: " + scaleX);
// FIXME: should be -X: to put the origin in the lower left corner
transformer.scale(scaleX, -scaleX);
scale = scaleX;
}
//translate
// System.out.println("scale: " + scale);
double dx = -contextBounds.getX() * scale + 0.5 * (rendererBounds.getWidth() - contextBounds.getWidth() * scale);
double dy = -contextBounds.getY() * scale - 0.5 * (rendererBounds.getHeight() + contextBounds.getHeight() * scale);
// System.out.println("dx/scaleX: " + dx / scaleX + " dy/scaleY:" + dy / scaleY);
transformer.translate(dx / scale, dy / scale);
}
public Rectangle2D createRectangle2D(Molecule molecule) {
if (molecule == null || molecule.countAtoms() == 0) {
return null;
}
double xmin, xmax = molecule.getAtom(0).getX();
xmin = xmax;
double ymin, ymax = molecule.getAtom(0).getY();
ymin = ymax;
double y, x;
for (int i = 1; i < molecule.countAtoms(); i++) {
y = molecule.getAtom(i).getY();
x = molecule.getAtom(i).getX();
if (x < xmin) {
xmin = x;
} else if (x > xmax) {
xmax = x;
}
if (y < ymin) {
ymin = y;
} else if (y > ymax) {
ymax = y;
}
}
double margin = 1; //1 is ~enough margin to make symbols + text appear on screen
return new Rectangle2D(xmin - margin, ymin - margin, (xmax - xmin) + 2 * margin, (ymax - ymin) + 2 * margin);
}
public static double[] distanceCalculator(double[] coords, double dist) {
double angle;
if ((coords[2] - coords[0]) == 0) {
angle = Math.PI / 2;
} else {
angle = Math.atan((coords[3] - coords[1]) / (coords[2] - coords[0]));
}
double begin1X = (Math.cos(angle + Math.PI / 2) * dist + coords[0]);
double begin1Y = (Math.sin(angle + Math.PI / 2) * dist + coords[1]);
double begin2X = (Math.cos(angle - Math.PI / 2) * dist + coords[0]);
double begin2Y = (Math.sin(angle - Math.PI / 2) * dist + coords[1]);
double end1X = (Math.cos(angle - Math.PI / 2) * dist + coords[2]);
double end1Y = (Math.sin(angle - Math.PI / 2) * dist + coords[3]);
double end2X = (Math.cos(angle + Math.PI / 2) * dist + coords[2]);
double end2Y = (Math.sin(angle + Math.PI / 2) * dist + coords[3]);
return new double[]{begin1X, begin1Y, begin2X, begin2Y, end1X, end1Y, end2X, end2Y};
}
public CordTransformer getTransformer() {
return transformer;
}
public ExtendedCanvas getCanvas() {
return canvas;
}
public void setCanvas(ExtendedCanvas canvas) {
this.canvas = canvas;
}
public RendererModel getRendererModel() {
return rendererModel;
}
}