<commit_before>/*
 * Aphelion
 * Copyright (c) 2013  <NAME>
 * 
 * This file is part of Aphelion
 * 
 * Aphelion is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 * 
 * Aphelion 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 Affero General Public License
 * along with Aphelion.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * In addition, the following supplemental terms apply, based on section 7 of
 * the GNU Affero General Public License (version 3):
 * a) Preservation of all legal notices and author attributions
 * b) Prohibition of misrepresentation of the origin of this material, and
 * modified versions are required to be marked in reasonable ways as
 * different from the original version (for example by appending a copyright notice).
 * 
 * Linking this library statically or dynamically with other modules is making a
 * combined work based on this library. Thus, the terms and conditions of the
 * GNU Affero General Public License cover the whole combination.
 * 
 * As a special exception, the copyright holders of this library give you 
 * permission to link this library with independent modules to produce an 
 * executable, regardless of the license terms of these independent modules,
 * and to copy and distribute the resulting executable under terms of your 
 * choice, provided that you also meet, for each linked independent module,
 * the terms and conditions of the license of that module. An independent
 * module is a module which is not derived from or based on this library.
 */

package aphelion.shared.physics.valueobjects;

import aphelion.shared.gameconfig.GCInteger;

/**
 * Used to provide dead reckon convergence / smoothing.
 * Note that the results of this object might not be deterministic!
 * Also, the output depends on the order that the input is given (this is how dead 
 * reckon convergence works).
 * It should not used be used for things that need strong consistency across clients,
 * or the results should be checked by later states for consistency, (eventual 
 * consistency).
 * 
 * @author Joris
 */
public class PhysicsPointHistorySmooth 
{
        private final PhysicsPointHistory positionHist;
        private final PhysicsPointHistory velocityHist;
        private final int HISTORY_LENGTH;
        
        /** The "baseline" is used so that multiple changes in the position do not cause multiple 
         * steps in the convergence
         */
        private final PhysicsPointHistory baseline;
        private final PhysicsPointHistory smooth;
        private SMOOTHING_ALGORITHM algorithm = SMOOTHING_ALGORITHM.NONE;
        private int lookAheadTicks = 20;
        private int stepRatio = 5000;
        private long smoothLimitDistanceSq = 10_000 * 10_000;
        
        public PhysicsPointHistorySmooth(long initial_tick, PhysicsPointHistory positionHist, PhysicsPointHistory velocityHist)
        {
                HISTORY_LENGTH = positionHist.HISTORY_LENGTH;
                assert HISTORY_LENGTH == velocityHist.HISTORY_LENGTH;
                
                // use the same index for ticks as positionHist for easy implementation
                
                this.positionHist = positionHist;
                this.velocityHist = velocityHist;
                positionHist.setListener(posListener);
                velocityHist.setListener(posListener);
                
                baseline = new PhysicsPointHistory(initial_tick, HISTORY_LENGTH);
                smooth = new PhysicsPointHistory(initial_tick, HISTORY_LENGTH);
        }

        public void setAlgorithm(SMOOTHING_ALGORITHM algorithm)
        {
                this.algorithm = algorithm;
        }

        public void setLookAheadTicks(int lookAheadTicks)
        {
                this.lookAheadTicks = lookAheadTicks;
        }
        
        /**
         * @param permille A ratio between 0 and 1048576
         */
        public void setStepRatio(int permille)
        {
                this.stepRatio = permille;
        }

        public void setSmoothLimitDistance(int smoothLimitDistance)
        {
                this.smoothLimitDistanceSq = (long) smoothLimitDistance * (long) smoothLimitDistance;
        }
        
        /** This method should be called before each iteration of the state.
         * It ensures that setting the actor position multiple times within the same state iteration,
         * has the exact same result as setting it only once.
         */
        public void updateBaseLine()
        {
                baseline.set(smooth);
        }
        
        public void getSmooth(PhysicsPoint ret, long tick)
        {
                smooth.get(ret, tick);
        }
        
        public int getX(long tick)
        {
                return smooth.getX(tick);
        }

        public int getY(long tick)
        {
                return smooth.getY(tick);
        }
        
        private void calculate(long tick)
        {
                PhysicsPoint base = new PhysicsPoint();
                PhysicsPoint desired = new PhysicsPoint();
                PhysicsPoint velocity = new PhysicsPoint();
                
                baseline.get(base, tick - 1);
                positionHist.get(desired, tick);
                velocityHist.get(velocity, tick);
                
                smooth.setHistory(tick, desired);
                
                if (base.set && base.distanceSquared(desired) <= smoothLimitDistanceSq)
                {
                        if (algorithm == SMOOTHING_ALGORITHM.NONE)
                        {
                        }
                        else if (algorithm == SMOOTHING_ALGORITHM.LINEAR && lookAheadTicks > 0)
                        {
                                PhysicsPoint smoothed = new PhysicsPoint();
                                smoothed.set(velocity);
                                smoothed.multiply(lookAheadTicks);
                                smoothed.add(desired);

                                smoothed.sub(base);
                                smoothed.applyRatio(stepRatio, GCInteger.RATIO_PRECISE);
                                smoothed.add(base);
                                
                                smooth.setHistory(tick, smoothed);
                        }
                }
        }

        private final PhysicsPointHistory.UpdateListener posListener = new PhysicsPointHistory.UpdateListener()
        {
                @Override
                public void updated(long tick, int index)
                {
                        calculate(tick);
                }
<commit_msg>slight improvement to convergence; Do not use smoothed position if the actual position is a smaller step!<commit_after>/*
 * Aphelion
 * Copyright (c) 2013  <NAME>
 * 
 * This file is part of Aphelion
 * 
 * Aphelion is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 * 
 * Aphelion 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 Affero General Public License
 * along with Aphelion.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * In addition, the following supplemental terms apply, based on section 7 of
 * the GNU Affero General Public License (version 3):
 * a) Preservation of all legal notices and author attributions
 * b) Prohibition of misrepresentation of the origin of this material, and
 * modified versions are required to be marked in reasonable ways as
 * different from the original version (for example by appending a copyright notice).
 * 
 * Linking this library statically or dynamically with other modules is making a
 * combined work based on this library. Thus, the terms and conditions of the
 * GNU Affero General Public License cover the whole combination.
 * 
 * As a special exception, the copyright holders of this library give you 
 * permission to link this library with independent modules to produce an 
 * executable, regardless of the license terms of these independent modules,
 * and to copy and distribute the resulting executable under terms of your 
 * choice, provided that you also meet, for each linked independent module,
 * the terms and conditions of the license of that module. An independent
 * module is a module which is not derived from or based on this library.
 */

package aphelion.shared.physics.valueobjects;

import aphelion.shared.gameconfig.GCInteger;

/**
 * Used to provide dead reckon convergence / smoothing.
 * Note that the results of this object might not be deterministic!
 * Also, the output depends on the order that the input is given (this is how dead 
 * reckon convergence works).
 * It should not used be used for things that need strong consistency across clients,
 * or the results should be checked by later states for consistency, (eventual 
 * consistency).
 * 
 * @author Joris
 */
public class PhysicsPointHistorySmooth 
{
        private final PhysicsPointHistory positionHist;
        private final PhysicsPointHistory velocityHist;
        private final int HISTORY_LENGTH;
        
        /** The "baseline" is used so that multiple changes in the position do not cause multiple 
         * steps in the convergence
         */
        private final PhysicsPointHistory baseline;
        private final PhysicsPointHistory smooth;
        private SMOOTHING_ALGORITHM algorithm = SMOOTHING_ALGORITHM.NONE;
        private int lookAheadTicks = 20;
        private int stepRatio = 5000;
        private long smoothLimitDistanceSq = 10_000 * 10_000;
        
        public PhysicsPointHistorySmooth(long initial_tick, PhysicsPointHistory positionHist, PhysicsPointHistory velocityHist)
        {
                HISTORY_LENGTH = positionHist.HISTORY_LENGTH;
                assert HISTORY_LENGTH == velocityHist.HISTORY_LENGTH;
                
                // use the same index for ticks as positionHist for easy implementation
                
                this.positionHist = positionHist;
                this.velocityHist = velocityHist;
                positionHist.setListener(posListener);
                velocityHist.setListener(posListener);
                
                baseline = new PhysicsPointHistory(initial_tick, HISTORY_LENGTH);
                smooth = new PhysicsPointHistory(initial_tick, HISTORY_LENGTH);
        }

        public void setAlgorithm(SMOOTHING_ALGORITHM algorithm)
        {
                this.algorithm = algorithm;
        }

        public void setLookAheadTicks(int lookAheadTicks)
        {
                this.lookAheadTicks = lookAheadTicks;
        }
        
        /**
         * @param permille A ratio between 0 and 1048576
         */
        public void setStepRatio(int permille)
        {
                this.stepRatio = permille;
        }

        public void setSmoothLimitDistance(int smoothLimitDistance)
        {
                this.smoothLimitDistanceSq = (long) smoothLimitDistance * (long) smoothLimitDistance;
        }
        
        /** This method should be called before each iteration of the state.
         * It ensures that setting the actor position multiple times within the same state iteration,
         * has the exact same result as setting it only once.
         */
        public void updateBaseLine()
        {
                baseline.set(smooth);
        }
        
        public void getSmooth(PhysicsPoint ret, long tick)
        {
                smooth.get(ret, tick);
        }
        
        public int getX(long tick)
        {
                return smooth.getX(tick);
        }

        public int getY(long tick)
        {
                return smooth.getY(tick);
        }
        
        private void calculate(long tick)
        {
                PhysicsPoint base = new PhysicsPoint();
                PhysicsPoint desired = new PhysicsPoint();
                PhysicsPoint velocity = new PhysicsPoint();
                
                baseline.get(base, tick - 1);
                positionHist.get(desired, tick);
                velocityHist.get(velocity, tick);
                
                smooth.setHistory(tick, desired);
                
                if (base.set && base.distanceSquared(desired) <= smoothLimitDistanceSq)
                {
                        if (algorithm == SMOOTHING_ALGORITHM.NONE)
                        {
                        }
                        else if (algorithm == SMOOTHING_ALGORITHM.LINEAR && lookAheadTicks > 0)
                        {
                                PhysicsPoint smoothed = new PhysicsPoint();
                                smoothed.set(velocity);
                                smoothed.multiply(lookAheadTicks);
                                smoothed.add(desired);

                                smoothed.sub(base);
                                smoothed.applyRatio(stepRatio, GCInteger.RATIO_PRECISE);
                                smoothed.add(base);
                                
                                if (base.distanceSquared(desired) <= base.distanceSquared(smoothed) )
                                {
                                        // do not bother with smoothed if the real position is closer!
                                        smoothed.set(desired);
                                }
                                
                                smooth.setHistory(tick, smoothed);
                        }
                }
        }

        private final PhysicsPointHistory.UpdateListener posListener = new PhysicsPointHistory.UpdateListener()
        {
                @Override
                public void updated(long tick, int index)
                {
                        calculate(tick);
                }

{'fraction_non_alphanumeric': 0.05501214013180714, 'fraction_numerical': 0.004578563995837669, 'mean_word_length': 1.8495750148250643, 'pattern_counts': {'":': 0, '<': 10, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6003368', 'n_tokens_mistral': 3542, 'n_tokens_neox': 3238, 'n_words': 1243}