/** {@inheritDoc} */ @Override @TestMethod("testEmptyReaction") public IRenderingElement generate(IReaction reaction, RendererModel model) { if (!model.getParameter(ShowReactionBoxes.class).getValue()) return null; double separation = model.getParameter(BondLength.class).getValue() / model.getParameter(Scale.class).getValue(); Rectangle2D totalBounds = BoundsCalculator.calculateBounds(reaction); if (totalBounds == null) return null; ElementGroup diagram = new ElementGroup(); Color foregroundColor = model.getParameter(BasicSceneGenerator.ForegroundColor.class).getValue(); diagram.add( new RectangleElement( totalBounds.getMinX() - separation, totalBounds.getMinY() - separation, totalBounds.getMaxX() + separation, totalBounds.getMaxY() + separation, foregroundColor)); if (reaction.getID() != null) { diagram.add( new TextElement( (totalBounds.getMinX() + totalBounds.getMaxX()) / 2, totalBounds.getMinY() - separation, reaction.getID(), foregroundColor)); } return diagram; }
public IRenderingElement generate(IAtomContainer ac, RendererModel model) { if (generators == null) return EMPTY_ELEMENT; if (use) { ElementGroup group = new ElementGroup(); for (IGenerator generator : generators) { group.add(generator.generate(ac, model)); } return group; } else return EMPTY_ELEMENT; }
private IRenderingElement generate(IAtomContainer molecule, RendererModel model, int atomNum) throws CDKException { // tag the atom and bond ids String molId = molecule.getProperty(MarkedElement.ID_KEY); if (molId != null) { int atomId = 0, bondid = 0; for (IAtom atom : molecule.atoms()) setIfMissing(atom, MarkedElement.ID_KEY, molId + "atm" + ++atomId); for (IBond bond : molecule.bonds()) setIfMissing(bond, MarkedElement.ID_KEY, molId + "bnd" + ++bondid); } if (annotateAtomNum) { for (IAtom atom : molecule.atoms()) { if (atom.getProperty(StandardGenerator.ANNOTATION_LABEL) != null) throw new UnsupportedOperationException("Multiple annotation labels are not supported."); atom.setProperty(StandardGenerator.ANNOTATION_LABEL, Integer.toString(atomNum++)); } } else if (annotateAtomVal) { for (IAtom atom : molecule.atoms()) { if (atom.getProperty(StandardGenerator.ANNOTATION_LABEL) != null) throw new UnsupportedOperationException("Multiple annotation labels are not supported."); atom.setProperty( StandardGenerator.ANNOTATION_LABEL, atom.getProperty(CDKConstants.COMMENT)); } } else if (annotateAtomMap) { for (IAtom atom : molecule.atoms()) { if (atom.getProperty(StandardGenerator.ANNOTATION_LABEL) != null) throw new UnsupportedOperationException("Multiple annotation labels are not supported."); int mapidx = accessAtomMap(atom); if (mapidx > 0) { atom.setProperty(StandardGenerator.ANNOTATION_LABEL, Integer.toString(mapidx)); } } } ElementGroup grp = new ElementGroup(); for (IGenerator<IAtomContainer> gen : gens) grp.add(gen.generate(molecule, model)); // cleanup if (annotateAtomNum || annotateAtomMap) { for (IAtom atom : molecule.atoms()) { atom.removeProperty(StandardGenerator.ANNOTATION_LABEL); } } return grp; }
/* (non-Javadoc) * @see org.openscience.cdk.renderer.generators.IGenerator#generate(org.openscience.cdk.interfaces.IAtomContainer, org.openscience.cdk.renderer.RendererModel) */ public IRenderingElement generate(IAtomContainer ac, RendererModel model) { double r = model.getHighlightDistance() / model.getScale(); ElementGroup group = new ElementGroup(); for (ISubStructure subStructure : subStructures) { ElementGroup subGroup = new ElementGroup(); if (subStructure.getAtomContainer() == null) continue; for (IAtom atom : subStructure.getAtomContainer().atoms()) { Point2d p = atom.getPoint2d(); Color color = subStructure.getHighlightingColor(atom); subGroup.add(generateElement(p, r, color != null ? color : DEFAULT_COLOR)); } group.add(subGroup); } return group; }
private IRenderingElement generateAbbreviationSgroup(Sgroup sgroup) { String label = sgroup.getSubscript(); // already handled by symbol remapping if (sgroup.getBonds().size() > 0 || label == null || label.isEmpty()) { return new ElementGroup(); } // we're showing a label where there were no atoms before, we put it in the // middle of all of those which were hidden final Point2d labelCoords = GeometryUtil.get2DCenter(sgroup.getAtoms()); ElementGroup group = new ElementGroup(); for (Shape outline : atomGenerator .generatePseudoSymbol(label, HydrogenPosition.Right) .resize(1 / scale, 1 / -scale) .getOutlines()) group.add(GeneralPath.shapeOf(outline, foreground)); return MarkedElement.markupAtom(group, null); }
/** * Generate the Sgroup elements for the provided atom contains. * * @param container molecule * @return Sgroup rendering elements */ IRenderingElement generateSgroups(IAtomContainer container) { ElementGroup result = new ElementGroup(); List<Sgroup> sgroups = container.getProperty(CDKConstants.CTAB_SGROUPS); if (sgroups == null || sgroups.isEmpty()) return result; for (Sgroup sgroup : sgroups) { switch (sgroup.getType()) { case CtabAbbreviation: result.add(generateAbbreviationSgroup(sgroup)); break; case CtabMultipleGroup: result.add(generateMultipleSgroup(sgroup)); break; case CtabAnyPolymer: case CtabMonomer: case CtabCrossLink: case CtabCopolymer: case CtabStructureRepeatUnit: case CtabMer: case CtabGraft: case CtabModified: result.add(generatePolymerSgroup(sgroup)); break; case CtabComponent: case CtabMixture: case CtabFormulation: result.add(generateMixtureSgroup(sgroup)); break; case CtabGeneric: // not strictly a polymer but okay to draw as one result.add(generatePolymerSgroup(sgroup)); break; } } return result; }
private IRenderingElement generateSgroupBrackets( Sgroup sgroup, List<SgroupBracket> brackets, String subscriptSuffix, String superscriptSuffix) { // brackets are square by default (style:0) Integer style = sgroup.getValue(SgroupKey.CtabBracketStyle); boolean round = style != null && style == 1; ElementGroup result = new ElementGroup(); Set<IAtom> atoms = sgroup.getAtoms(); Set<IBond> crossingBonds = sgroup.getBonds(); // easy to depict in correct orientation, we just // point each bracket at the atom of a crossing // bond that is 'in' the group - this scales // to more than two brackets // first we need to pair the brackets with the bonds Map<SgroupBracket, IBond> pairs = crossingBonds.size() == brackets.size() ? bracketBondPairs(brackets, crossingBonds) : Collections.<SgroupBracket, IBond>emptyMap(); if (!pairs.isEmpty()) { SgroupBracket suffixBracket = null; Vector2d suffixBracketPerp = null; for (Map.Entry<SgroupBracket, IBond> e : pairs.entrySet()) { final SgroupBracket bracket = e.getKey(); final IBond bond = e.getValue(); final IAtom inGroupAtom = atoms.contains(bond.getAtom(0)) ? bond.getAtom(0) : bond.getAtom(1); final Point2d p1 = bracket.getFirstPoint(); final Point2d p2 = bracket.getSecondPoint(); final Vector2d perp = VecmathUtil.newPerpendicularVector(VecmathUtil.newUnitVector(p1, p2)); // point the vector at the atom group Point2d midpoint = VecmathUtil.midpoint(p1, p2); if (perp.dot(VecmathUtil.newUnitVector(midpoint, inGroupAtom.getPoint2d())) < 0) { perp.negate(); } perp.scale(bracketDepth); Path2D path = new Path2D.Double(); if (round) { // bracket 1 (cp: control point) path.moveTo(p1.x + perp.x, p1.y + perp.y); Point2d cpb1 = new Point2d(midpoint); cpb1.add(VecmathUtil.negate(perp)); path.quadTo(cpb1.x, cpb1.y, p2.x + perp.x, p2.y + p2.y); } else { path.moveTo(p1.x + perp.x, p1.y + perp.y); path.lineTo(p1.x, p1.y); path.lineTo(p2.x, p2.y); path.lineTo(p2.x + perp.x, p2.y + perp.y); } result.add(GeneralPath.outlineOf(path, stroke, foreground)); if (suffixBracket == null) { suffixBracket = bracket; suffixBracketPerp = perp; } else { // is this bracket better as a suffix? Point2d sp1 = suffixBracket.getFirstPoint(); Point2d sp2 = suffixBracket.getSecondPoint(); double bestMaxX = Math.max(sp1.x, sp2.x); double thisMaxX = Math.max(p1.x, p2.x); double bestMaxY = Math.max(sp1.y, sp2.y); double thisMaxY = Math.max(p1.y, p2.y); // choose the most eastern or.. the most southern double xDiff = thisMaxX - bestMaxX; double yDiff = thisMaxY - bestMaxY; if (xDiff > EQUIV_THRESHOLD || (xDiff > -EQUIV_THRESHOLD && yDiff < -EQUIV_THRESHOLD)) { suffixBracket = bracket; suffixBracketPerp = perp; } } } // write the labels if (suffixBracket != null) { Point2d subSufPnt = suffixBracket.getFirstPoint(); Point2d supSufPnt = suffixBracket.getSecondPoint(); // try to put the subscript on the bottom double xDiff = subSufPnt.x - supSufPnt.x; double yDiff = subSufPnt.y - supSufPnt.y; if (yDiff > EQUIV_THRESHOLD || (yDiff > -EQUIV_THRESHOLD && xDiff > EQUIV_THRESHOLD)) { Point2d tmpP = subSufPnt; subSufPnt = supSufPnt; supSufPnt = tmpP; } // subscript/superscript suffix annotation if (subscriptSuffix != null && !subscriptSuffix.isEmpty()) { TextOutline subscriptOutline = leftAlign( makeText( subscriptSuffix.toLowerCase(Locale.ROOT), subSufPnt, suffixBracketPerp, labelScale)); result.add(GeneralPath.shapeOf(subscriptOutline.getOutline(), foreground)); } if (superscriptSuffix != null && !superscriptSuffix.isEmpty()) { TextOutline superscriptOutline = leftAlign( makeText( superscriptSuffix.toLowerCase(Locale.ROOT), supSufPnt, suffixBracketPerp, labelScale)); result.add(GeneralPath.shapeOf(superscriptOutline.getOutline(), foreground)); } } } else if (brackets.size() == 2) { final Point2d b1p1 = brackets.get(0).getFirstPoint(); final Point2d b1p2 = brackets.get(0).getSecondPoint(); final Point2d b2p1 = brackets.get(1).getFirstPoint(); final Point2d b2p2 = brackets.get(1).getSecondPoint(); final Vector2d b1vec = VecmathUtil.newUnitVector(b1p1, b1p2); final Vector2d b2vec = VecmathUtil.newUnitVector(b2p1, b2p2); final Vector2d b1pvec = VecmathUtil.newPerpendicularVector(b1vec); final Vector2d b2pvec = VecmathUtil.newPerpendicularVector(b2vec); // Point the vectors at each other if (b1pvec.dot(VecmathUtil.newUnitVector(b1p1, b2p1)) < 0) b1pvec.negate(); if (b2pvec.dot(VecmathUtil.newUnitVector(b2p1, b1p1)) < 0) b2pvec.negate(); // scale perpendicular vectors by how deep the brackets need to be b1pvec.scale(bracketDepth); b2pvec.scale(bracketDepth); // bad brackets if (Double.isNaN(b1pvec.x) || Double.isNaN(b1pvec.y) || Double.isNaN(b2pvec.x) || Double.isNaN(b2pvec.y)) return result; Path2D path = new Path2D.Double(); if (round) { // bracket 1 (cp: control point) path.moveTo(b1p1.x + b1pvec.x, b1p1.y + b1pvec.y); Point2d cpb1 = VecmathUtil.midpoint(b1p1, b1p2); cpb1.add(VecmathUtil.negate(b1pvec)); path.quadTo(cpb1.x, cpb1.y, b1p2.x + b1pvec.x, b1p2.y + b1pvec.y); // bracket 2 (cp: control point) path.moveTo(b2p1.x + b2pvec.x, b2p1.y + b2pvec.y); Point2d cpb2 = VecmathUtil.midpoint(b2p1, b2p2); cpb2.add(VecmathUtil.negate(b2pvec)); path.quadTo(cpb2.x, cpb2.y, b2p2.x + b2pvec.x, b2p2.y + b2pvec.y); } else { // bracket 1 path.moveTo(b1p1.x + b1pvec.x, b1p1.y + b1pvec.y); path.lineTo(b1p1.x, b1p1.y); path.lineTo(b1p2.x, b1p2.y); path.lineTo(b1p2.x + b1pvec.x, b1p2.y + b1pvec.y); // bracket 2 path.moveTo(b2p1.x + b2pvec.x, b2p1.y + b2pvec.y); path.lineTo(b2p1.x, b2p1.y); path.lineTo(b2p2.x, b2p2.y); path.lineTo(b2p2.x + b2pvec.x, b2p2.y + b2pvec.y); } result.add(GeneralPath.outlineOf(path, stroke, foreground)); // work out where to put the suffix labels (e.g. ht/hh/eu) superscript // and (e.g. n, xl, c, mix) subscript // TODO: could be improved double b1MaxX = Math.max(b1p1.x, b1p2.x); double b2MaxX = Math.max(b2p1.x, b2p2.x); double b1MaxY = Math.max(b1p1.y, b1p2.y); double b2MaxY = Math.max(b2p1.y, b2p2.y); Point2d subSufPnt = b2p2; Point2d supSufPnt = b2p1; Vector2d subpvec = b2pvec; double bXDiff = b1MaxX - b2MaxX; double bYDiff = b1MaxY - b2MaxY; if (bXDiff > EQUIV_THRESHOLD || (bXDiff > -EQUIV_THRESHOLD && bYDiff < -EQUIV_THRESHOLD)) { subSufPnt = b1p2; supSufPnt = b1p1; subpvec = b1pvec; } double xDiff = subSufPnt.x - supSufPnt.x; double yDiff = subSufPnt.y - supSufPnt.y; if (yDiff > EQUIV_THRESHOLD || (yDiff > -EQUIV_THRESHOLD && xDiff > EQUIV_THRESHOLD)) { Point2d tmpP = subSufPnt; subSufPnt = supSufPnt; supSufPnt = tmpP; } // subscript/superscript suffix annotation if (subscriptSuffix != null && !subscriptSuffix.isEmpty()) { TextOutline subscriptOutline = leftAlign( makeText(subscriptSuffix.toLowerCase(Locale.ROOT), subSufPnt, subpvec, labelScale)); result.add(GeneralPath.shapeOf(subscriptOutline.getOutline(), foreground)); } if (superscriptSuffix != null && !superscriptSuffix.isEmpty()) { TextOutline superscriptOutline = leftAlign( makeText( superscriptSuffix.toLowerCase(Locale.ROOT), supSufPnt, subpvec, labelScale)); result.add(GeneralPath.shapeOf(superscriptOutline.getOutline(), foreground)); } } return result; }