All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.timboudreau.vl.jung.demo.DemoWidget Maven / Gradle / Ivy

/* 
 * Copyright (c) 2013, Tim Boudreau
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice, this
 *   list of conditions and the following disclaimer.
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */
package com.timboudreau.vl.jung.demo;

import com.timboudreau.vl.jung.JungScene;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.color.ColorSpace;
import java.awt.geom.AffineTransform;
import java.awt.geom.RoundRectangle2D;
import java.util.HashMap;
import java.util.Map;
import org.netbeans.api.visual.action.ActionFactory;
import org.netbeans.api.visual.action.TextFieldInplaceEditor;
import org.netbeans.api.visual.widget.Widget;
import org.openide.util.Lookup;
import org.openide.util.lookup.Lookups;

/**
 *
 * @author Tim Boudreau
 */
public class DemoWidget extends Widget {

    private Stroke stroke = new BasicStroke(2);
    private String label = "";
    final N node;
    private final Lookup lkp;

    public DemoWidget(JungScene scene, N node) {
        super(scene);
        lkp = Lookups.fixed(node);
        this.node = node;
        setBackground(new Color(240, 240, 255));
        setForeground(Color.gray);
        getActions().addAction(ActionFactory.createInplaceEditorAction(new TextFieldInplaceEditor() {

            @Override
            public boolean isEnabled(Widget widget) {
                return true;
            }

            @Override
            public String getText(Widget widget) {
                return label;
            }

            @Override
            public void setText(Widget widget, String text) {
                label = text;
                revalidate();
            }
        }));
    }

    @Override
    public Lookup getLookup() {
        return lkp;
    }

    public void setLabel(String label) {
        this.label = label;
    }

    public void setStroke(Stroke stroke) {
        this.stroke = stroke;
    }

    private JungScene scene() {
        return (JungScene) getScene();
    }

    private Shape getShape() {
        // Get the node
        N node = (N) scene().findObject(this);
        // Grow the shape based on the number of connections
        int ix = scene().graph().getNeighborCount(node);
        // Minimum size 18 pixels, grow by 3 for each connection
        double w, h;
        w = h = 18 + (ix * 3);
        
        double labelWidth = getGraphics().getFontMetrics().stringWidth(label);
        w = Math.max(w, labelWidth + 18);
        
//        Ellipse2D.Double e = new Ellipse2D.Double(0, 0, w, h);
        RoundRectangle2D.Double e = new RoundRectangle2D.Double(-w / 2D, -h / 2D, w, h, 8, 8);
        return e;
    }

    @Override
    protected Rectangle calculateClientArea() {
        // Stroke the shape to make sure we include the line width in our
        // bounding box
        return stroke.createStrokedShape(getShape()).getBounds();
    }

    private Paint getPaint() {
        Color start = (Color) getBackground();
        Color end = start.brighter();
        Rectangle r = getClientArea();
        float x = r.x;
        float y = r.y;
        float x1 = r.x + r.width;
        float y1 = r.y + r.height;
        GradientPaint result;
        if (getScene().getSceneAnimator().isAnimatingBackgroundColor(this)) {
            // Don't cache transient gradient paints, just ones that will
            // be used repeatedly
            result = new GradientPaint(x, y, start, x1, y1, end);
        } else {
            result = CACHE.getPaint(x, y, start, x1, y1, end);
        }
        return result;
    }

    @Override
    protected void paintWidget() {
        Graphics2D g = getGraphics();

        g.setFont(getFont());
        g.setStroke(stroke);
        // First fill the shape
        Shape shape = getShape();
        // Set up our gradient
        g.setPaint(getPaint());
        g.fill(shape);

        // Now draw the outline
        g.setPaint(getForeground());
        g.draw(shape);

        // Now draw toString() on the node
        float ht = g.getFontMetrics().getHeight();
        float w = g.getFontMetrics().stringWidth(label);
        Rectangle r = getClientArea();
        g.setColor(getTextColor());
        float y = (float) r.getCenterY() - (ht / 2F);
        y += g.getFontMetrics().getMaxAscent();
        float x = (float) r.getCenterX() - (w / 2F);
        g.drawString(label, x, y);

        // Draw a highlight shape if it is selected
        if (scene().getSelection().isSelected(scene().findObject(this))) {
            g.setColor(new Color(150, 150, 250));
            AffineTransform scale = AffineTransform.getScaleInstance(0.8, 0.8);
            double width = (shape.getBounds().getWidth() * 0.0125D);
            scale.concatenate(AffineTransform.getTranslateInstance(width, width));
            shape = scale.createTransformedShape(shape);
            g.draw(shape);
        }
    }

    private static GPCache CACHE = new GPCache();

    private Color getTextColor() {
        // Convoluted but works.
        Color c = (Color) getBackground();
        // Get the grascale version of the color, so that we handle perceptual
        // differences in dark/light - a highly saturated blue is bright according
        // to HSB a dark color is not readable against it
        ColorSpace grayscale = ColorSpace.getInstance(ColorSpace.CS_GRAY);
        float[] rgb = new float[]{(float) c.getRed() / 255F,
            (float) c.getGreen() / 255F, (float) c.getBlue() / 255F};
//        rgb = grayscale.fromRGB(rgb);
        // Invert the grayscale version of all color components
        for (int i = 0; i < rgb.length; i++) {
            rgb[i] = 1F - rgb[i];
        }
        // Convert it back to RGB values between 0.0F and 1.0F
        rgb = grayscale.toRGB(rgb);
        // Convert it back to a color, doing some additional computation - 
        // we want values very close to 0.5F - neutral gray - not to be
        // indistinguishable, so have values between 0.3 and 0.7 repel
        // the value
        return new Color(toByteValue(rgb[0]), toByteValue(rgb[1]), toByteValue(rgb[2]));
    }

    private int toByteValue(float f) {
        float dist = 0.5F - f;
        // bounce the value away from 0.5
        if (Math.abs(dist) < 0.4) {
            f *= 1F - dist;
        }
        // multiply it back into a Color value and constrain it within 0-255
        float result = Math.min(255F, Math.max(0F, f * 255F));
        return (int) result;
    }

    /**
     * GradientPaint allocates a fairly large byte[] raster; since the number of
     * these needed is finite, we use a cache to reduce memory pressure.
     */
    private static class GPCache {

        private final Map paints = new HashMap<>();

        public GradientPaint getPaint(float x, float y, Color start, float x1, float y1, Color end) {
            // A key that will be unique by the passed parameters
            String key = (int) x + "-" + (int) y + "-" + (int) x1 + "-" + (int) y1 + ':' + c2s(start) + ',' + c2s(end);
            GradientPaint p = paints.get(key);
            if (p == null) {
                if (paints.size() > 50) {
                    // Don't let the cache get huge
                    paints.clear();
                }
                p = new GradientPaint(x, y, start, x1, y1, end);
                paints.put(key, p);
            }
            return p;
        }

        private String c2s(Color color) {
            return Integer.toHexString(color.getRGB());
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy