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}