org.jcurl.core.model.CurveManager.java Source code

Java tutorial

Introduction

Here is the source code for org.jcurl.core.model.CurveManager.java

Source

/*
 * jcurl curling simulation framework http://www.jcurl.org
 * Copyright (C) 2005-2007 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.core.model;

import java.awt.geom.AffineTransform;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import org.apache.commons.logging.Log;
import org.jcurl.core.base.Collider;
import org.jcurl.core.base.CollissionDetector;
import org.jcurl.core.base.ComputedTrajectorySet;
import org.jcurl.core.base.CurveStore;
import org.jcurl.core.base.CurveTransformed;
import org.jcurl.core.base.PositionSet;
import org.jcurl.core.base.Rock;
import org.jcurl.core.base.RockSet;
import org.jcurl.core.base.SlideBase;
import org.jcurl.core.base.Slider;
import org.jcurl.core.base.SpeedSet;
import org.jcurl.core.helpers.MutableObject;
import org.jcurl.core.log.JCLoggerFactory;
import org.jcurl.core.model.CollissionStore.Tupel;
import org.jcurl.math.R1RNFunction;

/**
 * Bring it all together and trigger computation.
 * 
 * @author <a href="mailto:jcurl@gmx.net">M. Rohrmoser </a>
 * @version $Id$
 */
public class CurveManager extends MutableObject implements PropertyChangeListener, ComputedTrajectorySet {

    private static final double _30 = 30.0;

    private static final double hitDt = 1e-6;

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

    private static final long serialVersionUID = 7198540442889130378L;

    private Collider collider = null;

    private CollissionDetector collissionDetector = null;

    private final CollissionStore collissionStore = new CollissionStore();

    private final PositionSet currentPos = new PositionSet();

    private final SpeedSet currentSpeed = new SpeedSet();

    private double currentTime = 0;

    private CurveStore curveStore = new CurveStore(RockSet.ROCKS_PER_SET);

    private boolean dirty = true;

    private PositionSet initialPos = null;

    private SpeedSet initialSpeed = null;

    private Slider slider = null;

    public CurveManager() {
    }

    /**
     * Internal. Compute one rock curve segment and don't change internal state.
     * 
     * @param i
     *            which rock
     * @param t0
     *            starttime
     * @return the new Curve in world coordinates.
     */
    R1RNFunction doComputeCurve(final int i, final double t0, final PositionSet p, final SpeedSet s) {
        final Rock x = p.getRock(i);
        final Rock v = s.getRock(i);
        final R1RNFunction wc;
        if (v.distanceSq(0, 0) == 0)
            wc = SlideBase.still(x);
        else
            // FIXME add stop detection! Either here or in each slider?
            wc = new CurveTransformed(slider.computeRc(x, v),
                    CurveTransformed.createRc2Wc(new AffineTransform(), x, v), t0);
        if (log.isDebugEnabled())
            log.debug(i + " " + wc);
        return wc;
    }

    /**
     * Internal.
     * 
     * @return when is the next hit, which are the rocks involved.
     */
    Tupel doGetNextHit() {
        doInit();
        return collissionStore.first();
    }

    /**
     * Internal. Compute initial curves and the first hit of each combination of
     * 2 rocks if the dirty flag is set.
     */
    void doInit() {
        if (!dirty)
            return;
        final double t0 = 0.0;
        // initial curves:
        for (int i = RockSet.ROCKS_PER_SET - 1; i >= 0; i--) {
            curveStore.reset(i);
            curveStore.add(i, t0, doComputeCurve(i, t0, initialPos, initialSpeed));
        }
        // initial collission detection:
        collissionStore.clear();
        for (int i = RockSet.ROCKS_PER_SET - 1; i >= 0; i--)
            for (int j = i - 1; j >= 0; j--)
                // log.info("collissionDetect " + i + ", " + j);
                collissionStore.add(
                        collissionDetector.compute(t0, _30, curveStore.getCurve(i), curveStore.getCurve(j)), i, j);
        dirty = false;
    }

    /**
     * Internal. Typically after a hit: Recompute the new curves and upcoming
     * collission candidates.
     * 
     * @param hitMask
     * @return bitmask of rocks with new curves
     */
    int doRecomputeCurvesAndCollissionTimes(final int hitMask, double t0) {
        int computedMask = 0;
        // first computed the new curves:
        for (int i = RockSet.ROCKS_PER_SET - 1; i >= 0; i--) {
            if (!RockSet.isSet(hitMask, i))
                continue;
            curveStore.add(i, t0, doComputeCurve(i, t0, currentPos, currentSpeed));
            computedMask |= 1 << i;
        }
        // then and all combinations of potential collissions
        t0 += hitDt;
        for (int i = RockSet.ROCKS_PER_SET - 1; i >= 0; i--) {
            if (!RockSet.isSet(computedMask, i))
                continue;
            for (int j = RockSet.ROCKS_PER_SET - 1; j >= 0; j--) {
                if (i == j || i > j && RockSet.isSet(computedMask, j))
                    continue;
                collissionStore.replace(
                        collissionDetector.compute(t0, _30, curveStore.getCurve(i), curveStore.getCurve(j)), i, j);
            }
        }
        return computedMask;
    }

    /**
     * Internal. Does not {@link RockSet#notifyChange()}!
     * 
     * @param currentTime
     * @param tmp
     */
    void doUpdatePosAndSpeed(final double currentTime, final double[] tmp) {
        for (int i = RockSet.ROCKS_PER_SET - 1; i >= 0; i--) {
            currentPos.getRock(i).setLocation(curveStore.getCurve(i).at(0, currentTime, tmp));
            currentSpeed.getRock(i).setLocation(curveStore.getCurve(i).at(1, currentTime, tmp));
        }
    }

    @Override
    public boolean equals(final Object obj) {
        return false;
    }

    public Collider getCollider() {
        return collider;
    }

    public CollissionDetector getCollissionDetector() {
        return collissionDetector;
    }

    public PositionSet getCurrentPos() {
        return currentPos;
    }

    public SpeedSet getCurrentSpeed() {
        return currentSpeed;
    }

    public double getCurrentTime() {
        return currentTime;
    }

    public CurveStore getCurveStore() {
        return curveStore;
    }

    public PositionSet getInitialPos() {
        return initialPos;
    }

    public SpeedSet getInitialSpeed() {
        return initialSpeed;
    }

    public Slider getSlider() {
        return slider;
    }

    @Override
    public int hashCode() {
        return 0;
    }

    public void propertyChange(final PropertyChangeEvent arg0) {
        log.info(arg0);
    }

    public void setCollider(final Collider collider) {
        dirty = true;
        propChange.firePropertyChange("collider", this.collider, collider);
        this.collider = collider;
    }

    public void setCollissionDetector(final CollissionDetector collissionDetector) {
        dirty = true;
        propChange.firePropertyChange("collissionDetector", this.collissionDetector, collissionDetector);
        this.collissionDetector = collissionDetector;
    }

    public void setCurrentTime(final double currentTime) {
        // log.info(Double.toString(currentTime));
        if (!dirty) {
            if (this.currentTime == currentTime)
                return;
        } else
            doInit();
        {
            // TUNE thread safety at the cost of two instanciations per call:
            final double[] tmp = { 0, 0, 0 };
            final AffineTransform m = new AffineTransform();
            // NaN-safe time range check (are we navigating known ground?):
            while (currentTime > doGetNextHit().t) {
                final Tupel nh = doGetNextHit();
                doUpdatePosAndSpeed(nh.t, tmp);
                // compute collission(s);
                final int mask = collider.compute(currentPos, currentSpeed, m);
                if (mask == 0)
                    break;
                doRecomputeCurvesAndCollissionTimes(mask, nh.t);
            }
            doUpdatePosAndSpeed(currentTime, tmp);
        }
        {
            final double ot = this.currentTime;
            this.currentTime = currentTime;
            currentPos.notifyChange();
            currentSpeed.notifyChange();
            propChange.firePropertyChange("currentTime", ot, currentTime);
            propChange.firePropertyChange("currentPos", currentPos, currentPos);
            propChange.firePropertyChange("currentSpeed", currentSpeed, currentSpeed);
        }
    }

    public void setCurveStore(final CurveStore curveStore) {
        dirty = true;
        propChange.firePropertyChange("curveStore", this.curveStore, curveStore);
        this.curveStore = curveStore;
    }

    public void setInitialPos(final PositionSet initialPos) {
        dirty = true;
        propChange.firePropertyChange("initialPos", this.initialPos, initialPos);
        this.initialPos = initialPos;
    }

    public void setInitialSpeed(final SpeedSet initialSpeed) {
        dirty = true;
        propChange.firePropertyChange("initialSpeed", this.initialSpeed, initialSpeed);
        this.initialSpeed = initialSpeed;
    }

    public void setSlider(final Slider slider) {
        dirty = true;
        propChange.firePropertyChange("slider", this.slider, slider);
        this.slider = slider;
    }
}