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

doodle.image.examples.BrownianMotion.scala Maven / Gradle / Ivy

There is a newer version: 0.27.0
Show newest version
/*
 * Copyright 2015 Creative Scala
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package doodle
package image
package examples

import cats.instances.all.*
import cats.syntax.all.*
import doodle.core.*
import doodle.image.Image.*
import doodle.image.syntax.core.*
import doodle.random.*

object BrownianMotion {
  def brownianMotion(start: Point, drift: Vec): Random[Point] =
    jitter(start) map { pt =>
      pt + drift
    }

  def jitter(point: Point): Random[Point] = {
    val noise = Random.normal(0, 5.0)

    (noise, noise) mapN { (dx, dy) =>
      Point.cartesian(point.x + dx, point.y + dy)
    }
  }

  val start = Point.zero
  val drift = Vec(3, 0)

  val smoke: Random[Image] = {
    val alpha = Random.normal(0.5, 0.1)
    val hue = Random.double.map(h => (h * 0.1 + 0.7).turns)
    val saturation = Random.double.map(s => (s * 0.8))
    val lightness = Random.normal(0.4, 0.1)
    val color =
      (hue, saturation, lightness, alpha) mapN { (h, s, l, a) =>
        Color.hsla(h, s, l, a)
      }
    val points = Random.int(3, 7)
    val radius = Random.normal(2, 1)
    val rotation = Random.double.map { r =>
      r.turns
    }
    val shape = (points, radius, rotation).mapN { (pts, r, rot) =>
      star(pts, r, r * 0.5).rotate(rot)
    }

    (shape, color).mapN { (shape, stroke) =>
      shape.strokeColor(stroke).strokeWidth(2).noFill
    }
  }

  def walk(steps: Int): Random[Image] = {
    def iter(
        step: Int,
        start: Random[Point],
        shape: Image
    ): List[Random[Image]] =
      step match {
        case 0 =>
          Nil
        case _ =>
          val here = start.map(shape at _.toVec)
          val next = start flatMap (pt => brownianMotion(pt, drift))
          here :: iter(step - 1, next, shape)
      }

    smoke.flatMap { shape =>
      iter(steps, Random.always(start), shape).sequence.map { imgs =>
        imgs.foldLeft(Image.empty) { _ on _ }
      }
    }
  }

  def walkParticles(nParticles: Int, steps: Int): Random[Image] =
    (1 to nParticles).toList
      .map { _ =>
        walk(steps)
      }
      .sequence
      .map { imgs =>
        imgs.foldLeft(Image.empty) { _ on _ }
      }

  val image: Random[Image] =
    walkParticles(10, 100)
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy