Open rmrmg opened 6 years ago
Just set autofit property of viewer to true:
viewer.setAutofit(true);
or in HTML tag:
<span data-widget="Kekule.ChemWidget.Viewer" data-autofit="true"></span>
@partridgejiang: This indeed decrease size of big structure and make it able to fit in Viewer. Unfortunately it also increase size of small molecule which I dont want.
Such a feature may be implemented in the future. Currently there is a workaround:
var box = viewer.getPainter().estimateRenderBox(viewer.getDrawContext());
var deltaX = box.x2 - box.x1;
var deltaY = box.y2 - box.y1;
var widgetDimension = viewer.getDimension();
if (deltaX > widgetDimension .width || deltaY > widgetDimension .height)
viewer.setAutofit(true);
var deltaX = box.x2 - box.x1; var deltaY = box.y2 - box.y1; var widgetDimension = viewer.getDimension(); if (deltaX > widgetDimension .width || deltaY > widgetDimension .height) viewer.setAutofit(true);
Unfortunately, this does not seem to work for me:
const { Molecule } = require('openchemlib');
try {
const molfile = Molecule.fromSmiles(this.props.structure).toMolfile();
const mol = Kekule.IO.loadFormatData(molfile, 'mol')
const parentElem = this.cardRef;
let moleculeContainer = parentElem.current.querySelector('.molecule-render-container');
let renderType = Kekule.Render.RendererType.R2D//R3D // do 2D or 3D drawing
let chemViewer = new Kekule.ChemWidget.Viewer(moleculeContainer);
Kekule.DomUtils.clearChildContent(moleculeContainer);
// create painter, bind with molecule
let painter = new Kekule.Render.ChemObjPainter(renderType, mol);
let box = painter.estimateRenderBox(chemViewer.getDrawContext());
let deltaX = box.x2 - box.x1;
let deltaY = box.y2 - box.y1;
let widgetDimension = chemViewer.getDimension();
if (deltaX > widgetDimension.width || deltaY > widgetDimension.height) {
console.log("AUTOFIT!");
chemViewer.setAutofit(true);
}
// create context inside parentElem
let dim = Kekule.HtmlElementUtils.getElemOffsetDimension(moleculeContainer); // get width/height of parent element
const { width, height } = dim;
// const { offsetWidth: width, offsetHeight: height } = moleculeContainer;//this.props;
let context = painter.createContext(moleculeContainer, width, height); // create context fulfill parent element
// at last, draw the molecule at the center of context
painter.draw(context, { 'x': width / 2, 'y': height / 2 });
} catch (error) {
console.log(error);
}
Hi @richardoptibrium, in the code above, you mixed the usage of the low level painter and the Viewer widget, and did the actual render task by the painter. By using the painter, you had to deal with many low level details manually, including the scale of molecule sizes. The autofit property of Viewer actually taken no effect there. So in most cases, the low level painter is not recommended, just use Viewer widget alone instead. The following code may fit your purpose:
const { Molecule } = require('openchemlib');
try {
const molfile = Molecule.fromSmiles(this.props.structure).toMolfile();
const mol = Kekule.IO.loadFormatData(molfile, 'mol')
const parentElem = this.cardRef;
let moleculeContainer = parentElem.current.querySelector('.molecule-render-container');
let renderType = Kekule.Render.RendererType.R2D;
let chemViewer = new Kekule.ChemWidget.Viewer(moleculeContainer, null, renderType);
chemViewer.setAutoSize(false).setAutofit(false).setChemObj(mol);
let box = chemViewer.getPainter().estimateRenderBox(chemViewer.getDrawContext()); // an internal painter is already encapsulated by viewer, no need to manually created one
let deltaX = box.x2 - box.x1;
let deltaY = box.y2 - box.y1;
let widgetDimension = chemViewer.getDimension();
if (deltaX > widgetDimension.width || deltaY > widgetDimension.height) {
console.log("AUTOFIT!");
chemViewer.setAutofit(true);
}
} catch (error) {
console.log(error);
}
Hi partridgejiang, Thanks for the response, however I'm now seeing molecules rendered in an extreme compressed state, example shown (aspirin):
It also means I have an interactive render when I ideally wanted a static render (that cannot be panned etc.)
@richardoptibrium To do that you have to set the size of the chem viewer element by CSS first or use JavaScript code:
chemViewer.setDimension('300px', '200px');
then you can disable the interaction ability of viewer by:
chemViewer.setPredefinedSetting('static');
Currently:
const molfile = Molecule.fromSmiles(this.props.structure).toMolfile();
const mol = Kekule.IO.loadFormatData(molfile, 'mol')
const parentElem = this.cardRef;
let moleculeContainer = parentElem.current.querySelector('.molecule-render-container');
let renderType = Kekule.Render.RendererType.R2D//R3D // do 2D or 3D drawing
let chemViewer = new Kekule.ChemWidget.Viewer(moleculeContainer);
chemViewer.setAutoSize(false).setAutofit(false).setChemObj(mol).setDimension('100px', '100px').setPredefinedSetting('static');
// create painter, bind with molecule
let box = chemViewer.getPainter().estimateRenderBox(chemViewer.getDrawContext());
let deltaX = box.x2 - box.x1;
let deltaY = box.y2 - box.y1;
let widgetDimension = chemViewer.getDimension();
if (deltaX > widgetDimension.width || deltaY > widgetDimension.height) {
console.log("AUTOFIT!");
chemViewer.setAutofit(true);
}
but same result as shown in my previous post.
@richardoptibrium Try to setChemObj after setDimension? What's more, be sure the .molecule-render-container elements are in document DOM tree before calling setChemObj, otherwise the client size of viewer may not be properly calculated. btw, would you please attach a whole demo here?
Hi partridgejiang, I have tried that now but same result, sorry. debug confirms dimensions of container are good and I changed the order (in fact made them separate calls). In addition the code is in a componentDidMount (REACT). I'm afraid I can't share the full code, sorry.
const molfile = Molecule.fromSmiles(this.props.structure).toMolfile();
const mol = Kekule.IO.loadFormatData(molfile, 'mol')
const parentElem = this.cardRef;
let moleculeContainer = parentElem.current.querySelector('.molecule-render-container');
let renderType = Kekule.Render.RendererType.R2D//R3D // do 2D or 3D drawing
let chemViewer = new Kekule.ChemWidget.Viewer(moleculeContainer);
console.log(moleculeContainer.offsetWidth, moleculeContainer.offsetHeight);
chemViewer.setAutoSize(false);
chemViewer.setAutofit(false);
chemViewer.setDimension(100, 100);
chemViewer.setChemObj(mol);
chemViewer.setPredefinedSetting('static');
// create painter, bind with molecule
let box = chemViewer.getPainter().estimateRenderBox(chemViewer.getDrawContext());
let deltaX = box.x2 - box.x1;
let deltaY = box.y2 - box.y1;
let widgetDimension = chemViewer.getDimension();
if (deltaX > widgetDimension.width || deltaY > widgetDimension.height) {
console.log("AUTOFIT!");
chemViewer.setAutofit(true);
}
@richardoptibrium, not the full code, but just extract the related part of it to form a runnabe one?
Thanks partridgejiang, unzip the attached file sample.zip, run npm install and npm start please
Hi @richardoptibrium, I have found the problem and it is quite simple. In the code, the size of viewer is set to 100X100px, meanwhile, the viewer itself has a default padding of 45px. Then the actual drawing client is only 10X10px, causing all atoms packing together. If you want to keep 100X100px as the viewer's size, just set a smaller padding for the viewer:
chemViewer.setPadding(20);
Afterwards, the issue should be resolved.
btw., there is an error in the line 20 of app.js attached:
chemViewer.renderType(Kekule.Render.RendererType.R2D);
just change it to
chemViewer.setRenderType(Kekule.Render.RendererType.R2D);
or
chemViewer.renderType = Kekule.Render.RendererType.R2D;
Hi @richardoptibrium, I have found the problem and it is quite simple. In the code, the size of viewer is set to 100X100px, meanwhile, the viewer itself has a default padding of 45px. Then the actual drawing client is only 10X10px, causing all atoms packing together. If you want to keep 100X100px as the viewer's size, just set a smaller padding for the viewer:
chemViewer.setPadding(20);
Afterwards, the issue should be resolved.
btw., there is an error in the line 20 of app.js attached:
chemViewer.renderType(Kekule.Render.RendererType.R2D);
just change it to
chemViewer.setRenderType(Kekule.Render.RendererType.R2D);
or
chemViewer.renderType = Kekule.Render.RendererType.R2D;
That's brilliant, thanks! I'm now also trying to set up a 3d render widget with the following code. If I select 2d, I see a molecule (but not the caption & toolbar), if I change to 3d (as below) I see nothing.
const { Molecule } = require('openchemlib');
const molfile = Molecule.fromSmiles('CC(=O)OC1=CC=CC=C1C(=O)O').toMolfile();
const mol = Kekule.IO.loadFormatData(molfile, 'mol')
let viewerContainer = document.getElementById('viewer');
let chemViewer = new Kekule.ChemWidget.Viewer(viewerContainer);
chemViewer.setAutoSize(false).setAutofit(false).setChemObj(mol).setPredefinedSetting('static').setPadding(6)
.setRenderType(Kekule.Render.RendererType.R3D);
chemViewer.moleculeDisplayType(Kekule.Render.Molecule3DDisplayType.BALL_STICK);
chemViewer.setBackgroundColor('#0000cc');
chemViewer.setCaption('Hooray!');
chemViewer.setPadding(6);
chemViewer.setResizable(true);
chemViewer.setEnableDirectInteraction(true);
chemViewer.setEnableToolbar(true);
// create painter, bind with molecule
let box = chemViewer.getPainter().estimateRenderBox(chemViewer.getDrawContext());
let deltaX = box.x2 - box.x1;
let deltaY = box.y2 - box.y1;
let widgetDimension = chemViewer.getDimension();
if (deltaX > widgetDimension.width || deltaY > widgetDimension.height) {
console.log("AUTOFIT!");
chemViewer.setAutofit(true);
}
Hi @richardoptibrium, currently the 3D rendering is utilizing a third-party lib Three.js, please include it in your application first, :).
Hi @richardoptibrium, currently the 3D rendering is utilizing a third-party lib Three.js, please include it in your application first, :).
Thanks, can I do that with npm? I tried npm install which worked but unsure how to import it in react such that kekule can use it. Also, can I set molecule colour for the 2d rendering?
@richardoptibrium Yes you can use Three.js with npm. In the import part of your App.js:
import React from 'react';
import './App.css';
import * as THREE from '../node_modules/three/build/three.module.js';
window.THREE = THREE; // A trick, make Kekule.js know the exists of Three.js
const Kekule = require('kekule'); // use require instead of import here, since import must be at the top of code
...
@richardoptibrium The color of molecule can be set by renderOptions property, for example, in your code:
const mol2 = Kekule.IO.loadFormatData(molfile, 'mol')
mol2.setRenderOption('color', '#ff0000'); // draw a red molecule
...
import * as THREE from '../node_modules/three/build/three.module.js'; window.THREE = THREE; // A trick, make Kekule.js know the exists of Three.js
Sorry, still struggling: sample.zip
@richardoptibrium , the import of Kekule must be after window.THREE = THREE
, so require
should be used instead of import
, just change the line 4-5 of App.js from
import Kekule from 'kekule';
window.THREE = THREE;
to:
window.THREE = THREE;
const Kekule = require('kekule');
In fixed-sized Viewer molecule are not scaled to Viewer size. It is not a problem for small molecule but for big only part of structure is display. Is is possible somehow to force scaling big (which not fit to viewer size) molecule ?