org.jcurl.demo.tactics.TrajectoryScenarioBean.java Source code

Java tutorial

Introduction

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

Source

/*
 * jcurl curling simulation 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;

import java.awt.BorderLayout;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.geom.AffineTransform;
import java.awt.geom.Point2D;
import java.awt.geom.RectangularShape;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

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

import org.apache.commons.logging.Log;
import org.jcurl.core.api.ComputedTrajectorySet;
import org.jcurl.core.api.Rock;
import org.jcurl.core.api.RockSet;
import org.jcurl.core.api.RockSetUtils;
import org.jcurl.core.api.RockType.Pos;
import org.jcurl.core.log.JCLoggerFactory;
import org.jcurl.core.ui.BroomPromptModel;
import org.jcurl.core.ui.ChangeManager;
import org.jcurl.core.ui.PosMemento;
import org.jcurl.demo.tactics.sg.AnimateAffine;
import org.jcurl.demo.tactics.sg.BroomPromptScenario;
import org.jcurl.demo.tactics.sg.SGIceFactory;
import org.jcurl.demo.tactics.sg.SGRockFactory;
import org.jcurl.demo.tactics.sg.SGTrajectoryFactory;
import org.jcurl.math.R1RNFunction;

import com.sun.scenario.scenegraph.JSGPanel;
import com.sun.scenario.scenegraph.SGComposite;
import com.sun.scenario.scenegraph.SGGroup;
import com.sun.scenario.scenegraph.SGNode;
import com.sun.scenario.scenegraph.SGRenderCache;
import com.sun.scenario.scenegraph.SGTransform;
import com.sun.scenario.scenegraph.SGComposite.OverlapBehavior;
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 TrajectoryScenarioBean extends TrajectoryBean<Affine, SGGroup> implements ChangeListener {
    private class MoveHandler extends SGMouseAdapter {
        private PosMemento first = null;
        private final Point2D tmp = new Point2D.Double(0, 0);

        @SuppressWarnings("unchecked")
        private PosMemento create(final MouseEvent e, final SGNode node) {
            final RockSet<Pos> rs = (RockSet<Pos>) node.getAttribute(ATTR_ROCKSET);
            final Point2D p = dc2wc.globalToLocal(e.getPoint(), tmp);
            if (log.isDebugEnabled())
                log.debug(p);
            return new PosMemento(rs, (Integer) node.getAttribute(ATTR_IDX16), p);
        }

        @Override
        public void mouseDragged(final MouseEvent e, final SGNode node) {
            if (log.isDebugEnabled())
                log.debug(node.getAttribute(ATTR_IDX16));
            getChanger().temporary(create(e, node));
        }

        @Override
        public void mousePressed(final MouseEvent e, final SGNode node) {
            if (log.isDebugEnabled())
                log.debug(node.getAttribute(ATTR_IDX16));
            final int i16 = (Integer) node.getAttribute(ATTR_IDX16);
            final RockSet<Pos> ipos = getCurves().getInitialPos();
            first = new PosMemento(ipos, i16, ipos.getRock(i16).p());
        }

        @Override
        public void mouseReleased(final MouseEvent e, final SGNode node) {
            if (log.isDebugEnabled())
                log.debug(node.getAttribute(ATTR_IDX16));
            getChanger().undoable(first, create(e, node));
        }
    }

    private static final boolean DoCacheRocks = true;
    private static final Log log = JCLoggerFactory.getLogger(TrajectoryScenarioBean.class);
    private static final long serialVersionUID = 6661957210899967106L;

    private static Affine createSceneRock(final int i16, final int opacity) {
        SGNode rock = new SGRockFactory.Fancy().newInstance(i16);
        if (DoCacheRocks) {
            final SGRenderCache rc = new SGRenderCache();
            rc.setChild(rock);
            rc.setInterpolationHint(RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
            rock = rc;
        }
        return SGTransform.createAffine(new AffineTransform(), rock);
    }

    /** update one curve's path */
    private static void syncM2V(final ComputedTrajectorySet src, final int i16, final SGGroup[] dst,
            final SGTrajectoryFactory tf) {
        syncM2V(src.getCurveStore().iterator(i16), i16, dst[i16], tf);

    }

    /** update one curves' path */
    private static void syncM2V(final Iterator<Entry<Double, R1RNFunction>> src, final int i16, final SGGroup dst,
            final SGTrajectoryFactory tf) {
        if (log.isDebugEnabled())
            log.debug("path " + i16);
        tf.refresh(src, RockSet.isDark(i16), 0, 30, dst);
    }

    /** update one rock */
    private static void syncM2V(final Rock<Pos> src, final Affine dst) {
        if (src == null || dst == null) {
            if (log.isDebugEnabled())
                log.debug(src + " " + dst);
            return;
        }
        if (log.isDebugEnabled())
            log.debug("rock " + dst.getAttribute(ATTR_IDX16));
        dst.setAffine(src.getAffineTransform());
    }

    private final BroomPromptScenario broom = new BroomPromptScenario();
    private final Affine[] current = new Affine[RockSet.ROCKS_PER_SET];
    private final Affine dc2wc;
    private final Affine[] initial = new Affine[RockSet.ROCKS_PER_SET];
    private final MoveHandler mouse = new MoveHandler();
    /** all rocks, trajectories and broomprompt */
    private final SGComposite opa_r0 = new SGComposite();
    private final SGComposite opa_r1 = new SGComposite();
    private final SGComposite opa_t0 = new SGComposite();
    private final JSGPanel panel;
    private final SGGroup[] path = new SGGroup[RockSet.ROCKS_PER_SET];
    /** Rock<Pos> -> SGNode lookup */
    private final Map<Rock<Pos>, Affine> r2n = new IdentityHashMap<Rock<Pos>, Affine>();
    private final SGGroup rocks = new SGGroup();
    private final transient SGTrajectoryFactory tf = new SGTrajectoryFactory.Fancy();
    private final Affine zoom;

    public TrajectoryScenarioBean() {
        setVisible(false);
        broom.setModel(tt);
        panel = new JSGPanel();
        setLayout(new BorderLayout());
        add(panel, BorderLayout.CENTER);

        final SGGroup world = new SGGroup();
        world.add(new SGIceFactory.Fancy().newInstance());

        // rocks.setVisible(false);
        final SGGroup r0 = new SGGroup();
        final SGGroup r1 = new SGGroup();
        final SGGroup pa = new SGGroup();
        // rocks.add(traj);
        final RockSet<Pos> home = RockSetUtils.allHome();
        final RockSet<Pos> out = RockSetUtils.allOut();
        for (int i16 = RockSet.ROCKS_PER_SET - 1; i16 >= 0; i16--) {
            Affine n = createSceneRock(home, i16, 255);
            n.setMouseBlocker(true);
            n.addMouseListener(mouse);
            n.setCursor(CURSOR);
            r0.add(initial[i16] = n);
            r1.add(current[i16] = n = createSceneRock(out, i16, 255));
            n.putAttribute(ATTR_TRIGGER_CURVE_UPDATE, true);
            pa.add(path[i16] = new SGGroup());
        }
        if (false) {
            rocks.add(pa);
            rocks.add(r0);
            rocks.add(r1);
        } else {
            opa_r0.setChild(r0);
            opa_r0.setOpacity(64.0F / 255.0F);
            opa_r0.setOverlapBehavior(OverlapBehavior.LAYER);

            opa_r1.setChild(r1);
            opa_r1.setOverlapBehavior(OverlapBehavior.LAYER);

            opa_t0.setChild(pa);
            opa_t0.setMouseBlocker(true);
            opa_t0.setOverlapBehavior(OverlapBehavior.LAYER);
            opa_t0.setOpacity(100.0F / 255.0F);

            rocks.add(opa_t0);
            rocks.add(opa_r0);
            rocks.add(opa_r1);
        }
        rocks.add(broom.getScene());
        rocks.setVisible(false);
        world.add(rocks);

        // make world right-handed:
        final AffineTransform rightHand = AffineTransform.getScaleInstance(1, -1);
        zoom = SGTransform.createAffine(new AffineTransform(), dc2wc = SGTransform.createAffine(rightHand, world));
        broom.setDc2wc(dc2wc);
        panel.setScene(zoom);
        setVisible(true);
    }

    private Affine createSceneRock(final RockSet<Pos> pos, final int i16, final int opacity) {
        return syncM2V(pos, i16, createSceneRock(i16, opacity));
    }

    /**
     * keep the recent viewport visible
     * 
     * @Override public void doLayout() { super.doLayout(); if (tmpViewPort !=
     *           null) zoom.setAffine(AnimateAffine.map(tmpViewPort,
     *           this.getBounds(), zoom.getAffine())); }
     */
    @Override
    public BroomPromptModel getBroom() {
        return broom.getModel();
    }

    Affine getDc2Wc() {
        return dc2wc;
    }

    @Override
    public void setChanger(final ChangeManager changer) {
        super.setChanger(changer);
        broom.setChanger(changer);
    }

    @Override
    public void setCurves(final ComputedTrajectorySet curves) {
        // rocks.setVisible(model != null);
        if (this.curves != null) {
            removeCL(this.curves.getInitialPos());
            removeCL(this.curves.getCurrentPos());
        }
        this.curves = curves;
        r2n.clear();
        if (this.curves != null) {
            final RockSet<Pos> ip = this.curves.getInitialPos();
            final RockSet<Pos> cp = this.curves.getCurrentPos();
            addCL(ip);
            addCL(cp);
            for (int i16 = RockSet.ROCKS_PER_SET - 1; i16 >= 0; i16--) {
                syncM2V(ip, i16, initial[i16]);
                syncM2V(cp, i16, current[i16]);
                syncM2V(getCurves(), i16, path, tf);
            }
        }
        tt.init(this.curves);
        rocks.setVisible(this.curves != null);
    }

    public void setZoom(final RectangularShape viewport, final int transitionMillis) {
        AnimateAffine.animateToCenterBounds(zoom, this.getBounds(), tmpViewPort = viewport, transitionMillis);
    }

    public void stateChanged(final ChangeEvent e) {
        if (e.getSource() instanceof Rock) {
            // update the rock position, be it initial or current
            final Rock<Pos> r = (Rock<Pos>) e.getSource();
            final Affine n = r2n.get(r);
            syncM2V(r, n);

            // update the trajectory path, current only.
            if (!Boolean.TRUE.equals(n.getAttribute(ATTR_TRIGGER_CURVE_UPDATE)))
                return;
            final int i16 = (Integer) n.getAttribute(ATTR_IDX16);
            syncM2V(getCurves(), i16, path, tf);
        } else if (log.isDebugEnabled())
            log.debug("Unconsumed event from " + e.getSource());
    }

    /**
     * rather an "init" than sync. Don't use with high frequency.
     * 
     * @param src
     * @param i16
     * @param dst
     * @return
     */
    private Affine syncM2V(final RockSet<Pos> src, final int i16, final Affine dst) {
        final Rock<Pos> r = src.getRock(i16);
        syncM2V(r, dst);
        dst.putAttribute(ATTR_ROCKSET, src);
        dst.putAttribute(ATTR_ROCK, r);
        dst.putAttribute(ATTR_IDX16, i16);
        r2n.put(r, dst);
        return dst;
    }
}