001package conexp.fx.core.exporter;
002
003/*
004 * #%L
005 * Concept Explorer FX
006 * %%
007 * Copyright (C) 2010 - 2023 Francesco Kriegel
008 * %%
009 * This program is free software: you can redistribute it and/or modify
010 * it under the terms of the GNU General Public License as
011 * published by the Free Software Foundation, either version 3 of the
012 * License, or (at your option) any later version.
013 * 
014 * This program is distributed in the hope that it will be useful,
015 * but WITHOUT ANY WARRANTY; without even the implied warranty of
016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
017 * GNU General Public License for more details.
018 * 
019 * You should have received a copy of the GNU General Public
020 * License along with this program.  If not, see
021 * <http://www.gnu.org/licenses/gpl-3.0.html>.
022 * #L%
023 */
024
025import java.awt.Color;
026import java.awt.Graphics2D;
027import java.awt.RenderingHints;
028import java.awt.image.BufferedImage;
029import java.io.File;
030import java.io.IOException;
031import java.util.Map;
032
033import javax.imageio.ImageIO;
034
035import conexp.fx.core.context.Concept;
036import conexp.fx.core.context.MatrixContext;
037import conexp.fx.core.layout.AdditiveConceptLayout;
038
039public class PNGExporter<G, M> {
040
041  public static <G, M> void export(
042      MatrixContext<G, M> context,
043      Map<Integer, Integer> domainPermutation,
044      Map<Integer, Integer> codomainPermutation,
045      AdditiveConceptLayout<G, M> layout,
046      boolean exportArrows,
047      boolean exportLabels,
048      File file) {
049    try {
050      if (!file.exists()) {
051        if (!file.getParentFile().exists())
052          file.mkdirs();
053        file.createNewFile();
054      }
055      BufferedImage img = toBufferedImage(
056          file.getName(),
057          context,
058          domainPermutation,
059          codomainPermutation,
060          layout,
061          exportArrows,
062          exportLabels);
063      ImageIO.write(img, "png", file);
064    } catch (IOException e) {
065      System.err.println("Unable to create or write GraphicsBuffer to file " + file);
066      e.printStackTrace();
067    }
068  }
069
070  private static <G, M> BufferedImage toBufferedImage(
071      String name,
072      MatrixContext<G, M> formalContext,
073      Map<Integer, Integer> domainPermutation,
074      Map<Integer, Integer> codomainPermutation,
075      AdditiveConceptLayout<G, M> layout,
076      boolean exportArrows,
077      boolean exportLabels) {
078    final double width = 100d * layout.getCurrentBoundingBox(false, false).getWidth();
079    final double minX = 100d * layout.getCurrentBoundingBox(false, false).getMinX();
080    final double height = 100d * layout.getCurrentBoundingBox(false, false).getHeight();
081    // final double unit = Math.min(160 / width, 230 / height);
082    final int border = 100;
083    final int circleSize = 20;
084    final int textOffset = 0;
085    BufferedImage img = new BufferedImage(((int) width) + border, ((int) height) + border, BufferedImage.TYPE_INT_RGB);
086    Graphics2D gfx = img.createGraphics();
087    gfx.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
088    gfx.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
089    gfx.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
090    gfx.setBackground(Color.WHITE);
091    gfx.setColor(Color.WHITE);
092    gfx.fillRect(0, 0, (int) width + border, (int) height + border);
093    gfx.setColor(Color.BLACK);
094    for (int i = 0; i < layout.lattice.rowHeads().size(); i++) {
095      Concept<G, M> conceptNode = layout.lattice.rowHeads().get(i);
096      final double x = 100d * layout.getPosition(conceptNode).getValue().getX();
097      final double y = 100d * layout.getPosition(conceptNode).getValue().getY();
098      gfx.fillOval(
099          (int) (x - minX) - (circleSize / 2) + (border / 2),
100          (int) y - (circleSize / 2) + (border / 2),
101          circleSize,
102          circleSize);
103    }
104    for (int i = 0; i < layout.lattice.rowHeads().size(); i++) {
105      for (int j = 0; j < layout.lattice.rowHeads().size(); j++) {
106        if (layout.lattice._contains(i, j)) {
107          final int x1 = (int) (100d * layout.getPosition(layout.lattice.rowHeads().get(i)).getValue().getX()
108              - minX + (border / 2));
109          final int y1 = (int) (100d * layout.getPosition(layout.lattice.rowHeads().get(i)).getValue().getY()
110              + (border / 2));
111          final int x2 = (int) (100d * layout.getPosition(layout.lattice.rowHeads().get(j)).getValue().getX()
112              - minX + (border / 2));
113          final int y2 = (int) (100d * layout.getPosition(layout.lattice.rowHeads().get(j)).getValue().getY()
114              + (border / 2));
115          gfx.drawLine(x1, y1, x2, y2);
116        }
117      }
118    }
119    if (exportLabels)
120      for (int i = 0; i < layout.lattice.rowHeads().size(); i++) {
121        Concept<G, M> conceptNode = layout.lattice.rowHeads().get(i);
122        String objLabels = layout.lattice.objectLabels(conceptNode).toString().substring(
123            1,
124            layout.lattice.objectLabels(conceptNode).toString().length() - 1);
125        String attLabels = layout.lattice.attributeLabels(conceptNode).toString().substring(
126            1,
127            layout.lattice.attributeLabels(conceptNode).toString().length() - 1);
128        final int x =
129            (int) (100d * layout.getPosition(layout.lattice.rowHeads().get(i)).getValue().getX() - minX);
130        final int y = (int) (100d * layout.getPosition(layout.lattice.rowHeads().get(i)).getValue().getY());
131        final int owidth = gfx.getFontMetrics().stringWidth(objLabels);
132        final int awidth = gfx.getFontMetrics().stringWidth(attLabels);
133        final int theight = gfx.getFontMetrics().getHeight();
134        gfx.drawString(
135            objLabels,
136            x + (border / 2) - (owidth / 2),
137            y + (circleSize + textOffset) + (border / 2) + (theight / 2));
138        gfx.drawString(attLabels, x + (border / 2) - (awidth / 2), y - (circleSize + textOffset) + (border / 2));
139      }
140    gfx.dispose();
141    return img;
142  }
143}