reymond-group / smilesDrawer

A small, highly performant JavaScript component for parsing and drawing SMILES strings. Released under the MIT license.
https://smilesdrawer.rocks
MIT License
416 stars 66 forks source link

Bug: PNG drawer does work in version 2.1.7 as a React component #167

Open biostu24 opened 1 year ago

biostu24 commented 1 year ago

I am using React in Next.js.

I created a React component to render a chemical structure and I found when using SmilesDrawer.Drawer, the canvas is not updated and no structure is drawn. Using SmilesDrawer.SvgDrawer does work. After a bit of experimentation I found this appears to be a bug with version 2.1.7. Rolling back to earlier 2.1.x versions resulted in an import error. Rolling back to version 2.0.3 the SmilesDrawer.Drawer class does work.

Also, it would be very helpful if the documentation showed how to use smiles-drawer with React as it is not immediately obvious.

Summary of issues:

Example code

This code is for a component to render a structure either as PNG or SVG.

import React, { useRef, useEffect } from "react";
import SmilesDrawer from "smiles-drawer";

const USE_SVG = false;

const SETTINGS = {
  width: 800,
  height: 400,
};

const SmileDrawerContainer = ({ smilesStr }: { smilesStr: string }) => {
  if (USE_SVG) {
    // SVG version (versions: <=2.0.3 and 2.1.7)
    const svgRef = useRef<SVGElement>(null);

    let drawer = new SmilesDrawer.SvgDrawer(SETTINGS);

    useEffect(() => {
      SmilesDrawer.parse(smilesStr, function (tree: any) {
        drawer.draw(tree, "structure-svg", "light");
      });
    }, []);

    return (
      <div>
        <svg id="structure-svg"></svg>
      </div>
    );
  } else {
    // Canvas version (versions: <=2.0.3 only)
    const canvasRef = useRef<HTMLCanvasElement>(null);

    let drawer = new SmilesDrawer.Drawer(SETTINGS);

    useEffect(() => {
      SmilesDrawer.parse(smilesStr, function (tree: any) {
        drawer.draw(tree, "structure-canvas", "light");
      });
    }, []);

    return (
      <div>
        <canvas
          className="relative"
          id="structure-canvas"
          ref={canvasRef}
          width={"800px"}
          height={"400px"}
        />
      </div>
    );
  }
};

export default SmileDrawerContainer;
whitead commented 1 year ago

Thanks for writing this out - I've been so confused.

Acylation commented 1 year ago
const source = 'CC(=O)Nc1ccccc1C(=O)O'
const targetImg = container.createEl('img') as HTMLImageElement // container is a HTMLDivElement, decleared previously
const theme = 'dark'

let drawer = new SmilesDrawer.SmiDrawer();
drawer.draw(source, targetImg, theme)

These lines are able to generate a PNG file in v2.1.7.

biostu24 commented 1 year ago

If that is the case, the documentation really needs to be updated to reflect that a canvas element no longer works

Acylation commented 1 year ago

Here's a React example that yields png

const imgRef = useRef<HTMLImageElement>(null);
const drawer = new SmilesDrawer.SmiDrawer(options); // options is a param

useEffect(() => {
  drawer.draw(smilesStr, imgRef.current, 'light');
});

return (
  <div>
    <img ref={imgRef} width={300}></img>
  </div>
);
Sinkler521 commented 1 month ago

If that is the case, the documentation really needs to be updated to reflect that a canvas element no longer works

2.1.7 smiles-drawer still works with canvas. Look at example/next.html file in lib repository and go this way. In case you'll draw a lot of different molecules, create a function which generates unique classname for each of those canvas element

Example: codesandbox link look at the files SmilesDrawer.tsx and App.js