package ch.retorte.intervalmusiccompositor.ui.graphics; import com.sun.javafx.tk.FontMetrics; import com.sun.javafx.tk.Toolkit; import javafx.scene.canvas.Canvas; import javafx.scene.canvas.GraphicsContext; import javafx.scene.image.WritableImage; import javafx.scene.paint.Color; import javafx.scene.text.Font; import javafx.scene.text.FontSmoothingType; import javafx.scene.text.FontWeight; import java.util.Collection; import java.util.List; /** * BarChart class. Used to generate bars with different widths and a specified spacing between. * * @author nw */ public class BarChart { // / The image where the bar chart is drawn. private WritableImage image; /** * Constructor. * * @param width * Width for bar chart. * @param height * Height for bar chart. */ public BarChart(Integer width, Integer height) { image = new WritableImage(width, height); fill(245, 245, 245); } /** * Fills the background of the bar chart. * * @param red * Red value for background (0-255) * @param green * Green value for background (0-255) * @param blue * Blue value for background (0-255) */ private void fill(int red, int green, int blue) { Color color = Color.rgb(red, green, blue); for (int i = 0; i < image.getHeight(); i++) { for (int j = 0; j < image.getWidth(); j++) { image.getPixelWriter().setColor(j, i, color); } } } /** * Generates the bar chart. */ public void generate(List<Integer> soundPattern, List<Integer> breakPattern, Integer iterations, Boolean hasBreakTrack) { Canvas canvas = new Canvas(image.getWidth(), image.getHeight()); GraphicsContext graphicsContext = canvas.getGraphicsContext2D(); graphicsContext.setFontSmoothingType(FontSmoothingType.LCD); graphicsContext.drawImage(image, 0, 0); double absoluteWidth = 0; for (int i = 0; i < soundPattern.size(); i++) { // Add current pattern item absoluteWidth += soundPattern.get(i); // Add current break item (or restart if there are not enough of them) if (has(breakPattern)) { absoluteWidth += breakPattern.get(i % breakPattern.size()); } } absoluteWidth *= iterations; Font soundFont = Font.font("Sans Serif", FontWeight.BOLD, 10); FontMetrics soundFontMetrics = Toolkit.getToolkit().getFontLoader().getFontMetrics(soundFont); Font breakFont = Font.font("Sans Serif", FontWeight.NORMAL, 9); FontMetrics breakFontMetrics = Toolkit.getToolkit().getFontLoader().getFontMetrics(breakFont); Color darkBlue = Color.rgb(55, 119, 248); Color lightBlue = Color.rgb(113, 174, 243); Color evenLighterBlue = Color.rgb(182, 216, 255); Color textColor = Color.GRAY; double top = 0; double bottom = Math.max(soundFontMetrics.getLineHeight(), breakFontMetrics.getLineHeight()) * 1.2; double border = 0; double p = border; double scale = (image.getWidth() - (2.0 * border)) / absoluteWidth; for (int i = 0; i < iterations; i++) { for (int j = 0; j < soundPattern.size(); j++) { // Add next sound pattern item graphicsContext.setFont(soundFont); double width = soundPattern.get(j) * scale; graphicsContext.setFill(lightBlue); graphicsContext.fillRect(p, top, width, image.getHeight() - top - bottom); graphicsContext.setFill(darkBlue); graphicsContext.strokeRect(p, top, width, image.getHeight() - top - bottom); graphicsContext.setFill(textColor); float labelWidth = soundFontMetrics.computeStringWidth(soundPattern.get(j).toString()); if (labelWidth < width) { graphicsContext.fillText(soundPattern.get(j).toString(), p + (width - labelWidth) / 2, image.getHeight() - soundFontMetrics.getDescent()); } p = p + width; // Add next break pattern item graphicsContext.setFont(breakFont); int breakWidth = 0; if (has(breakPattern)) { breakWidth = (int) (breakPattern.get(j % breakPattern.size()) * scale); } // We only paint the break if there is a break track if (hasBreakTrack) { graphicsContext.setFill(evenLighterBlue); graphicsContext.fillRect(p, top, breakWidth, image.getHeight() - top - bottom); graphicsContext.setFill(lightBlue); graphicsContext.strokeRect(p, top, breakWidth, image.getHeight() - top - bottom); } graphicsContext.setFill(textColor); float breakLabelWidth = 0; if (has(breakPattern)) { breakLabelWidth = breakFontMetrics.computeStringWidth(breakPattern.get(j % breakPattern.size()).toString()); } if (breakLabelWidth < breakWidth) { graphicsContext.fillText(breakPattern.get(j % breakPattern.size()).toString(), p + (breakWidth - breakLabelWidth) / 2, image.getHeight() - breakFontMetrics.getDescent()); } if (has(breakPattern)) { p = p + (int) (breakPattern.get(j % breakPattern.size()) * scale); } } } image = canvas.snapshot(null, null); } private boolean has(Collection<?> collection) { return !collection.isEmpty(); } /** * Returns the bar chart as image. * * @return bar chart image. */ public WritableImage getWritableImage() { return image; } }