Last active
August 18, 2023 07:29
-
-
Save dacr/0efc7b25c979d14111698e1708b57cf5 to your computer and use it in GitHub Desktop.
Drawing interactions / published by https://github.com/dacr/code-examples-manager #8d3870b9-83fc-49f7-b535-0364796259b8/87341813c93d056f5e4f206584cc7e85d0124736
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// summary : Drawing interactions | |
// keywords : scala, vector-graphics, doodle, examples | |
// publish : gist | |
// authors : Noel Welsh, David Crosson | |
// license : Apache NON-AI License Version 2.0 (https://raw.githubusercontent.com/non-ai-licenses/non-ai-licenses/main/NON-AI-APACHE2) | |
// id : 8d3870b9-83fc-49f7-b535-0364796259b8 | |
// created-on : 2019-08-12T16:28:07Z | |
// managed-by : https://github.com/dacr/code-examples-manager | |
// run-with : scala-cli $file | |
// --------------------- | |
//> using scala "3.3.0" | |
//> using dep "org.creativescala::doodle:0.20.0" | |
// --------------------- | |
//doodle.java2d.examples.Ripples.go | |
// https://github.com/creativescala/doodle/blob/master/java2d/src/main/scala/doodle/java2d/examples/Ripples.scala | |
// Copy paste of this example code with minor changes : | |
object Ripples { | |
import cats.effect.IO | |
import doodle.core.* | |
import doodle.syntax.* | |
import doodle.syntax.all.* | |
import doodle.interact.* | |
import doodle.interact.syntax.* | |
import doodle.interact.syntax.all.* | |
import doodle.java2d.* | |
import doodle.java2d.effect.* | |
import fs2.Stream | |
import cats.effect.unsafe.implicits.global | |
import scala.concurrent.duration.{FiniteDuration, MILLISECONDS} | |
import cats.instances.all._ | |
import cats.syntax.all._ | |
val frame = Frame( | |
size = Size.fixedSize(600, 600), | |
title = "Photos", | |
center = Center.atOrigin, | |
background = Some(Color.midnightBlue), | |
redraw = Redraw.clearToBackground | |
) | |
final case class Ripple(age: Int, x: Double, y: Double) { | |
val maxAge = 400 | |
def alive: Boolean = age <= maxAge | |
def older: Ripple = | |
this.copy(age = age + 1) | |
def picture = | |
circle(age.toDouble) | |
.strokeColor( | |
Color.hotpink.alpha(((maxAge - age) / (maxAge.toDouble)).normalized) | |
) | |
.at(x, y) | |
} | |
def ripples(canvas: Canvas): IO[Stream[IO, Picture[Unit]]] = { | |
import cats.effect.std.Queue | |
Queue | |
.bounded[IO, Option[Ripple]](5) | |
.flatMap { queue => | |
val redraw = canvas.redraw | |
.map(_ => none[Ripple]) | |
.evalMap(r => queue.offer(r)) | |
.compile | |
.drain | |
val mouseMove = canvas.mouseMove | |
.debounce( | |
FiniteDuration(100, MILLISECONDS) | |
) // Stop spamming with too many mouse events | |
.map(pt => Ripple(0, pt.x, pt.y).some) | |
.evalMap(r => queue.offer(r)) | |
.compile | |
.drain | |
val ripples: Stream[IO, Picture[Unit]] = Stream | |
.fromQueueUnterminated(queue) | |
.scan(List.empty[Ripple]) { (ripples, ripple) => | |
ripple match { | |
case Some(r) => r :: ripples | |
case None => ripples.filter(_.alive).map(_.older) | |
} | |
} | |
.map(ripples => ripples.map(_.picture).allOn) | |
(redraw.start >> mouseMove.start).as(ripples) | |
} | |
} | |
def go(): Unit = { | |
// frame.canvas.flatMap(canvas => ripples(canvas.mouseMove).map(_.animateToCanvas(canvas))).unsafeRunSync() | |
(for { | |
canvas <- frame.canvas() | |
frames <- ripples(canvas) | |
a <- frames.animateWithCanvasToIO(canvas) | |
} yield a).unsafeRunAsync(println _) | |
} | |
} | |
Ripples.go() | |
scala.io.StdIn.readLine("Enter to exit...") // required when run as a script |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment