Skip to content

Commit

Permalink
Merge pull request #153 from creativescala/feature/default-frame
Browse files Browse the repository at this point in the history
Implement `DefaultFrame` type class
  • Loading branch information
noelwelsh authored Mar 20, 2024
2 parents 643f034 + 4608bfa commit 1d9c0c0
Show file tree
Hide file tree
Showing 18 changed files with 84 additions and 51 deletions.
14 changes: 7 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ jobs:
- name: Setup Java (temurin@8)
id: setup-java-temurin-8
if: matrix.java == 'temurin@8'
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 8
Expand Down Expand Up @@ -79,7 +79,7 @@ jobs:

- name: Upload target directories
if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main')
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v4
with:
name: target-${{ matrix.os }}-${{ matrix.java }}-${{ matrix.scala }}
path: targets.tar
Expand All @@ -102,7 +102,7 @@ jobs:
- name: Setup Java (temurin@8)
id: setup-java-temurin-8
if: matrix.java == 'temurin@8'
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 8
Expand All @@ -113,7 +113,7 @@ jobs:
run: sbt +update

- name: Download target directories (3)
uses: actions/download-artifact@v3
uses: actions/download-artifact@v4
with:
name: target-${{ matrix.os }}-${{ matrix.java }}-3

Expand Down Expand Up @@ -163,7 +163,7 @@ jobs:
- name: Setup Java (temurin@8)
id: setup-java-temurin-8
if: matrix.java == 'temurin@8'
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 8
Expand Down Expand Up @@ -195,7 +195,7 @@ jobs:
- name: Setup Java (temurin@8)
id: setup-java-temurin-8
if: matrix.java == 'temurin@8'
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 8
Expand All @@ -208,7 +208,7 @@ jobs:
- name: Setup Java (temurin@11)
id: setup-java-temurin-11
if: matrix.java == 'temurin@11'
uses: actions/setup-java@v3
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 11
Expand Down
2 changes: 1 addition & 1 deletion build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ ThisBuild / developers := List(
// true by default, set to false to publish to s01.oss.sonatype.org
ThisBuild / tlSonatypeUseLegacyHost := true

lazy val scala3 = "3.3.1"
lazy val scala3 = "3.3.3"

ThisBuild / crossScalaVersions := List(scala3)
ThisBuild / scalaVersion := scala3
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ package effect
import cats.effect.IO
import doodle.algebra.Algebra
import doodle.algebra.Picture

import java.awt.image.BufferedImage

/** The BufferedImageWriter type represent the ability to encode an image as a
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import cats.effect.unsafe.IORuntime
import doodle.algebra.Algebra
import doodle.algebra.Picture
import doodle.effect.BufferedImageWriter

import java.awt.image.BufferedImage

trait BufferedImageWriterSyntax {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,16 @@
* limitations under the License.
*/

package doodle
package effect
package doodle.effect

import doodle.algebra.Algebra

/** The `DefaultRenderer` typeclass is a `Renderer` that has a reasonable
* default frame.
/** The `DefaultFrame` typeclass provides a reasonable default `Frame`. This is
* a convenience for `Renderer` or `Writer` users that don't want to have to
* create a `Frame` instance.
*/
trait DefaultRenderer[+Alg <: Algebra, Frame, Canvas]
extends Renderer[Alg, Frame, Canvas] {
trait DefaultFrame[Frame] {
def default: Frame
}
object DefaultRenderer {
def apply[Alg <: Algebra, Frame, Canvas](implicit
renderer: DefaultRenderer[Alg, Frame, Canvas]
): DefaultRenderer[Alg, Frame, Canvas] = renderer
object DefaultFrame {
def apply[Frame](implicit frame: DefaultFrame[Frame]): DefaultFrame[Frame] =
frame
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ import cats.effect.IO
import cats.effect.unsafe.IORuntime
import doodle.algebra.Algebra
import doodle.algebra.Picture
import doodle.effect.DefaultRenderer
import doodle.effect.DefaultFrame
import doodle.effect.Renderer

/** Rendering works differently on different platforms. The Javascript runtime
* must render asynchronously. The JVM runtime can render asychronously or
* sychronously. However, rendering in a Swing / Java2D context takes places on
* a daemon thread. This means the JVM will exit if this is the only thread
* running. The implication is that short Doodle program that does not block
* running. The implication is that a short Doodle program that does not block
* the main thread waiting for the Swing thread to complete will usually exit
* before the output appears. Therefore, at least in the common case of calling
* `draw`, rendering should be synchronous on the JVM.
Expand All @@ -48,7 +48,8 @@ trait AbstractRendererSyntax {
* options for this `Renderer`.
*/
def draw[Frame, Canvas]()(implicit
renderer: DefaultRenderer[Alg, Frame, Canvas],
renderer: Renderer[Alg, Frame, Canvas],
frame: DefaultFrame[Frame],
r: IORuntime
): Unit =
runIO(drawToIO())
Expand All @@ -73,10 +74,11 @@ trait AbstractRendererSyntax {
* `Frame` for this `Renderer`.
*/
def drawToIO[Frame, Canvas]()(implicit
renderer: DefaultRenderer[Alg, Frame, Canvas]
renderer: Renderer[Alg, Frame, Canvas],
frame: DefaultFrame[Frame]
): IO[A] =
renderer
.canvas(renderer.default)
.canvas(frame.default)
.flatMap(canvas => drawWithCanvasToIO(canvas))

/** Create an effect that, when run, will draw the `Picture` using the given
Expand Down
11 changes: 9 additions & 2 deletions docs/src/pages/effect/README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Effects

A picture is a description of what should be drawn. An effect carries out the description by, for example, drawing a picture to the screen. Another example includes converting a picture to a different type or saving it to a file which is managed by a special class of effects called `Writers`.
A picture is a description of what should be drawn. An effect carries out the description by, for example, drawing a picture to the screen or writing it to a file. Effects are implemented as type classes and defined in the @:api(doodle.effect) package. Users will not usually interact directly with effects, but instead work with the syntax defined for them. For example calling `draw` on a `Picture` is syntax that uses the @:api(doodle.effect.Renderer) effect.

Effects are defined in the @:api(doodle.effect.index) package. All effects have @:api(doodle.syntax.index) that makes them easier to use. You should not have to use effects directly unless you are extending Doodle.
The main effects are:

* @:api(doodle.effect.Renderer), which draws a `Picture` to the screen.
* @:api(doodle.effect.Writer), is a marker trait for effects that convert a `Picture` to some other type or write it to a file.
* @:api(doodle.effect.FileWriter), for writing to a `File`.
* @:api(doodle.effect.Base64Writer), for writing to a base 64 encoded value.
* @:api(doodle.effect.BufferedImageWriter), for writing to a `BufferedImage` on the JVM.
* @:api(doodle.effect.DefaultFrame), not strictly an effect but a useful for utility to obtain a default frame instance. This makes other operations more convenient, as the user does not need to specify a frame.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ package image
package syntax

import cats.effect.unsafe.IORuntime
import doodle.effect.DefaultRenderer
import doodle.effect.DefaultFrame
import doodle.effect.Renderer
import doodle.language.Basic
import doodle.syntax.AbstractRendererSyntax
Expand All @@ -34,7 +34,8 @@ abstract class AbstractImageSyntax(rendererSyntax: AbstractRendererSyntax) {
image.compile[Alg].drawWithFrame(frame)

def draw[Alg <: Basic, Frame, Canvas]()(implicit
renderer: DefaultRenderer[Alg, Frame, Canvas],
renderer: Renderer[Alg, Frame, Canvas],
frame: DefaultFrame[Frame],
r: IORuntime
): Unit =
image.compile[Alg].draw()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ import cats.Monoid
import cats.effect.IO
import doodle.algebra.Algebra
import doodle.algebra.Picture
import fs2.Stream
import doodle.effect.Writer
import fs2.Stream

import java.io.File

Expand Down
23 changes: 23 additions & 0 deletions java2d/src/main/scala/doodle/java2d/effect/DefaultFrame.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* 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.java2d.effect

import doodle.effect.DefaultFrame

object Java2dDefaultFrame extends DefaultFrame[Frame] {
val default: Frame = Frame.default.withSizedToPicture(20)
}
9 changes: 4 additions & 5 deletions java2d/src/main/scala/doodle/java2d/effect/Java2d.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,15 @@ package java2d
package effect

import cats.effect.IO
import doodle.algebra.generic.*
import doodle.core.BoundingBox
import doodle.core.{Transform => Tx}
import doodle.core.Color
import doodle.core.Transform

import doodle.algebra.generic.*
import doodle.java2d.algebra.Graphics2DGraphicsContext
import doodle.java2d.algebra.reified.Reified
import doodle.core.{Transform => Tx}
import doodle.java2d.algebra.Algebra
import doodle.java2d.algebra.Graphics2DGraphicsContext
import doodle.java2d.algebra.reified.Reification
import doodle.java2d.algebra.reified.Reified
import doodle.java2d.algebra.{Java2D => Java2dAlgebra}

import java.awt.Graphics2D
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package effect
import cats.effect.IO
import doodle.effect.*
import doodle.java2d.effect.{Java2d => Java2dEffect}

import java.awt.image.BufferedImage

object Java2dBufferedImageWriter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,16 @@ package java2d
package effect

import cats.effect.IO
import doodle.effect.DefaultRenderer
import doodle.effect.Renderer

import javax.swing.JFrame

object Java2dRenderer extends DefaultRenderer[Algebra, Frame, Canvas] {
object Java2dRenderer extends Renderer[Algebra, Frame, Canvas] {

import cats.effect.unsafe.implicits.global

private var jFrames: List[JFrame] = List.empty

val default: Frame = Frame.default.withSizedToPicture(20)

def canvas(description: Frame): IO[Canvas] =
Canvas(description).flatMap { jFrame =>
IO {
Expand Down
9 changes: 6 additions & 3 deletions java2d/src/main/scala/doodle/java2d/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@ package doodle
import doodle.algebra._
import doodle.core.format._
import doodle.effect.Base64Writer
import doodle.effect.DefaultRenderer
import doodle.effect.BufferedImageWriter
import doodle.effect.DefaultFrame
import doodle.effect.FileWriter
import doodle.effect.Renderer
import doodle.interact.algebra._
import doodle.interact.effect.AnimationRenderer
import doodle.interact.effect.AnimationWriter
import doodle.java2d.algebra.reified.Reification
import doodle.language.Basic
import doodle.effect.BufferedImageWriter

package object java2d extends Java2dToPicture {
type Algebra =
Expand All @@ -52,8 +53,10 @@ package object java2d extends Java2dToPicture {
doodle.java2d.effect.Java2dAnimationWriter

implicit val java2dRenderer
: DefaultRenderer[Algebra, doodle.java2d.effect.Frame, Canvas] =
: Renderer[Algebra, doodle.java2d.effect.Frame, Canvas] =
doodle.java2d.effect.Java2dRenderer
implicit val java2dFrame: DefaultFrame[doodle.java2d.effect.Frame] =
doodle.java2d.effect.Java2dDefaultFrame
implicit val java2dGifWriter
: FileWriter[Algebra, Frame, Gif] with Base64Writer[Algebra, Frame, Gif] =
doodle.java2d.effect.Java2dGifWriter
Expand Down
4 changes: 2 additions & 2 deletions project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import org.portablescala.sbtplatformdeps.PlatformDepsPlugin.autoImport._
object Dependencies {
// Library Versions
val catsVersion = "2.10.0"
val catsEffectVersion = "3.5.2"
val fs2Version = "3.9.3"
val catsEffectVersion = "3.5.4"
val fs2Version = "3.10.0"

val scalatagsVersion = "0.12.0"

Expand Down
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
@@ -1 +1 @@
sbt.version=1.9.7
sbt.version=1.9.9
8 changes: 4 additions & 4 deletions project/plugins.sbt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.14.0")
addSbtPlugin("org.scala-js" % "sbt-scalajs" % "1.16.0")
addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.6.4")
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.2")
addSbtPlugin("com.github.sbt" % "sbt-unidoc" % "0.5.0")
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.11.1")
addSbtPlugin("org.typelevel" % "sbt-typelevel" % "0.6.2")
addSbtPlugin("org.typelevel" % "sbt-typelevel-site" % "0.6.2")
addSbtPlugin("ch.epfl.scala" % "sbt-scalafix" % "0.12.0")
addSbtPlugin("org.typelevel" % "sbt-typelevel" % "0.6.7")
addSbtPlugin("org.typelevel" % "sbt-typelevel-site" % "0.6.7")
5 changes: 3 additions & 2 deletions reactor/shared/src/main/scala/doodle/reactor/Reactor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,12 @@ final case class Reactor[A](
}

def draw[Alg <: Basic, Frame, Canvas]()(implicit
renderer: DefaultRenderer[Alg, Frame, Canvas],
renderer: Renderer[Alg, Frame, Canvas],
frame: DefaultFrame[Frame],
runtime: IORuntime
): Unit = {
import doodle.image.syntax.all._
this.image.draw()(renderer, runtime)
this.image.draw()(renderer, frame, runtime)
}
}
object Reactor {
Expand Down

0 comments on commit 1d9c0c0

Please sign in to comment.