KoTrendline.java Source code

Java tutorial

Introduction

Here is the source code for KoTrendline.java

Source

/*
 * Copyright Jordan Schalm 2015
 * This program is distributed under the terms of the GNU General Public License.
 * 
 * This file is part of Ko.
 *
 * Ko 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 3 of the License, or
 * (at your option) any later version.
 *
 * Ko 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 Ko.  If not, see <http://www.gnu.org/licenses/>.
 */

import java.util.Set;
import java.util.TreeSet;

import org.opencv.core.Point;

/**
 * @author Jordan Schalm
 * 
 * KoTrendline represents a trendline in the set of detected stones along
 * the x or y axis. 
 */
public class KoTrendline implements Comparable<KoTrendline> {
    public static final int X_ORIENTATION = 1;
    public static final int Y_ORIENTATION = 0;

    private Set<KoCircle> constituents;
    private int orientation;

    /**
     * Creates a new trendline with the given orientation and the given set of constituent stones.
     * @param constituents
     *             a set of KoCircle objects that are members of this trendline.
     * @param orientation
     *             orientation of the new trendline, must be either X_ORIENTATION (1) or Y_ORIENTATION (0)
     */
    public KoTrendline(Set<KoCircle> constituents, int orientation) {
        if (orientation != X_ORIENTATION && orientation != Y_ORIENTATION) {
            System.out.println(
                    "Illegal value for orientation in Trendline initializer. Must be X_ORIENTATION (1) or Y_ORIENTATION (0).");
            throw new IllegalArgumentException();
        }
        this.orientation = orientation;
        this.constituents = constituents; // TODO: consider creating a copy here to prevent rep exposure
    }

    /**
     * Creates a new, empty trendline with the given orientation.
     * @param orientation
     *             orientation of the new trendline, must be either X_ORIENTATION (1) or Y_ORIENTATION (0)
     */
    public KoTrendline(int orientation) {
        if (orientation != X_ORIENTATION && orientation != Y_ORIENTATION) {
            System.out.println("Illegal value for direction in Trendline initializer. Must be 'x' or 'y'.");
            throw new IllegalArgumentException();
        }
        this.orientation = orientation;
        this.constituents = new TreeSet<KoCircle>();
    }

    /**
     * Returns this stone's orientation.
     * @return either 
     */
    public int getOrientation() {
        return this.orientation;
    }

    /**
     * Adds a stone to this trendline, if it doesn't already exist.
     * @param stone
     *          The stone to add.
     * @return
     *          true if the stone was successfully added (ie. was not already
     *          in the trendline) and false otherwise.
     */
    public boolean addStone(KoCircle stone) {
        if (constituents.add(stone)) {
            return true;
        }
        return false;
    }

    /**
     * Removes a stone from this trendline, if it exists.
     * @param stone
     *          The stone to remove.
     * @return
     *          true if the stone was successfully removed (ie. initially existed
     *          in the trendline) false otherwise.
     */
    public boolean removeStone(KoCircle stone) {
        if (constituents.remove(stone)) {
            return true;
        }
        return false;
    }

    /**
     * @return the set of all DetectedCircle objects contained in this trendline
     */
    public Set<KoCircle> getConstituents() {
        return new TreeSet<KoCircle>(this.constituents);
    }

    /**
     * @return the average x (if orientation == Y) or y (if orientation == X)
     *          coordinate of all stones in this trendline.
     */
    public double getOrigin() {
        double sum = 0;
        for (KoCircle current : this.constituents) {
            if (orientation == X_ORIENTATION)
                sum += current.getImgY();
            else
                sum += current.getImgX();
        }
        return sum / this.constituents.size();
    }

    /**
     * @return a Point containing the coordinates of the bottom-most (or leftmost)
     *          stone in this trendline.
     */
    public Point getBottomPoint() {
        double origin = getOrigin();
        double max = 0;
        for (KoCircle current : this.constituents) {
            if (orientation == X_ORIENTATION) {
                if (current.getImgX() > max) {
                    max = current.getImgX();
                }
            }
            if (orientation == Y_ORIENTATION) {
                if (current.getImgY() > max) {
                    max = current.getImgY();
                }
            }
        }
        if (orientation == X_ORIENTATION) {
            return new Point(max, origin);
        } else {
            return new Point(origin, max);
        }
    }

    /**
     * @return a Point containing the coordinates of the topmost (or rightmost)
     *          stone in this trendline.
     */
    public Point getTopPoint() {
        double origin = getOrigin();
        double min = Double.MAX_VALUE;
        for (KoCircle current : this.constituents) {
            if (orientation == X_ORIENTATION) {
                if (current.getImgX() < min) {
                    min = current.getImgX();
                }
            }
            if (orientation == Y_ORIENTATION) {
                if (current.getImgY() < min) {
                    min = current.getImgY();
                }
            }
        }
        if (orientation == X_ORIENTATION) {
            return new Point(min, origin);
        } else {
            return new Point(origin, min);
        }
    }

    /**
     * @return the slope of this trendline using least squares method
     */
    public double calculateSlope() {
        double xSum = 0, ySum = 0;
        for (KoCircle current : this.constituents) {
            xSum += current.getImgX();
            ySum += current.getImgY();
        }
        double xBar = xSum / this.constituents.size();
        double yBar = ySum / this.constituents.size();
        double numerator = 0, denominator = 0;
        for (KoCircle current : this.constituents) {
            numerator += (current.getImgX() - xBar) * (current.getImgY() - yBar);
            denominator += (current.getImgX() - xBar) * (current.getImgX() - xBar);
        }
        return numerator / denominator;
    }

    @Override
    public int compareTo(KoTrendline other) {
        if (other.getOrientation() != this.orientation) {
            throw new IllegalArgumentException(); // TODO BAD!
        } else {
            return (int) (this.getOrigin() - other.getOrigin());
        }
    }
}