diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3b8f3a8d..32bae1f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 diff --git a/build.sbt b/build.sbt index ffda0bf3..b5aa6f68 100644 --- a/build.sbt +++ b/build.sbt @@ -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 diff --git a/core/jvm/src/main/scala/doodle/effect/BufferedImageWriter.scala b/core/jvm/src/main/scala/doodle/effect/BufferedImageWriter.scala index d1d94dbb..2f6d7bc8 100644 --- a/core/jvm/src/main/scala/doodle/effect/BufferedImageWriter.scala +++ b/core/jvm/src/main/scala/doodle/effect/BufferedImageWriter.scala @@ -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 diff --git a/core/jvm/src/main/scala/doodle/syntax/BufferedImageWriterSyntax.scala b/core/jvm/src/main/scala/doodle/syntax/BufferedImageWriterSyntax.scala index 01c7ff8e..a8f60227 100644 --- a/core/jvm/src/main/scala/doodle/syntax/BufferedImageWriterSyntax.scala +++ b/core/jvm/src/main/scala/doodle/syntax/BufferedImageWriterSyntax.scala @@ -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 { diff --git a/core/shared/src/main/scala/doodle/effect/DefaultRenderer.scala b/core/shared/src/main/scala/doodle/effect/DefaultFrame.scala similarity index 59% rename from core/shared/src/main/scala/doodle/effect/DefaultRenderer.scala rename to core/shared/src/main/scala/doodle/effect/DefaultFrame.scala index 7ca84db7..dc0dc223 100644 --- a/core/shared/src/main/scala/doodle/effect/DefaultRenderer.scala +++ b/core/shared/src/main/scala/doodle/effect/DefaultFrame.scala @@ -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 } diff --git a/core/shared/src/main/scala/doodle/syntax/AbstractRendererSyntax.scala b/core/shared/src/main/scala/doodle/syntax/AbstractRendererSyntax.scala index 4078985e..6cb4214f 100644 --- a/core/shared/src/main/scala/doodle/syntax/AbstractRendererSyntax.scala +++ b/core/shared/src/main/scala/doodle/syntax/AbstractRendererSyntax.scala @@ -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. @@ -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()) @@ -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 diff --git a/docs/src/pages/effect/README.md b/docs/src/pages/effect/README.md index 32d81007..f78dd7d1 100644 --- a/docs/src/pages/effect/README.md +++ b/docs/src/pages/effect/README.md @@ -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. diff --git a/image/shared/src/main/scala/doodle/image/syntax/AbstractImageSyntax.scala b/image/shared/src/main/scala/doodle/image/syntax/AbstractImageSyntax.scala index ceeec988..85b50873 100644 --- a/image/shared/src/main/scala/doodle/image/syntax/AbstractImageSyntax.scala +++ b/image/shared/src/main/scala/doodle/image/syntax/AbstractImageSyntax.scala @@ -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 @@ -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() diff --git a/interact/shared/src/main/scala/doodle/interact/effect/AnimationWriter.scala b/interact/shared/src/main/scala/doodle/interact/effect/AnimationWriter.scala index 8215acc8..993589a5 100644 --- a/interact/shared/src/main/scala/doodle/interact/effect/AnimationWriter.scala +++ b/interact/shared/src/main/scala/doodle/interact/effect/AnimationWriter.scala @@ -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 diff --git a/java2d/src/main/scala/doodle/java2d/effect/DefaultFrame.scala b/java2d/src/main/scala/doodle/java2d/effect/DefaultFrame.scala new file mode 100644 index 00000000..6fe67b75 --- /dev/null +++ b/java2d/src/main/scala/doodle/java2d/effect/DefaultFrame.scala @@ -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) +} diff --git a/java2d/src/main/scala/doodle/java2d/effect/Java2d.scala b/java2d/src/main/scala/doodle/java2d/effect/Java2d.scala index 55c713c6..37f962bb 100644 --- a/java2d/src/main/scala/doodle/java2d/effect/Java2d.scala +++ b/java2d/src/main/scala/doodle/java2d/effect/Java2d.scala @@ -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 diff --git a/java2d/src/main/scala/doodle/java2d/effect/Java2dBufferedImageWriter.scala b/java2d/src/main/scala/doodle/java2d/effect/Java2dBufferedImageWriter.scala index a1b251e2..f6af1df4 100644 --- a/java2d/src/main/scala/doodle/java2d/effect/Java2dBufferedImageWriter.scala +++ b/java2d/src/main/scala/doodle/java2d/effect/Java2dBufferedImageWriter.scala @@ -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 diff --git a/java2d/src/main/scala/doodle/java2d/effect/Java2dRenderer.scala b/java2d/src/main/scala/doodle/java2d/effect/Java2dRenderer.scala index e37e47f3..ca4bd2e2 100644 --- a/java2d/src/main/scala/doodle/java2d/effect/Java2dRenderer.scala +++ b/java2d/src/main/scala/doodle/java2d/effect/Java2dRenderer.scala @@ -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 { diff --git a/java2d/src/main/scala/doodle/java2d/package.scala b/java2d/src/main/scala/doodle/java2d/package.scala index dd4f3836..1ddf8786 100644 --- a/java2d/src/main/scala/doodle/java2d/package.scala +++ b/java2d/src/main/scala/doodle/java2d/package.scala @@ -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 = @@ -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 diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 88aacab3..da623899 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -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" diff --git a/project/build.properties b/project/build.properties index e8a1e246..04267b14 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.9.7 +sbt.version=1.9.9 diff --git a/project/plugins.sbt b/project/plugins.sbt index 7eb36854..a4447605 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -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") diff --git a/reactor/shared/src/main/scala/doodle/reactor/Reactor.scala b/reactor/shared/src/main/scala/doodle/reactor/Reactor.scala index 42860ec8..000bfa7a 100644 --- a/reactor/shared/src/main/scala/doodle/reactor/Reactor.scala +++ b/reactor/shared/src/main/scala/doodle/reactor/Reactor.scala @@ -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 {