package zenproject.meditation.android.sketch.painting.flowers.branch; import android.os.AsyncTask; import com.juankysoriano.rainbow.core.drawing.RainbowDrawer; import com.juankysoriano.rainbow.core.event.RainbowInputController; import com.juankysoriano.rainbow.utils.RainbowMath; import com.novoda.notils.exception.DeveloperError; import java.util.ArrayList; import java.util.List; import zenproject.meditation.android.ContextRetriever; import zenproject.meditation.android.R; import zenproject.meditation.android.preferences.BrushOptionsPreferences; import zenproject.meditation.android.preferences.FlowerOptionPreferences; import zenproject.meditation.android.sketch.actions.StepPerformer; import zenproject.meditation.android.sketch.painting.PaintStepSkipper; import zenproject.meditation.android.sketch.painting.flowers.Flower; import zenproject.meditation.android.sketch.painting.flowers.FlowerDrawer; import zenproject.meditation.android.sketch.painting.ink.BrushColor; import zenproject.meditation.android.sketch.painting.ink.InkDrop; import zenproject.meditation.android.ui.menu.dialogs.flower.FlowerSelectedListener; public class BranchPerformer implements StepPerformer, FlowerSelectedListener { private static final int ALPHA = 128; private static final int MAX_THRESHOLD = 100; private static final int BLOOM_THRESHOLD = 90; private static final float INK_VELOCITY_THRESHOLD = ContextRetriever.INSTANCE.getResources().getDimension(R.dimen.ink_velocity_threshold); private static final int BRANCH_THRESHOLD_FAST = 63; private static final int BRANCH_THRESHOLD_SLOW = 83; private static final int FLOWER_THRESHOLD = 70; private static final float LEAF_SIZE = ContextRetriever.INSTANCE.getResources().getDimension(R.dimen.branch_default_radius) * 2; private final RainbowDrawer rainbowDrawer; private final BranchesList branchesList; private final PaintStepSkipper paintStepSkipper; private final BrushOptionsPreferences brushOptionsPreferences; private final InkDrop inkDrop; private final RainbowInputController rainbowInputController; private FlowerDrawer flowerDrawer; private boolean enabled = true; private boolean initialised; protected BranchPerformer(InkDrop inkDrop, BranchesList branchesList, FlowerDrawer flowerDrawer, PaintStepSkipper paintStepSkipper, BrushOptionsPreferences brushOptionsPreferences, RainbowDrawer rainbowDrawer, RainbowInputController rainbowInputController) { this.inkDrop = inkDrop; this.branchesList = branchesList; this.flowerDrawer = flowerDrawer; this.rainbowDrawer = rainbowDrawer; this.paintStepSkipper = paintStepSkipper; this.brushOptionsPreferences = brushOptionsPreferences; this.rainbowInputController = rainbowInputController; } public static BranchPerformer newInstance(InkDrop inkDrop, RainbowDrawer rainbowDrawer, RainbowInputController rainbowInputController) { BrushOptionsPreferences brushOptionsPreferences = BrushOptionsPreferences.newInstance(); BranchesList branchesList = BranchesList.newInstance(); FlowerOptionPreferences flowerOptionPreferences = FlowerOptionPreferences.newInstance(); Flower flower = flowerOptionPreferences.getFlower(); FlowerDrawer flowerDrawer = FlowerDrawer.newInstance(flower, rainbowDrawer); return new BranchPerformer(inkDrop, branchesList, flowerDrawer, new PaintStepSkipper(), brushOptionsPreferences, 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(); flowerDrawer.init(); } } private void configureDrawer() { rainbowDrawer.noStroke(); rainbowDrawer.smooth(); } @Override public void doStep() { if (enabled) { if (!paintStepSkipper.hasToSkipStep() && hasFlower()) { paintAndUpdateBranches(); } attemptToBloomBranch(); paintStepSkipper.recordStep(); } } private boolean hasFlower() { return flowerDrawer != null; } private void paintAndUpdateBranches() { List<Branch> branchList = new ArrayList<>(branchesList.asList()); for (Branch branch : branchList) { paintAndUpdateBranch(branch); } } private void paintAndUpdateBranch(Branch branch) { if (branch.isDead()) { branchesList.prune(branch); bloomFlowerIfLuck(branch); } else { performBranchPainting(branch); branch.update(); bloomBranchIfLuck(branch); } } private void bloomFlowerIfLuck(Branch branch) { if (RainbowMath.random(MAX_THRESHOLD) > FLOWER_THRESHOLD) { paintFlowerFor(branch); } else { paintLeafFor(branch); } } private void performBranchPainting(Branch branch) { rainbowDrawer.stroke(brushOptionsPreferences.getBranchColor(), ALPHA); rainbowDrawer.line(branch.getX(), branch.getY(), branch.getOldX(), branch.getOldY()); } private void bloomBranchIfLuck(Branch branch) { if (RainbowMath.random(MAX_THRESHOLD) > BLOOM_THRESHOLD) { branchesList.bloomFrom(branch); } } private void paintFlowerFor(Branch branch) { flowerDrawer.paintFlowerFor(branch); } private void paintLeafFor(Branch branch) { rainbowDrawer.strokeWeight(LEAF_SIZE); rainbowDrawer.stroke(brushOptionsPreferences.getLeafColor(), ALPHA); rainbowDrawer.point(branch.getX(), branch.getY()); rainbowDrawer.strokeWeight(1); } private void attemptToBloomBranch() { float bloomBranchThreshold = rainbowInputController.getFingerVelocity() > INK_VELOCITY_THRESHOLD ? BRANCH_THRESHOLD_FAST : BRANCH_THRESHOLD_SLOW; if (!isErasing() && rainbowInputController.isScreenTouched() && RainbowMath.random(MAX_THRESHOLD) > bloomBranchThreshold) { createBranchAt(rainbowInputController.getSmoothX(), rainbowInputController.getSmoothY()); } } private void createBranchAt(float x, float y) { RainbowInputController.MovementDirection verticalMovement = rainbowInputController.getVerticalDirection(); RainbowInputController.MovementDirection horizontalMovement = rainbowInputController.getHorizontalDirection(); float radius = inkDrop.getRadius() / 4; float verticalOffset = verticalMovement == RainbowInputController.MovementDirection.DOWN ? radius : -radius; float horizontalOffset = horizontalMovement == RainbowInputController.MovementDirection.RIGHT ? radius : -radius; branchesList.bloomFrom(Branch.createAt(x + horizontalOffset, y + verticalOffset)); } @Override public void enable() { enabled = true; } @Override public void disable() { enabled = false; } @Override public void reset() { branchesList.clear(); } private boolean isErasing() { return BrushColor.ERASE == brushOptionsPreferences.getBrushColor(); } @Override public void onFlowerSelected(final Flower flower) { new AsyncTask<Flower, Void, Void>() { @Override protected Void doInBackground(Flower... flowers) { flowerDrawer = FlowerDrawer.newInstance(flowers[0], rainbowDrawer); flowerDrawer.init(); return null; } }.execute(flower); } }