Example usage for java.awt.geom AffineTransform AffineTransform

List of usage examples for java.awt.geom AffineTransform AffineTransform

Introduction

In this page you can find the example usage for java.awt.geom AffineTransform AffineTransform.

Prototype

public AffineTransform() 

Source Link

Document

Constructs a new AffineTransform representing the Identity transformation.

Usage

From source file:org.jcurl.demo.tactics.sg.BroomPromptScenario.java

public BroomPromptScenario() {
    // create the scene
    final boolean stickUp = false;
    final boolean bothSides = true;
    final int pieAngle = 150;
    final Color sp = Color.BLACK;
    final Color bgc = new Color(1, 1, 1, 0.5f);
    final Stroke fine = new BasicStroke(0.01f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER);
    final Stroke bold = new BasicStroke(0.03f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_MITER);
    // final Font fo = new Font("SansSerif", Font.BOLD, 1);
    final float halo = RockProps.DEFAULT.getRadius();
    final float outer = 0.8f * RockProps.DEFAULT.getRadius();
    stickLength = (stickUp ? 1 : -1) * 5 * outer;
    final float inner = 0.5F * outer;

    final SGGroup me = new SGGroup();
    // the transparent background
    {/*from  w  w w .  j  a va2  s.  c o  m*/
        final SGShape bg = node(new Arc2D.Float(-halo, -halo, 2 * halo, 2 * halo, 0, 360, Arc2D.OPEN), null,
                null, scale0);
        bg.setFillPaint(bgc);
        bg.addMouseListener(new MoveHandler());
        bg.setMouseBlocker(true);
        bg.setCursor(moveC);
        me.add(bg);
    }
    // the cross-hair and stick
    {
        final int off = 90;
        final int pieOff = 180;
        final int arrowLengthDegrees = 7;
        // colored pie:
        pie = node(new Arc2D.Float(-outer, -outer, 2 * outer, 2 * outer, off - pieOff, pieAngle, Arc2D.PIE),
                null, null, scale0);
        me.add(pie);
        // inner circle:
        me.add(node(new Arc2D.Float(-inner, -inner, 2 * inner, 2 * inner, off, pieOff + pieAngle, Arc2D.OPEN),
                fine, sp, scale50));
        // outer circle:
        me.add(node(new Arc2D.Float(-outer, -outer, 2 * outer, 2 * outer, off,
                pieOff + pieAngle - (14 + arrowLengthDegrees), Arc2D.OPEN), fine, sp, scale50));
        // Semantic zooming: me.add(node(new Arc2D.Float(-outer, -outer, 2 *
        // outer, 2 * outer,
        // off, pieOff + pieAngle, Arc2D.OPEN), fine, sp, -scale50));
        final double ar = Math.PI * (off + pieAngle) / 180.0;
        // radius
        // if (pieAngle % 90 != 0)
        me.add(node(new Line2D.Double(0, 0, -outer * Math.cos(ar), outer * Math.sin(ar)), bold, sp, scale0));

        // arrow:
        final float f = outer / 10;
        final SGShape s = node(IceShapes.createArrowHead(f, 3 * f, 0.5f * f), null, null, scale50);
        s.setFillPaint(sp);
        final double a = Math.PI * (off + pieAngle - arrowLengthDegrees) / 180.0;
        final AffineTransform a_ = new AffineTransform();
        a_.translate(-outer * Math.cos(a), outer * Math.sin(a));
        a_.rotate(Math.PI * (90 - (off + pieAngle) + 8 + arrowLengthDegrees) / 180.0);
        final Affine s_ = SGTransform.createAffine(a_, s);
        me.add(s_);
    }
    { // y-axis:
        me.add(node(new Line2D.Float(0, -Math.signum(stickLength) * halo, 0, stickLength), fine, sp, scale0));
        // x-axis:
        me.add(node(new Line2D.Float(-halo, 0, halo, 0), fine, sp, scale0));
    }
    { // slider
        sli = new SGShape();
        sli.setShape(IceShapes.createSlider(0.4f * outer, bothSides));
        // sli.setFillPaint(sp);
        sli.setDrawStroke(fine);
        sli.setDrawPaint(sp);
        sli.setMode(Mode.STROKE_FILL);
        sli.setAntialiasingHint(RenderingHints.VALUE_ANTIALIAS_ON);
        me.add(slider = SGTransform.createAffine(new AffineTransform(), sli));
        slider.setCursor(moveC);
        slider.addMouseListener(new SpeedHandler());
        slider.setMouseBlocker(true);
    }
    handle = SGTransform.createAffine(new AffineTransform(), me);
    scene = SGTransform.createAffine(new AffineTransform(), handle);
    scene.setVisible(false);
}

From source file:org.jcurl.core.impl.CurveManager.java

private void recompute(final double currentTime, final boolean complete) {
    if (getSuspended())
        return;/*from   ww w. j  ava  2  s .  c  o m*/
    if (complete) {
        {
            // initial
            final double t0 = 0.0;
            // TUNE Parallel
            // initial curves:
            for (int i16 = RockSet.ROCKS_PER_SET - 1; i16 >= 0; i16--) {
                curveStore.reset(i16);
                curveStore.add(i16, t0, doComputeCurve(i16, t0, initialPos, initialSpeed, NoSweep), _30);
            }
            // initial collission detection:
            collissionStore.clear();
            // TUNE Parallel
            for (int i16 = RockSet.ROCKS_PER_SET - 1; i16 >= 0; i16--)
                for (int j16 = i16 - 1; j16 >= 0; j16--)
                    // log.info("collissionDetect " + i + ", " + j);
                    collissionStore.add(collissionDetector.compute(t0, _30, curveStore.getCurve(i16),
                            curveStore.getCurve(j16)), i16, j16);
        }
        final AffineTransform m = new AffineTransform();
        // NaN-safe time range check (are we navigating known ground?):
        while (currentTime > doGetNextHit().t) {
            final Tupel nh = doGetNextHit();
            if (log.isDebugEnabled())
                log.debug(nh.a + " - " + nh.b + " : " + nh.t);
            doUpdatePosAndVel(nh.t, tmpPos, tmpVel);
            // compute collission(s);
            final int mask = collider.compute(tmpPos, tmpVel, m);
            if (mask == 0)
                break;
            doRecomputeCurvesAndCollissionTimes(mask, nh.t, tmpPos, tmpVel);
        }
    }
    doUpdatePosAndVel(currentTime, currentPos, currentVel);
}

From source file:se.ngm.ditaaeps.EpsRenderer.java

public static void renderToEps(Diagram diagram, PrintWriter out, RenderingOptions options) {
    //RenderedImage renderedImage = image;
    EpsGraphics2D g2 = new EpsGraphics2D(out,
            new Rectangle2D.Double(0, -diagram.getHeight(), diagram.getWidth(), diagram.getHeight()));

    g2.scale(1, -1); // g2 origo is top-left, eps is bottom-left

    Object antialiasSetting = antialiasSetting = RenderingHints.VALUE_ANTIALIAS_OFF;
    if (options.performAntialias())
        antialiasSetting = RenderingHints.VALUE_ANTIALIAS_ON;

    //g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antialiasSetting);

    g2.setColor(Color.white);//from  www .j a v a  2 s. c  om
    //TODO: find out why the next line does not work
    //g2.fillRect(0, 0, image.getWidth()+10, image.getHeight()+10);
    /*for(int y = 0; y < diagram.getHeight(); y ++)
      g2.drawLine(0, y, diagram.getWidth(), y);*/

    g2.setStroke(new BasicStroke(1, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND));

    ArrayList shapes = diagram.getAllDiagramShapes();

    if (DEBUG)
        System.out.println("Rendering " + shapes.size() + " shapes (groups flattened)");

    Iterator shapesIt;
    if (options.dropShadows()) {
        //render shadows
        shapesIt = shapes.iterator();
        while (shapesIt.hasNext()) {
            DiagramShape shape = (DiagramShape) shapesIt.next();

            if (shape.getPoints().isEmpty())
                continue;

            //GeneralPath path = shape.makeIntoPath();
            GeneralPath path;
            path = shape.makeIntoRenderPath(diagram);

            float offset = diagram.getMinimumOfCellDimension() / 3.333f;

            if (path != null && shape.dropsShadow()) {
                GeneralPath shadow = new GeneralPath(path);
                AffineTransform translate = new AffineTransform();
                translate.setToTranslation(offset, offset);
                shadow.transform(translate);
                g2.setColor(new Color(150, 150, 150));
                g2.fill(shadow);

            }
        }

        //blur shadows

        //            if(true) {
        //                int blurRadius = 6;
        //                int blurRadius2 = blurRadius * blurRadius;
        //                float blurRadius2F = blurRadius2;
        //                float weight = 1.0f / blurRadius2F;
        //                float[] elements = new float[blurRadius2];
        //                for (int k = 0; k < blurRadius2; k++)
        //                    elements[k] = weight;
        //                Kernel myKernel = new Kernel(blurRadius, blurRadius, elements);
        //
        //                //if EDGE_NO_OP is not selected, EDGE_ZERO_FILL is the default which creates a black border
        //                ConvolveOp simpleBlur =
        //                        new ConvolveOp(myKernel, ConvolveOp.EDGE_NO_OP, null);
        //                //BufferedImage destination = new BufferedImage(image.getWidth()+blurRadius, image.getHeight()+blurRadius, image.getType());
        //                BufferedImage destination =
        //                        new BufferedImage(
        //                        image.getWidth(),
        //                        image.getHeight(),
        //                        image.getType());
        //                simpleBlur.filter(image, destination);
        //                //destination = destination.getSubimage(blurRadius/2, blurRadius/2, image.getWidth(), image.getHeight());
        //                g2 = destination.createGraphics();
        //                g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, antialiasSetting);
        //                renderedImage = destination;
        //            }
    }

    //fill and stroke

    float dashInterval = Math.min(diagram.getCellWidth(), diagram.getCellHeight()) / 2;
    //Stroke normalStroke = g2.getStroke();

    float strokeWeight = diagram.getMinimumOfCellDimension() / 10;

    Stroke normalStroke = new BasicStroke(strokeWeight,
            //10,
            BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);

    Stroke dashStroke = new BasicStroke(strokeWeight, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND, 0,
            new float[] { dashInterval }, 0);

    //find storage shapes
    ArrayList storageShapes = new ArrayList();
    shapesIt = shapes.iterator();
    while (shapesIt.hasNext()) {
        DiagramShape shape = (DiagramShape) shapesIt.next();
        if (shape.getType() == DiagramShape.TYPE_STORAGE) {
            storageShapes.add(shape);
            continue;
        }
    }

    //render storage shapes
    //special case since they are '3d' and should be
    //rendered bottom to top
    //TODO: known bug: if a storage object is within a bigger normal box, it will be overwritten in the main drawing loop
    //(BUT this is not possible since tags are applied to all shapes overlaping shapes)

    Collections.sort(storageShapes, new Shape3DOrderingComparator());

    g2.setStroke(normalStroke);
    shapesIt = storageShapes.iterator();
    while (shapesIt.hasNext()) {
        DiagramShape shape = (DiagramShape) shapesIt.next();

        GeneralPath path;
        path = shape.makeIntoRenderPath(diagram);

        if (!shape.isStrokeDashed()) {
            if (shape.getFillColor() != null)
                g2.setColor(shape.getFillColor());
            else
                g2.setColor(Color.white);
            g2.fill(path);
        }

        if (shape.isStrokeDashed())
            g2.setStroke(dashStroke);
        else
            g2.setStroke(normalStroke);
        g2.setColor(shape.getStrokeColor());
        g2.draw(path);
    }

    //render the rest of the shapes
    ArrayList pointMarkers = new ArrayList();
    shapesIt = shapes.iterator();
    while (shapesIt.hasNext()) {
        DiagramShape shape = (DiagramShape) shapesIt.next();
        if (shape.getType() == DiagramShape.TYPE_POINT_MARKER) {
            pointMarkers.add(shape);
            continue;
        }
        if (shape.getType() == DiagramShape.TYPE_STORAGE) {
            continue;
        }

        if (shape.getPoints().isEmpty())
            continue;

        int size = shape.getPoints().size();

        GeneralPath path;
        path = shape.makeIntoRenderPath(diagram);

        if (path != null && shape.isClosed() && !shape.isStrokeDashed()) {
            if (shape.getFillColor() != null)
                g2.setColor(shape.getFillColor());
            else
                g2.setColor(Color.white);
            g2.fill(path);
        }
        if (shape.getType() != DiagramShape.TYPE_ARROWHEAD) {
            g2.setColor(shape.getStrokeColor());
            if (shape.isStrokeDashed())
                g2.setStroke(dashStroke);
            else
                g2.setStroke(normalStroke);
            g2.draw(path);
        }
    }

    //render point markers

    g2.setStroke(normalStroke);
    shapesIt = pointMarkers.iterator();
    while (shapesIt.hasNext()) {
        DiagramShape shape = (DiagramShape) shapesIt.next();
        //if(shape.getType() != DiagramShape.TYPE_POINT_MARKER) continue;

        GeneralPath path;
        path = shape.makeIntoRenderPath(diagram);

        g2.setColor(Color.white);
        g2.fill(path);
        g2.setColor(shape.getStrokeColor());
        g2.draw(path);
    }

    //handle text
    //g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);

    Iterator textIt = diagram.getTextObjects().iterator();
    while (textIt.hasNext()) {
        DiagramText text = (DiagramText) textIt.next();
        g2.setColor(text.getColor());
        g2.setFont(text.getFont());
        g2.drawString(text.getText(), text.getXPos(), text.getYPos());
    }

    if (options.renderDebugLines() || DEBUG) {
        Stroke debugStroke = new BasicStroke(1, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
        g2.setStroke(debugStroke);
        g2.setColor(new Color(170, 170, 170));
        g2.setXORMode(Color.white);
        for (int x = 0; x < diagram.getWidth(); x += diagram.getCellWidth())
            g2.drawLine(x, 0, x, diagram.getHeight());
        for (int y = 0; y < diagram.getHeight(); y += diagram.getCellHeight())
            g2.drawLine(0, y, diagram.getWidth(), y);
    }

    g2.dispose();
}

From source file:com.mikenimer.familydam.services.photos.ThumbnailService.java

/**
 * get the right transformation based on the orientation setting in the exif metadata. In case the physical image is actually
 * stored in a rotated state.//from  w w  w .ja v  a2 s .  c  o  m
 *
 * @param orientation
 * @param width
 * @param height
 * @return
 */
public static AffineTransform getExifTransformation(int orientation, double width, double height, double scaleW,
        double scaleH) {

    AffineTransform t = new AffineTransform();

    switch (orientation) {
    case 1:
        t.scale(scaleW, scaleH);
        break;
    case 2: // Flip X //todo: test & fix
        t.scale(-scaleW, scaleH);
        t.translate(-width, 0);
        break;
    case 3: // PI rotation
        t.translate(width, height);
        t.rotate(Math.PI);
        t.scale(scaleW, scaleH);
        break;
    case 4: // Flip Y //todo: test & fix
        t.scale(scaleW, -scaleH);
        t.translate(0, -height);
        break;
    case 5: // - PI/2 and Flip X
        t.rotate(-Math.PI / 2);
        t.scale(-scaleW, scaleH);
        break;
    case 6: // -PI/2 and -width
        t.translate(height, 0);
        t.rotate(Math.PI / 2);
        t.scale(scaleW, scaleH);
        break;
    case 7: // PI/2 and Flip //todo:test & fix
        t.scale(-scaleW, scaleH);
        t.translate(-height, 0);
        t.translate(0, width);
        t.rotate(3 * Math.PI / 2);
        break;
    case 8: // PI / 2
        t.translate(0, width);
        t.rotate(3 * Math.PI / 2);
        t.scale(scaleW, scaleH);
        break;
    }

    return t;
}

From source file:com.mucommander.ui.viewer.image.ImageViewer.java

private synchronized void zoom(double factor) {
    setFrameCursor(CURSOR_WAIT);//from  w  w w  .  ja  v  a 2  s.  co m

    final int srcWidth = image.getWidth(null);
    final int srcHeight = image.getHeight(null);
    final int scaledWidth = (int) (srcWidth * factor);
    final int scaledHeight = (int) (srcHeight * factor);

    if (factor != 1.0) {
        AbstractFile file = filesInDirectory.get(indexInDirectory);
        if ("svg".equalsIgnoreCase(file.getExtension())) {
            try {
                this.scaledImage = transcodeSVGDocument(file, scaledWidth, scaledHeight);
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            this.scaledImage = new BufferedImage(scaledWidth, scaledHeight, BufferedImage.TYPE_INT_ARGB);
            AffineTransform at = new AffineTransform();
            at.scale(factor, factor);
            AffineTransformOp scaleOp = new AffineTransformOp(at, AffineTransformOp.TYPE_NEAREST_NEIGHBOR);
            this.scaledImage = scaleOp.filter(this.image, this.scaledImage);
        }
    } else {
        this.scaledImage = image;
    }

    statusBar.setZoom(factor);
    checkZoom();
    setFrameCursor(CURSOR_DEFAULT);
}

From source file:org.geolatte.maprenderer.sld.graphics.ExternalGraphicsRepository.java

private BufferedImage scale(BufferedImage unscaledImage, float size) {
    Dimension dim = getWidthAndHeight(unscaledImage.getWidth(), unscaledImage.getHeight(), size);
    AffineTransform tx = new AffineTransform();
    tx.scale(((double) dim.width / unscaledImage.getWidth()),
            ((double) dim.height) / unscaledImage.getHeight());
    AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BICUBIC);
    return op.filter(unscaledImage, null);
}

From source file:com.ctsim.dmi.MainFrame.java

private void drawPin() {

    //double speed = 100 * (double) x / 1013;
    String speedShow = df.format(App.speed);
    int strWidth;

    AffineTransform restore = g2.getTransform();
    AffineTransform trans = new AffineTransform();
    trans.translate(266, 90);/*from w ww  . j a va2 s.  com*/
    trans.rotate(Math.toRadians(getPinAngle(App.speed)), 40, 165);
    g2.setTransform(trans);
    g2.drawImage(speedoPinWhite, 0, 0, this);
    g2.setTransform(restore);

    // Draw actual speed
    g2.setFont(new Font("Loma", Font.BOLD, 30));
    g2.setColor(Color.BLACK);
    FontMetrics metrics = g2.getFontMetrics();
    strWidth = metrics.stringWidth(speedShow);

    g2.drawString(String.valueOf(speedShow), 306 - strWidth / 2, 265);
}

From source file:org.hydroponics.dao.JDBCHydroponicsDaoImpl.java

public byte[] getThumbnail(BufferedImage buffImage) throws IOException {
    BufferedImage pDestImage = new BufferedImage(Constants.THUMBNAIL_WIDTH, Constants.THUMBNAIL_HEIGHT,
            BufferedImage.TYPE_3BYTE_BGR);

    AffineTransform transform = new AffineTransform();
    transform.scale((float) Constants.THUMBNAIL_WIDTH / (float) buffImage.getWidth(),
            (float) Constants.THUMBNAIL_HEIGHT / (float) buffImage.getHeight());

    Graphics2D g = (Graphics2D) pDestImage.getGraphics();

    //set the rendering hints for a good thumbnail image
    Map m = g.getRenderingHints();
    m.put(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    m.put(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
    m.put(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
    m.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
    g.setRenderingHints(m);//from  ww  w  .j a v a 2 s .c o  m

    g.drawImage(buffImage, transform, null);
    g.dispose();

    ByteArrayOutputStream out = new ByteArrayOutputStream();
    ImageIO.write(pDestImage, "JPEG", out);
    return out.toByteArray();
}

From source file:org.geomajas.plugin.rasterizing.layer.RasterDirectLayer.java

protected void addImage(Graphics2D graphics, ImageResult imageResult, MapViewport viewport) throws IOException {
    Rectangle screenArea = viewport.getScreenArea();
    ReferencedEnvelope worldBounds = viewport.getBounds();
    // convert map bounds to application bounds
    double printScale = screenArea.getWidth() / worldBounds.getWidth();
    if (tileScale < 0) {
        tileScale = printScale;//from   w ww . j  a v  a2  s.  c  o  m
    }
    Envelope applicationBounds = new Envelope((worldBounds.getMinX()) * printScale,
            (worldBounds.getMaxX()) * printScale, -(worldBounds.getMinY()) * printScale,
            -(worldBounds.getMaxY()) * printScale);
    Bbox imageBounds = imageResult.getRasterImage().getBounds();
    // find transform between image bounds and application bounds
    double tx = (imageBounds.getX() * printScale / tileScale - applicationBounds.getMinX());
    double ty = (imageBounds.getY() * printScale / tileScale - applicationBounds.getMinY());
    BufferedImage image = ImageIO.read(new ByteArrayInputStream(imageResult.getImage()));
    double scaleX = imageBounds.getWidth() / image.getWidth() * printScale / tileScale;
    double scaleY = imageBounds.getHeight() / image.getHeight() * printScale / tileScale;
    AffineTransform transform = new AffineTransform();
    transform.translate(tx, ty);
    transform.scale(scaleX, scaleY);
    if (log.isDebugEnabled()) {
        log.debug("adding image, width=" + image.getWidth() + ",height=" + image.getHeight() + ",x=" + tx
                + ",y=" + ty);
    }
    // opacity
    log.debug("before drawImage");
    // create a copy to apply transform
    Graphics2D g = (Graphics2D) graphics.create();
    // apply opacity to image off-graphics to avoid interference with whatever opacity model is used by graphics
    BufferedImage opaqueCopy = makeOpaque(image);
    g.drawImage(opaqueCopy, transform, null);
    log.debug("after drawImage");
}

From source file:edu.purdue.cc.bionet.ui.HeatMap.java

/**
 * This method retrieves a heatmap image from jfreechart and places it on the panel
 * along with black divider lines and labels to create a heat map graph.
 * //from   w w w  .j a v  a  2  s .c  o  m
 * @param g The Graphics for the jpanel.
 */
public void paintComponent(Graphics g) {
    super.paintComponent(g);

    if (moleculeList.size() > 0) {
        float tickStep;
        BufferedImage drawing = HeatMapUtilities.createHeatMapImage(this.getDataset(), this.spectrum);
        int leftEdge = this.getWidth() / 8;
        int topEdge = this.getHeight() / 32;
        int bottomEdge = this.getHeight() * 7 / 8;
        int rightEdge = this.getWidth() * 31 / 32;
        mapPosition = new Rectangle(leftEdge, topEdge, rightEdge - leftEdge, bottomEdge - topEdge);
        g.drawImage(drawing, leftEdge, topEdge, rightEdge - leftEdge, bottomEdge - topEdge,
                this.getBackground(), this);
        // y-axis
        int yAxisPos = leftEdge - 1;
        //      g.drawLine( yAxisPos, topEdge, yAxisPos, bottomEdge );
        tickStep = (bottomEdge - topEdge) / (float) moleculeList.size();
        for (int i = 0; i <= moleculeList.size(); i++) {
            int tickY = Math.round(topEdge + i * tickStep);
            g.drawLine(rightEdge, tickY, yAxisPos - tickSize, tickY);
            if (i < moleculeList.size()) {
                String name = this.moleculeList.get(this.moleculeList.size() - 1 - i).toString();
                g.drawString(name, yAxisPos - 4 - g.getFontMetrics().stringWidth(name),
                        (int) (tickY + tickStep));
            }
        }

        // x-axis
        int xAxisPos = bottomEdge;
        tickStep = (rightEdge - leftEdge) / (float) moleculeList.size();
        //      g.drawLine( leftEdge, xAxisPos, rightEdge, xAxisPos );
        for (int i = 0; i <= moleculeList.size(); i++) {
            int tickX = (int) (leftEdge + i * tickStep);
            g.drawLine(tickX, topEdge, tickX, xAxisPos + tickSize);
        }
        // transform clockwise 90 degrees for the vertical text
        AffineTransform at = new AffineTransform();
        at.quadrantRotate(3);
        Graphics2D g2d = (Graphics2D) g.create();
        g2d.transform(at);
        for (int i = 0; i < moleculeList.size(); i++) {
            int tickX = Math.round(leftEdge + i * tickStep);
            String name = this.moleculeList.get(i).toString();
            g2d.drawString(name, -(int) (xAxisPos + 4 + g.getFontMetrics().stringWidth(name)),
                    (int) (tickX + tickStep));
        }
    }
}