org.jcurl.demo.tactics.sg.BroomPromptScenario.java Source code

Java tutorial

Introduction

Here is the source code for org.jcurl.demo.tactics.sg.BroomPromptScenario.java

Source

/*
 * jcurl java curling software framework http://www.jcurl.org Copyright (C)
 * 2005-2009 M. Rohrmoser
 * 
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
 * details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
 * Place, Suite 330, Boston, MA 02111-1307 USA
 */

package org.jcurl.demo.tactics.sg;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Paint;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Arc2D;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.BoundedRangeModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import org.apache.commons.logging.Log;
import org.jcurl.core.api.IceSize;
import org.jcurl.core.api.RockProps;
import org.jcurl.core.api.RockSet;
import org.jcurl.core.log.JCLoggerFactory;
import org.jcurl.core.ui.BroomPromptModel;
import org.jcurl.core.ui.ChangeManager;
import org.jcurl.core.ui.IceShapes;
import org.jcurl.core.ui.BroomPromptModel.HandleMemento;
import org.jcurl.core.ui.BroomPromptModel.SplitMemento;
import org.jcurl.core.ui.BroomPromptModel.XYMemento;
import org.jcurl.demo.tactics.HasChanger;
import org.jcurl.math.MathVec;

import com.sun.scenario.scenegraph.SGGroup;
import com.sun.scenario.scenegraph.SGNode;
import com.sun.scenario.scenegraph.SGShape;
import com.sun.scenario.scenegraph.SGTransform;
import com.sun.scenario.scenegraph.SGAbstractShape.Mode;
import com.sun.scenario.scenegraph.SGTransform.Affine;
import com.sun.scenario.scenegraph.event.SGMouseAdapter;

/**
 * @author <a href="mailto:m@jcurl.org">M. Rohrmoser </a>
 * @version $Id$
 */
public class BroomPromptScenario implements PropertyChangeListener, ChangeListener, HasChanger {

    private class MoveHandler extends SGMouseAdapter {
        private XYMemento first = null;

        private XYMemento create(final MouseEvent e, final SGNode node) {
            final Point2D p = dc2wc.globalToLocal(e.getPoint(), tmp);
            return new XYMemento(model, p);
        }

        @Override
        public void mouseClicked(final MouseEvent e, final SGNode node) {
            if (e.getClickCount() != 2)
                return;
            final HandleMemento pre = new HandleMemento(model, model.getOutTurn());
            final HandleMemento post = new HandleMemento(model, !model.getOutTurn());
            getChanger().undoable(pre, post);
        }

        @Override
        public void mouseDragged(final MouseEvent e, final SGNode node) {
            if (first == null)
                first = new XYMemento(model, model.getBroom());
            getChanger().temporary(create(e, node));
        }

        @Override
        public void mouseReleased(final MouseEvent e, final SGNode node) {
            if (first != null)
                getChanger().undoable(first, create(e, node));
            first = null;
        }
    }

    private class SpeedHandler extends SGMouseAdapter {
        private SplitMemento first = null;

        private SplitMemento create(final MouseEvent e, final SGNode node) {
            final Point2D p = handle.globalToLocal(e.getPoint(), tmp);
            double f = 1 - p.getY() / stickLength;
            if (f > 1)
                f = 1;
            if (f < 0)
                f = 0;
            final BoundedRangeModel brm = model.getSplitTimeMillis();
            final int v = (int) (brm.getMinimum() + f * (brm.getMaximum() - brm.getMinimum()));
            return new SplitMemento(model, v);
        }

        @Override
        public void mouseClicked(final MouseEvent e, final SGNode node) {
            if (e.getClickCount() != 2)
                return;
            final HandleMemento pre = new HandleMemento(model, model.getOutTurn());
            final HandleMemento post = new HandleMemento(model, !model.getOutTurn());
            getChanger().undoable(pre, post);
        }

        @Override
        public void mouseDragged(final MouseEvent e, final SGNode node) {
            if (first == null)
                first = new SplitMemento(model, model.getSplitTimeMillis().getValue());
            // create(e, node);
            getChanger().temporary(create(e, node));
        }

        @Override
        public void mouseReleased(final MouseEvent e, final SGNode node) {
            if (first != null)
                getChanger().undoable(first, create(e, node));
            first = null;
        }
    }

    private static final Log log = JCLoggerFactory.getLogger(BroomPromptScenario.class);

    private static final Cursor moveC = Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR);

    private static final double scale0 = 0;
    private static final int scale50 = 50;

    static SGShape node(final Shape sh, final Stroke st, final Paint sp, final double minScale) {
        final SGShape s = new SGShape();
        s.setShape(sh);
        if (sp != null)
            s.setDrawPaint(sp);
        if (st != null)
            s.setDrawStroke(st);
        s.setAntialiasingHint(RenderingHints.VALUE_ANTIALIAS_ON);
        if (st != null && sp != null)
            s.setMode(Mode.STROKE);
        else if (sp != null)
            s.setMode(Mode.FILL);
        return s;
    }

    /** adjust position + rotation */
    private static void syncBroomM2V(final Point2D b, final Affine scene) {
        if (b == null)
            return;
        final AffineTransform t = scene.getAffine();
        t.setToIdentity();
        t.translate(b.getX(), b.getY());
        MathVec.rotate(t, b.getX(), b.getY() - IceSize.FAR_HACK_2_TEE);
        MathVec.rotate(t, 0, 1);
        scene.setAffine(t);
    }

    private static void syncHandleM2V(final boolean outTurn, final Affine handle) {
        final AffineTransform t = handle.getAffine();
        t.setToIdentity();
        if (outTurn)
            t.scale(-1, 1);
        handle.setAffine(t);
    }

    private static void syncIndexM2V(final int i16, final SGShape pie) {
        if (pie != null)
            pie.setFillPaint(RockSet.isDark(i16) ? Color.RED : Color.YELLOW);
    }

    private ChangeManager changer;
    private Affine dc2wc;
    private final Affine handle;
    private BroomPromptModel model;
    private final SGShape pie;
    private final Affine scene;
    private final SGShape sli;
    private final Affine slider;
    private final float stickLength;
    private final Point2D tmp = new Point2D.Double();

    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
        {
            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);
    }

    public ChangeManager getChanger() {
        return ChangeManager.getTrivial(changer);
    }

    public Affine getDc2Wc() {
        return dc2wc;
    }

    public BroomPromptModel getModel() {
        return model;
    }

    public SGNode getScene() {
        return scene;
    }

    public void propertyChange(final PropertyChangeEvent evt) {
        if (evt.getSource() == model) {
            if ("idx16".equals(evt.getPropertyName()))
                syncIndexM2V((Integer) evt.getNewValue(), pie);
            else if ("outTurn".equals(evt.getPropertyName()))
                syncHandleM2V((Boolean) evt.getNewValue(), handle);
            else if ("broom".equals(evt.getPropertyName()))
                syncBroomM2V((Point2D) evt.getNewValue(), scene);
            else if ("splitTimeMillis".equals(evt.getPropertyName())) {
                final BoundedRangeModel os = (BoundedRangeModel) evt.getOldValue();
                if (os != null)
                    os.removeChangeListener(this);
                final BoundedRangeModel ns = (BoundedRangeModel) evt.getNewValue();
                if (ns != null)
                    ns.addChangeListener(this);
                syncSpeedM2V(ns);
            } else
                log.info(evt.getPropertyName() + " " + evt.getSource());
        } else
            log.warn("Unconsumed event from " + evt.getSource());
    }

    public void setChanger(final ChangeManager changer) {
        final ChangeManager old = this.changer;
        if (old == changer)
            return;
        this.changer = changer;
        // firePropertyChange("changer", old, this.changer);
    }

    public void setDc2wc(final Affine dc2wc) {
        this.dc2wc = dc2wc;
    }

    public void setModel(final BroomPromptModel model) {
        if (this.model == model)
            return;
        if (this.model != null) {
            this.model.removePropertyChangeListener(this);
            if (this.model.getSplitTimeMillis() != null)
                this.model.getSplitTimeMillis().removeChangeListener(this);
        }
        this.model = model;
        scene.setVisible(true || this.model != null && model.getBroom() != null);
        if (this.model != null) {
            // setBroom(this.model.getBroom());
            // setIdx16(this.model.getIdx16());
            // setOutTurn(this.model.getOutTurn());
            // setSlider(this.model.getSplitTimeMillis());
            this.model.addPropertyChangeListener(this);
            if (this.model.getSplitTimeMillis() != null)
                this.model.getSplitTimeMillis().addChangeListener(this);
        }
    }

    public void stateChanged(final ChangeEvent e) {
        if (e.getSource() instanceof BoundedRangeModel)
            syncSpeedM2V((BoundedRangeModel) e.getSource());
        else
            log.warn("Unconsumed event from " + e.getSource());
    }

    private void syncSpeedM2V(final BoundedRangeModel r) {
        // log.info(r.getValue() + "/" + r.getMaximum());
        sli.setFillPaint(IceShapes.sliderColor(r));
        final AffineTransform t = slider.getAffine();
        t.setToTranslation(0, stickLength * IceShapes.value2ratio(r));
        slider.setAffine(t);
    }
}