package com.jotuntech.sketcher.client; import java.util.ArrayList; import java.util.List; import com.jotuntech.sketcher.common.Input; /** * Static singleton for input interpolation. * * @author Thor Harald Johansen */ public class Smoother { /* Smoothing threshold */ private static float threshold = 4f; private static float smoothness = 1.5f; private static boolean enabled = true; Input[] cursor = new Input[] {new Input(), new Input(), new Input()}; /** * Sets all points to given input * @param d input to add. */ public void set(Input d) { cursor[0] = d; cursor[1] = cursor[0]; cursor[2] = cursor[1]; } public void setPressure(int pressure) { //cursor[0].pressure = pressure; //cursor[1].pressure = pressure; //cursor[2].pressure = pressure; } /** * Adds a new input for smoothing. * * @param d input to add. */ public void add(Input d) { float dx = d.x - cursor[2].x; float dy = d.y - cursor[2].y; float dd = d.pressure - cursor[2].pressure; float h = (float)Math.hypot(dx, dy); float d2 = h - smoothness; if(d2 > 0) { float sx = dx * d2 / h; float sy = dy * d2 / h; float sd = dd * d2 / h; cursor[0] = cursor[1]; cursor[1] = cursor[2]; cursor[2] = new Input(cursor[2].x + sx, cursor[2].y + sy, (int)(cursor[2].pressure + sd)); } } /** * Return a list of smoothed inputs. * * @return List of smoothed inputs. */ public List get() { /* Create list for inputs. */ List inputs = new ArrayList(); if(enabled) { /* Segment cursor. */ segment(inputs, cursor[0], cursor[1], cursor[2]); } else { inputs.add(cursor[2]); } return inputs; } public Input getIndex(int index) { return cursor[index]; } // uses a, b and c to interpolate inputs between b and c, store in inputs. void segment(List inputs, Input a, Input b, Input c) { // difference AB & BC Input dab = a.difference(b); Input dbc = b.difference(c); // magnitude AB & BC float mab = (float)Math.hypot(dab.x, dab.y); float mbc = (float)Math.hypot(dbc.x, dbc.y); // orientation AB & BC float oab = (float)Math.acos(dab.x / mab) * (dab.y < 0 ? -1 : 1); float obc = (float)Math.acos(dbc.x / mbc) * (dbc.y < 0 ? -1 : 1); // normalize if(obc - oab > Math.PI || obc - oab < -Math.PI) { obc -= Math.PI * 2; } // angle ABC float abc = Math.abs(obc - oab); // threshold of operation if(mbc * abc > threshold && abc < Math.PI / 2) { // three-quarter of angle ABC float angle = (oab + (obc * 3)) / 4; // half of magnitude BC Input d = new Input(b.x + ((float)Math.cos(angle) * mbc / 2), b.y + ((float)Math.sin(angle) * mbc / 2), (b.pressure + c.pressure) / 2); segment(inputs, a, b, d); inputs.add(d); segment(inputs, b, d, c); } else { inputs.add(c); } } public static void setThreshold(float threshold) { Smoother.threshold = threshold; } public static float getThreshold() { return threshold; } public static void setSmoothness(float smoothness) { Smoother.smoothness = smoothness; } public static float getSmoothness() { return smoothness; } public static void setEnabled(boolean enabled) { Smoother.enabled = enabled; } public static boolean isEnabled() { return enabled; } }