package zenproject.meditation.android.sketch.painting.ink; import com.juankysoriano.rainbow.core.drawing.RainbowDrawer; import com.juankysoriano.rainbow.core.event.RainbowInputController; import com.juankysoriano.rainbow.core.graphics.RainbowGraphics; import com.juankysoriano.rainbow.core.graphics.RainbowImage; import com.juankysoriano.rainbow.utils.RainbowMath; import com.novoda.notils.exception.DeveloperError; import zenproject.meditation.android.R; import zenproject.meditation.android.sketch.actions.StepPerformer; public class InkPerformer implements StepPerformer, RainbowImage.LoadPictureListener { private static final RainbowImage NO_IMAGE = null; private static final int MAX_THRESHOLD = 100; private static final int INK_ISSUE_THRESHOLD = 98; private static final float INK_DROP_IMAGE_SCALE = 0.5f; private static final int OPAQUE = 255; private final RainbowDrawer rainbowDrawer; private final RainbowInputController rainbowInputController; private final InkDrop inkDrop; private RainbowImage image; private boolean enabled = true; private boolean initialised; protected InkPerformer(InkDrop inkDrop, RainbowDrawer rainbowDrawer, RainbowInputController rainbowInputController) { this.inkDrop = inkDrop; this.rainbowDrawer = rainbowDrawer; this.rainbowInputController = rainbowInputController; } public static InkPerformer newInstance(InkDrop inkDrop, RainbowDrawer rainbowDrawer, RainbowInputController rainbowInputController) { return new InkPerformer(inkDrop, rainbowDrawer, rainbowInputController); } @Override public void init() { synchronized (this) { if (initialised) { throw new DeveloperError("You don't really want init this if it was already initialised"); } initialised = true; configureDrawer(); } } private void configureDrawer() { rainbowDrawer.noStroke(); rainbowDrawer.smooth(); rainbowDrawer.loadImage(R.drawable.brush_ink, RainbowImage.LOAD_ORIGINAL_SIZE, this); } @Override public void onLoadSucceed(RainbowImage imageLoaded) { image = imageLoaded; } @Override public void onLoadFail() { image = NO_IMAGE; } @Override public void doStep() { if (enabled) { moveAndPaintInkDrop(); } } private void moveAndPaintInkDrop() { drawInk(rainbowInputController.getPreviousSmoothX(), rainbowInputController.getPreviousSmoothY(), rainbowInputController.getSmoothX(), rainbowInputController.getSmoothY()); inkDrop.updateInkRadius(); } private void drawInk(float px, float py, float x, float y) { paintDropWithoutImage(px, py, x, y); if (hasToPaintDropImage()) { paintDropWithImage(x, y); } } private void paintDropWithoutImage(float px, float py, float x, float y) { rainbowDrawer.stroke(inkDrop.getBrushColor().toAndroidColor(), OPAQUE); rainbowDrawer.strokeWeight(isErasing() ? inkDrop.getMaxRadius() * INK_DROP_IMAGE_SCALE : inkDrop.getRadius() * INK_DROP_IMAGE_SCALE); rainbowDrawer.line(px, py, x, y); } private boolean hasToPaintDropImage() { return !isErasing() && RainbowMath.random(MAX_THRESHOLD) > INK_ISSUE_THRESHOLD && hasImage(); } private void paintDropWithImage(float x, float y) { rainbowDrawer.tint(inkDrop.getBrushColor().toAndroidColor()); rainbowDrawer.imageMode(RainbowGraphics.CENTER); rainbowDrawer.pushMatrix(); rainbowDrawer.translate(x, y); rainbowDrawer.rotate(RainbowMath.random(RainbowMath.TWO_PI)); rainbowDrawer.image(image, 0, 0, inkDrop.getRadius(), inkDrop.getRadius()); rainbowDrawer.popMatrix(); } private boolean isErasing() { return BrushColor.ERASE == inkDrop.getBrushColor(); } // TODO revisit this once RainbowImage has support for EmptyRainbowImage private boolean hasImage() { return image != NO_IMAGE; } @Override public void reset() { inkDrop.resetRadius(); } @Override public void enable() { enabled = true; } @Override public void disable() { enabled = false; } }