All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.pepsoft.worldpainter.brushes.SymmetricBrush Maven / Gradle / Ivy

There is a newer version: 2.23.2
Show newest version
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.pepsoft.worldpainter.brushes;

import org.pepsoft.util.ObjectUtils;

/**
 * An object which calculates brush strengths for simple mathematical brushes,
 * which must be symmetric in both the x and y axes (so that only one quadrant
 * of the brush needs to be calculated).
 *
 * @author pepijn
 */
public abstract class SymmetricBrush extends AbstractBrush {
    public SymmetricBrush(String name, BrushShape brushShape, boolean rotationallySymmetric) {
        super(name);
        this.brushShape = brushShape;
        this.rotationallySymmetric = rotationallySymmetric;
        cacheStrengths();
    }

    @Override
    public BrushShape getBrushShape() {
        return brushShape;
    }

    @Override
    public int getRadius() {
        return radius;
    }

    @Override
    public void setRadius(int radius) {
        if (radius != this.radius) {
            this.radius = radius;
            cacheStrengths();
        }
    }

    @Override
    public float getLevel() {
        return level;
    }

    @Override
    public void setLevel(float level) {
        if (level != this.level) {
            this.level = level;
            cacheStrengths();
        }
    }

    public final boolean isRotationallySymmetric() {
        return rotationallySymmetric;
    }

    @Override
    public final float getStrength(int dx, int dy) {
        return strengthCache[Math.abs(dx)][Math.abs(dy)];
    }
    
    @Override
    public final float getFullStrength(int dx, int dy) {
        return fullStrengthCache[Math.abs(dx)][Math.abs(dy)];
    }
    
    @Override
    public String toString() {
        return getName() + " (radius=" + radius + ", brushShape=" + brushShape + ", level=" + level + ')';
    }

    @Override
    public SymmetricBrush clone() {
        SymmetricBrush clone = (SymmetricBrush) super.clone();
        if (strengthCache != null) {
            clone.strengthCache = ObjectUtils.clone(strengthCache);
        }
        if (fullStrengthCache != null) {
            if (fullStrengthCache == strengthCache) {
                clone.fullStrengthCache = clone.strengthCache;
            } else {
                clone.fullStrengthCache = ObjectUtils.clone(fullStrengthCache);
            }
        }
        return clone;
    }
    
    protected abstract float calcStrength(int dx, int dy);
    
    private void cacheStrengths() {
        if ((radius != cachedRadius) || (level != cachedLevel)) {
            // No need to allocate a new array if there is one already and the
            // data will fit in it:
            if ((strengthCache == null) || (strengthCache.length < (radius + 1))) {
                strengthCache = new float[radius + 1][radius + 1];
            }
            // Only update the full strength cache if necessary
            if (radius != cachedRadius) {
                // No need to allocate a new array if there is one already and the
                // data will fit in it:
                if ((fullStrengthCache == null) || (fullStrengthCache.length < (radius + 1))) {
                    fullStrengthCache = new float[radius + 1][radius + 1];
                }
                for (int dx = 0; dx <= radius; dx++) {
                    for (int dy = 0; dy <= radius; dy++) {
                        float strength = calcStrength(dx, dy);
                        strengthCache[dx][dy] = strength * level;
                        fullStrengthCache[dx][dy] = strength;
                    }
                }
            } else {
                for (int dx = 0; dx <= radius; dx++) {
                    for (int dy = 0; dy <= radius; dy++) {
                        strengthCache[dx][dy] = calcStrength(dx, dy) * level;
                    }
                }
            }
            cachedRadius = radius;
            cachedLevel = level;
        }
    }

    protected final boolean rotationallySymmetric;
    protected final BrushShape brushShape;

    private int radius, cachedRadius = -1;
    private float[][] strengthCache, fullStrengthCache;
    private float level = 1.0f, cachedLevel = -1.0f;

    public static final SymmetricBrush CONSTANT_CIRCLE = new RadialBrush("Constant Circle", BrushShape.CIRCLE, true) {
        @Override
        protected float calcStrength(float dr) {
            return 1.0f;
        }
    };

    public static final SymmetricBrush CONSTANT_SQUARE = new RadialBrush("Constant Square", BrushShape.SQUARE, false) {
        @Override
        protected float calcStrength(float dr) {
            return 1.0f;
        }
    };
    
    public static final SymmetricBrush LINEAR_CIRCLE = new RadialBrush("Linear Circle", BrushShape.CIRCLE, true) {
        @Override
        protected float calcStrength(float dr) {
            return 1.0f - dr;
        }
    };

    public static final SymmetricBrush LINEAR_SQUARE = new RadialBrush("Linear Square", BrushShape.SQUARE, false) {
        @Override
        protected float calcStrength(float dr) {
            return 1.0f - dr;
        }
    };

    public static final SymmetricBrush COSINE_CIRCLE = new RadialBrush("Sine Circle", BrushShape.CIRCLE, true) {
        @Override
        protected float calcStrength(float dr) {
            return (float) Math.cos(dr * Math.PI) / 2 + 0.5f;
        }
    };
    
    public static final SymmetricBrush COSINE_SQUARE = new RadialBrush("Sine Square", BrushShape.SQUARE, false) {
        @Override
        protected float calcStrength(float dr) {
            return (float) Math.cos(dr * Math.PI) / 2 + 0.5f;
        }
    };

    public static final SymmetricBrush PLATEAU_CIRCLE = new RadialBrush("Plateau Circle", BrushShape.CIRCLE, true) {
        @Override
        protected float calcStrength(float dr) {
            if (dr <= 0.5f) {
                return 1.0f;
            } else {
                return (float) Math.cos((dr - 0.5f) * TWO_PI) / 2 + 0.5f;
            }
        }
    };
    
    public static final SymmetricBrush PLATEAU_SQUARE = new RadialBrush("Plateau Square", BrushShape.SQUARE, false) {
        @Override
        protected float calcStrength(float dr) {
            if (dr <= 0.5f) {
                return 1.0f;
            } else {
                return (float) Math.cos((dr - 0.5f) * TWO_PI) / 2 + 0.5f;
            }
        }
    };

    public static final SymmetricBrush SPIKE_CIRCLE = new RadialBrush("Spike Circle", BrushShape.CIRCLE, true) {
        @Override
        protected float calcStrength(float dr) {
            return (1.0f - dr) * (1.0f - dr);
        }
    };

    public static final SymmetricBrush SPIKE_SQUARE = new RadialBrush("Spike Square", BrushShape.SQUARE, false) {
        @Override
        protected float calcStrength(float dr) {
            return (1.0f - dr) * (1.0f - dr);
        }
    };

    public static final SymmetricBrush DOME_CIRCLE = new RadialBrush("Dome Circle", BrushShape.CIRCLE, true) {
        @Override
        protected float calcStrength(float dr) {
            return (float) Math.sqrt(1 - dr * dr);
        }
    };

    public static final SymmetricBrush DOME_SQUARE = new RadialBrush("Dome Square", BrushShape.SQUARE, false) {
        @Override
        protected float calcStrength(float dr) {
            return (float) Math.sqrt(1 - dr * dr);
        }
    };
    
    private static final float TWO_PI = (float) (Math.PI * 2);
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy