Package net.algart.math.patterns

Source Code of net.algart.math.patterns.MinkowskiSum

/*
* The MIT License (MIT)
*
* Copyright (c) 2007-2014 Daniel Alievsky, AlgART Laboratory (http://algart.net)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/

package net.algart.math.patterns;

import net.algart.math.Point;
import net.algart.math.Range;
import net.algart.math.RectangularArea;

import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.util.*;

final strictfp class MinkowskiSum extends AbstractPattern implements Pattern {
    private final Pattern[] summands;
    private final List<Pattern> optimizedSummands;
    private final Pattern[] projections;
    private volatile Reference<Set<Point>> points = null;

    MinkowskiSum(Pattern[] patterns) {
        this(patterns, null);
    }

    private MinkowskiSum(Pattern[] patterns, List<Pattern> optimizedSummands) {
        super(getDimCountAndCheck(patterns));
        List<Pattern> allSummands = new ArrayList<Pattern>();
        for (Pattern ptn : patterns) {
            if (ptn instanceof MinkowskiSum) {
                allSummands.addAll(Arrays.asList(((MinkowskiSum) ptn).summands));
            } else {
                allSummands.add(ptn);
            }
            // An alternate idea could be using ptn.minkowskiDecomposition always.
            // But it requires to choose minimalPointCount, which is unknown here:
            // for example, maybe we do not want to decompose little rectangular patterns.
        }
        double[] minCoord = new double[dimCount]; // zero-filled (for further summing)
        double[] maxCoord = new double[dimCount]; // zero-filled (for further summing)
        for (Pattern ptn : allSummands) {
            for (int k = 0; k < dimCount; k++) {
                Range range = ptn.coordRange(k);
                minCoord[k] += range.min();
                maxCoord[k] += range.max();
            }
        }
        for (int k = 0; k < dimCount; k++) {
            this.coordRanges[k] = Range.valueOf(minCoord[k], maxCoord[k]);
            checkCoordRange(this.coordRanges[k]);
        }
        this.summands = allSummands.toArray(new Pattern[allSummands.size()]);
        if (optimizedSummands == null) {
            this.optimizedSummands = optimizeMinkowskiSum(allSummands);
        } else {
            this.optimizedSummands = optimizedSummands;
        }
        this.projections = new Pattern[dimCount]; //null-filled
    }

    @Override
    public long pointCount() {
        return points().size();
    }

//    @Override
//    public boolean contains(IPoint point) {
//        return points().contains(point.toPoint());
//    }

    @Override
    public Set<Point> points() {
        Set<Point> resultPoints = points == null ? null : points.get();
        if (resultPoints == null) {
            Pattern ptn = new SimplePattern(optimizedSummands.get(0).points());
            // this actualization is necessary for a case when minkowskiAdd method just returns MinkowskiSum instance
            for (int k = 1, n = optimizedSummands.size(); k < n; k++) {
                ptn = ptn.minkowskiAdd(optimizedSummands.get(k));
            }
            resultPoints = ptn.points(); // immutable set
            points = new SoftReference<Set<Point>>(resultPoints);
        }
        return resultPoints;
    }

    @Override
    public Range coordRange(int coordIndex) {
        return coordRanges[coordIndex];
    }

    @Override
    public RectangularArea coordArea() {
        return RectangularArea.valueOf(coordRanges);
    }

    @Override
    public boolean isSurelySinglePoint() {
        for (Pattern p : optimizedSummands) {
            if (!p.isSurelySinglePoint()) {
                return false;
            }
        }
        return true;
    }

    @Override
    public boolean isSurelyInteger() {
        if (surelyInteger == null) {
            boolean allInteger = true;
            for (Pattern p : optimizedSummands) {
                if (!p.isSurelyInteger()) {
                    allInteger = false;
                    break;
                }
            }
            surelyInteger = allInteger;
        }
        return surelyInteger;
    }

    @Override
    public Pattern projectionAlongAxis(int coordIndex) {
        checkCoordIndex(coordIndex);
        assert dimCount > 0;
        if (dimCount == 1)
            throw new IllegalStateException("Cannot perform projection for 1-dimensional pattern");
        synchronized (projections) {
            if (projections[coordIndex] == null) {
                Pattern[] newSummands = new Pattern[summands.length];
                for (int k = 0; k < newSummands.length; k++) {
                    newSummands[k] = summands[k].projectionAlongAxis(coordIndex);
                }
                projections[coordIndex] = new MinkowskiSum(newSummands);
            }
            return projections[coordIndex];
        }
    }

    // We could also optimize minBound()/maxBound() here

    @Override
    public Pattern shift(Point shift) {
        if (shift.coordCount() != dimCount)
            throw new IllegalArgumentException("The number of shift coordinates " + shift.coordCount()
                + " is not equal to the number of pattern coordinates " + dimCount);
        if (shift.isOrigin()) {
            return this;
        }
        Pattern[] newSummands = summands.clone();
        newSummands[0] = newSummands[0].shift(shift);
        List<Pattern> newOptimizedSummands = new ArrayList<Pattern>(optimizedSummands);
        newOptimizedSummands.set(0, newOptimizedSummands.get(0).shift(shift));
        return new MinkowskiSum(newSummands, newOptimizedSummands);
    }

    @Override
    public Pattern scale(double... multipliers) {
        if (multipliers == null)
            throw new NullPointerException("Null multipliers argument");
        if (multipliers.length != dimCount)
            throw new IllegalArgumentException("Illegal number of multipliers: "
                + multipliers.length + " instead of " + dimCount);
        Pattern[] newSummands = new Pattern[summands.length];
        for (int k = 0; k < newSummands.length; k++) {
            newSummands[k] = summands[k].scale(multipliers);
        }
        return new MinkowskiSum(newSummands);
    }

    @Override
    public Pattern minkowskiAdd(Pattern added) {
        if (added == null)
            throw new NullPointerException("Null added argument");
        Pattern[] newSummands = new Pattern[summands.length + 1];
        System.arraycopy(summands, 0, newSummands, 0, summands.length);
        newSummands[summands.length] = added;
        return new MinkowskiSum(newSummands);
    }

    @Override
    public List<Pattern> minkowskiDecomposition(int minimalPointCount) {
        ArrayList<Pattern> result = new ArrayList<Pattern>();
        for (Pattern summand : optimizedSummands) {
            result.addAll(summand.minkowskiDecomposition(minimalPointCount));
        }
        int numberOfPatternsWithAtLeast2Points = 0;
        for (Pattern ptn : result) {
            long pointCount = ptn.pointCount();
            if (pointCount >= 3) {
                return Collections.unmodifiableList(result);
            }
            if (pointCount >= 2) {
                numberOfPatternsWithAtLeast2Points++;
            }
        }
        if (numberOfPatternsWithAtLeast2Points <= 1) {
            return Collections.<Pattern>singletonList(this);
        }
        return Collections.unmodifiableList(result);
    }

    @Override
    public String toString() {
        int n = optimizedSummands.size();
        StringBuilder sb = new StringBuilder(dimCount + "D Minkowski sum of " + n + " patterns: ");
        for (int k = 0; k < n; k++) {
            if (k > 0) {
                sb.append(" (+) ");
            }
            sb.append(optimizedSummands.get(k));
        }
        return sb.toString();
    }

    @Override
    public int hashCode() {
        return Arrays.hashCode(summands) ^ getClass().getName().hashCode();
    }

    @Override
    public boolean equals(Object obj) {
        return obj instanceof MinkowskiSum && (obj == this || Arrays.equals(summands, ((MinkowskiSum) obj).summands));
    }

    static int getDimCountAndCheck(Pattern[] patterns) {
        if (patterns == null)
            throw new NullPointerException("Null patterns argument");
        if (patterns.length == 0)
            throw new IllegalArgumentException("Empty patterns array");
        if (patterns[0] == null)
            throw new NullPointerException("Null pattern is the array");
        int result = patterns[0].dimCount();
        for (int k = 1; k < patterns.length; k++) {
            if (patterns[k] == null)
                throw new NullPointerException("Null pattern #" + k + " is the array");
            if (patterns[k].dimCount() != result)
                throw new IllegalArgumentException("Patterns dimensions mismatch: the first pattern has "
                    + result + " dimensions, but pattern #" + k + " has " + patterns[k].dimCount());
        }
        return result;
    }

    private static List<Pattern> optimizeMinkowskiSum(List<Pattern> patterns) {
        Map<Pattern, Integer> numbersOfEquals = new HashMap<Pattern, Integer>();
        Map<Point, UniformGridPattern> rectangularSummands = new HashMap<Point, UniformGridPattern>();
        Pattern onePointSummand = null;
        for (Pattern pattern : patterns) {
            if (pattern instanceof OnePointPattern) {
                onePointSummand = onePointSummand == null ? pattern : onePointSummand.minkowskiAdd(pattern);
                if (!(onePointSummand instanceof OnePointPattern))
                    throw new AssertionError("Invalid OnePointPattern.minkowskiAdd implementation");
            } else if (pattern instanceof UniformGridPattern
                && ((UniformGridPattern) pattern).isActuallyRectangular())
            {
                UniformGridPattern ugPattern = (UniformGridPattern) pattern;
                ugPattern = new BasicRectangularPattern(ugPattern.originOfGrid(), ugPattern.stepsOfGrid(),
                    ugPattern.gridIndexArea().ranges());
                Point steps = Point.valueOf(ugPattern.stepsOfGrid());
                UniformGridPattern previousSummand = rectangularSummands.get(steps);
                pattern = previousSummand == null ? ugPattern : previousSummand.minkowskiAdd(ugPattern);
                if (!(pattern instanceof BasicRectangularPattern))
                    throw new AssertionError("Invalid RectangularUniformGridPattern.minkowskiAdd implementation");
                rectangularSummands.put(steps, (BasicRectangularPattern) pattern);
            } else {
                Integer previousNumber = numbersOfEquals.get(pattern);
                if (previousNumber == null) {
                    numbersOfEquals.put(pattern, 1);
                } else {
                    numbersOfEquals.put(pattern, previousNumber + 1);
                }
            }
        }
        List<Map.Entry<Pattern, Integer>> multiPatterns =
            new ArrayList<Map.Entry<Pattern, Integer>>(numbersOfEquals.entrySet());
        // Sorting by decreasing number of points
        Collections.sort(multiPatterns, new Comparator<Map.Entry<Pattern, Integer>>() {
            public int compare(Map.Entry<Pattern, Integer> o1, Map.Entry<Pattern, Integer> o2) {
                long pointCount1 = o1.getKey().pointCount();
                long pointCount2 = o2.getKey().pointCount();
                return pointCount1 < pointCount2 ? 1 : pointCount1 > pointCount2 ? -1 : 0;
            }
        });
        List<Pattern> result = new ArrayList<Pattern>();
        result.addAll(rectangularSummands.values());
        if (onePointSummand != null) {
            if (result.isEmpty()) {
                result.add(onePointSummand);
            } else {
                result.set(0, onePointSummand.minkowskiAdd(result.get(0)));
            }
        }
        Pattern last = null;
        for (Map.Entry<Pattern, Integer> multiPattern : multiPatterns) {
            Pattern p = multiPattern.getKey();
            int n = multiPattern.getValue();
            Pattern c = null;
            if (n > 1 || last != null) {
                c = p.carcass();
            }
            if (last != null && last.minkowskiAdd(p).equals(last.minkowskiAdd(c))) {
                // probably true, because patterns were sorted
                result.add(c);
            } else {
                result.add(p);
            }
            n--; // 1 pattern used (included into result);
            if (n > 0) {
                int maxMultiplier = p.maxCarcassMultiplier();
                for (int m = 1; ; ) {
                    if (m > n) {
                        m = n;
                    }
                    assert c != null; // because n > 0 after n--
                    result.add(c.multiply(m));
                    n -= m; // m patterns used now
                    if (n == 0) {
                        break;
                    }
                    assert n >= 0 : "Counter overflow while optimizing Minkowski sum";
                    if (2 * m >= 0 && // no overflow yet
                        2 * m <= maxMultiplier)
                    {
                        m *= 2;
                    }
                }
            }
            last = p;
        }
        return result;
    }
}
TOP

Related Classes of net.algart.math.patterns.MinkowskiSum

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.