From b46b037be8d2244c16487ac36839f45daf6fdfe9 Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Sun, 23 Jun 2024 13:45:45 +0530 Subject: [PATCH 01/43] Something --- build.sbt | 4 +- .../doodle/canvas/algebra/CanvasAlgebra.scala | 2 + .../doodle/canvas/algebra/CanvasDrawing.scala | 6 ++ .../scala/doodle/canvas/algebra/Raster.scala | 15 +++ .../scala/doodle/canvas/algebra/Shape.scala | 7 +- .../main/scala/doodle/syntax/package.scala | 4 + .../main/scala/doodle/syntax/package.scala | 4 + .../main/scala/doodle/algebra/Raster.scala | 16 +++ .../algebra/generic/GenericRaster.scala | 25 +++++ .../doodle/algebra/generic/GenericShape.scala | 4 +- .../scala/doodle/syntax/RasterSyntax.scala | 16 +++ docs/src/pages/canvas/examples.md | 2 +- .../examples/canvas/ConcentricCircles.scala | 37 ++++--- .../examples/canvas/ParametricSpiral.scala | 98 +++++++++++++++++++ 14 files changed, 223 insertions(+), 17 deletions(-) create mode 100644 canvas/src/main/scala/doodle/canvas/algebra/Raster.scala create mode 100644 core/shared/src/main/scala/doodle/algebra/Raster.scala create mode 100644 core/shared/src/main/scala/doodle/algebra/generic/GenericRaster.scala create mode 100644 core/shared/src/main/scala/doodle/syntax/RasterSyntax.scala create mode 100644 examples/js/src/main/scala/doodle/examples/canvas/ParametricSpiral.scala diff --git a/build.sbt b/build.sbt index 4f521a7d..aab773ce 100644 --- a/build.sbt +++ b/build.sbt @@ -152,9 +152,9 @@ lazy val docs = val dest1 = mdocOut.value val dest2 = (laikaSite / target).value val cmd1 = - s"npx tailwindcss -i ${src.toString}/creative-scala.css -o ${dest1.toString}/creative-scala.css" + s"cmd /c npx tailwindcss -i ${src.toString}/creative-scala.css -o ${dest1.toString}/creative-scala.css" val cmd2 = - s"npx tailwindcss -i ${src.toString}/creative-scala.css -o ${dest2.toString}/creative-scala.css" + s"cmd /c npx tailwindcss -i ${src.toString}/creative-scala.css -o ${dest2.toString}/creative-scala.css" cmd1 ! cmd2 ! diff --git a/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala b/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala index 6d29d825..3276d48c 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala @@ -30,6 +30,8 @@ final case class CanvasAlgebra( functorDrawing: Functor[CanvasDrawing] = Apply.apply[CanvasDrawing] ) extends Path, Shape, + Raster, + GenericRaster[CanvasDrawing], GenericDebug[CanvasDrawing], GenericLayout[CanvasDrawing], GenericSize[CanvasDrawing], diff --git a/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala b/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala index ef94387e..a5cdcf8e 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala @@ -121,6 +121,12 @@ object CanvasDrawing { } } + def raster[CanvasRenderingContext2D](width: Int, height: Int)( + f: CanvasRenderingContext2D => Unit + ): CanvasDrawing[Unit] = { + CanvasDrawing.raster(width, height)(f) + } + def setFill(fill: Option[Fill]): CanvasDrawing[Unit] = fill.map(setFill).getOrElse(unit) diff --git a/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala b/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala new file mode 100644 index 00000000..19ac65b3 --- /dev/null +++ b/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala @@ -0,0 +1,15 @@ +package doodle.canvas.algebra + +import doodle.algebra.Algebra +import doodle.algebra.generic.* + +trait Raster extends GenericRaster[CanvasDrawing] { + self: Algebra { type Drawing[Unit] = Finalized[CanvasDrawing, Unit] } => + object RasterApi extends RasterApi { + def raster[A](width: Int, height: Int)( + f: A => Unit + ): CanvasDrawing[Unit] = { + CanvasDrawing.raster(width, height)(f) + } + } +} diff --git a/canvas/src/main/scala/doodle/canvas/algebra/Shape.scala b/canvas/src/main/scala/doodle/canvas/algebra/Shape.scala index 803f7965..ac86c8be 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/Shape.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/Shape.scala @@ -32,13 +32,14 @@ trait Shape extends GenericShape[CanvasDrawing] { stroke: Option[Stroke], width: Double, height: Double - ): CanvasDrawing[Unit] = + ): CanvasDrawing[Unit] = { CanvasDrawing.setTransform(tx) >> CanvasDrawing.withStroke(stroke) { CanvasDrawing.withFill(fill) { CanvasDrawing.rectangle(width, height) } } + } def triangle( tx: Tx, @@ -46,13 +47,15 @@ trait Shape extends GenericShape[CanvasDrawing] { stroke: Option[Stroke], width: Double, height: Double - ): CanvasDrawing[Unit] = + ): CanvasDrawing[Unit] = { + println("Inside triangle here") CanvasDrawing.setTransform(tx) >> CanvasDrawing.withStroke(stroke) { CanvasDrawing.withFill(fill) { CanvasDrawing.closedPath(ClosedPath.triangle(width, height)) } } + } def circle( tx: Tx, diff --git a/core/js/src/main/scala/doodle/syntax/package.scala b/core/js/src/main/scala/doodle/syntax/package.scala index 607578ca..6cf6cd22 100644 --- a/core/js/src/main/scala/doodle/syntax/package.scala +++ b/core/js/src/main/scala/doodle/syntax/package.scala @@ -16,6 +16,8 @@ package doodle +import java.awt.image.Raster + package object syntax { object all extends AngleSyntax @@ -25,6 +27,7 @@ package object syntax { with LayoutSyntax with NormalizedSyntax with PathSyntax + with RasterSyntax with RendererSyntax with ShapeSyntax with SizeSyntax @@ -41,6 +44,7 @@ package object syntax { object layout extends LayoutSyntax object normalized extends NormalizedSyntax object path extends PathSyntax + object raster extends RasterSyntax object renderer extends RendererSyntax object shape extends ShapeSyntax object size extends SizeSyntax diff --git a/core/jvm/src/main/scala/doodle/syntax/package.scala b/core/jvm/src/main/scala/doodle/syntax/package.scala index bdb53859..d4583cd2 100644 --- a/core/jvm/src/main/scala/doodle/syntax/package.scala +++ b/core/jvm/src/main/scala/doodle/syntax/package.scala @@ -16,6 +16,8 @@ package doodle +import java.awt.image.Raster + package object syntax { object all extends AngleSyntax @@ -28,6 +30,7 @@ package object syntax { with NormalizedSyntax with PathSyntax with RendererSyntax + with RasterSyntax with ShapeSyntax with SizeSyntax with StyleSyntax @@ -46,6 +49,7 @@ package object syntax { object layout extends LayoutSyntax object normalized extends NormalizedSyntax object path extends PathSyntax + object raster extends RasterSyntax object renderer extends RendererSyntax object shape extends ShapeSyntax object size extends SizeSyntax diff --git a/core/shared/src/main/scala/doodle/algebra/Raster.scala b/core/shared/src/main/scala/doodle/algebra/Raster.scala new file mode 100644 index 00000000..76b909f2 --- /dev/null +++ b/core/shared/src/main/scala/doodle/algebra/Raster.scala @@ -0,0 +1,16 @@ +package doodle +package algebra + +trait Raster extends Algebra { + def raster[A](width: Int, height: Int)(f: A => Unit): Drawing[Unit] +} + +trait RasterConstructor { + self: BaseConstructor { type Algebra <: Raster } => + + def raster[A](width: Int, height: Int)(f: A => Unit): Picture[Unit] = + new Picture[Unit] { + def apply(implicit algebra: Algebra): algebra.Drawing[Unit] = + algebra.raster(width, height)(f) + } +} diff --git a/core/shared/src/main/scala/doodle/algebra/generic/GenericRaster.scala b/core/shared/src/main/scala/doodle/algebra/generic/GenericRaster.scala new file mode 100644 index 00000000..c607910d --- /dev/null +++ b/core/shared/src/main/scala/doodle/algebra/generic/GenericRaster.scala @@ -0,0 +1,25 @@ +package doodle +package algebra +package generic + +import cats.data.State +import doodle.core.BoundingBox + +trait GenericRaster[G[_]] extends Raster { + self: Algebra { type Drawing[Unit] = Finalized[G, Unit] } => + trait RasterApi { + def raster[A](width: Int, height: Int)(f: A => Unit): G[Unit] + } + + def RasterApi: RasterApi + + def raster[A](width: Int, height: Int)(f: A => Unit): Finalized[G, Unit] = { + Finalized.leaf { dc => + val bb = BoundingBox.centered(width, height) + ( + bb, + State.inspect(_ => RasterApi.raster(width, height)(f)) + ) + } + } +} diff --git a/core/shared/src/main/scala/doodle/algebra/generic/GenericShape.scala b/core/shared/src/main/scala/doodle/algebra/generic/GenericShape.scala index 3d3891eb..a10a6b00 100644 --- a/core/shared/src/main/scala/doodle/algebra/generic/GenericShape.scala +++ b/core/shared/src/main/scala/doodle/algebra/generic/GenericShape.scala @@ -33,6 +33,7 @@ trait GenericShape[G[_]] extends Shape { width: Double, height: Double ): G[Unit] + def triangle( tx: Tx, fill: Option[Fill], @@ -51,7 +52,7 @@ trait GenericShape[G[_]] extends Shape { def ShapeApi: ShapeApi - def rectangle(width: Double, height: Double): Finalized[G, Unit] = + def rectangle(width: Double, height: Double): Finalized[G, Unit] = { Finalized.leaf { dc => val strokeWidth = dc.strokeWidth.getOrElse(0.0) val bb = BoundingBox.centered(strokeWidth + width, strokeWidth + height) @@ -62,6 +63,7 @@ trait GenericShape[G[_]] extends Shape { ) ) } + } def square(width: Double): Finalized[G, Unit] = rectangle(width, width) diff --git a/core/shared/src/main/scala/doodle/syntax/RasterSyntax.scala b/core/shared/src/main/scala/doodle/syntax/RasterSyntax.scala new file mode 100644 index 00000000..8452b69f --- /dev/null +++ b/core/shared/src/main/scala/doodle/syntax/RasterSyntax.scala @@ -0,0 +1,16 @@ +package doodle +package syntax + +import doodle.algebra.Picture +import doodle.algebra.Raster + +trait RasterSyntax { + def raster[Alg <: Raster, A]( + width: Int, + height: Int + )(f: A => Unit): Picture[Alg, Unit] = + new Picture[Alg, Unit] { + def apply(implicit algebra: Alg): algebra.Drawing[Unit] = + algebra.raster(width, height)(f) + } +} \ No newline at end of file diff --git a/docs/src/pages/canvas/examples.md b/docs/src/pages/canvas/examples.md index 55b3df82..9ee02dcd 100644 --- a/docs/src/pages/canvas/examples.md +++ b/docs/src/pages/canvas/examples.md @@ -5,7 +5,7 @@ The source for these examples is [in the repository](https://github.com/creative ## Concentric Circles -@:doodle("concentric-circles", "CanvasConcentricCircles.draw") +@:doodle("Example1", "Example1.draw") ## Parametric Spiral diff --git a/examples/js/src/main/scala/doodle/examples/canvas/ConcentricCircles.scala b/examples/js/src/main/scala/doodle/examples/canvas/ConcentricCircles.scala index 0e8b69f8..0eb9fa7f 100644 --- a/examples/js/src/main/scala/doodle/examples/canvas/ConcentricCircles.scala +++ b/examples/js/src/main/scala/doodle/examples/canvas/ConcentricCircles.scala @@ -23,18 +23,33 @@ import doodle.syntax.all.* import scala.scalajs.js.annotation.* -@JSExportTopLevel("CanvasConcentricCircles") -object ConcentricCircles { - def circles(count: Int): Picture[Unit] = - if count == 0 then - Picture.circle(20).fillColor(Color.hsl(0.degrees, 0.7, 0.6)) - else - Picture - .circle(count.toDouble * 20.0) - .fillColor(Color.hsl((count * 15).degrees, 0.7, 0.6)) - .under(circles(count - 1)) +// @JSExportTopLevel("Example1") +// object ConcentricCircles { +// def circles(count: Int): Picture[Unit] = +// if count == 0 then +// Picture.circle(20).fillColor(Color.hsl(0.degrees, 0.7, 0.6)) +// else +// Picture +// .circle(count.toDouble * 20.0) +// .fillColor(Color.hsl((count * 15).degrees, 0.7, 0.6)) +// .under(circles(count - 1)) + +// @JSExport +// def draw(mount: String) = +// circles(7).drawWithFrame(Frame(mount)) +// } + +@JSExportTopLevel("Example1") +object Rectangle { + val rect = Picture.rectangle(50, 50).fillColor(Color.red) + val cir = Picture.circle(50).fillColor(Color.blue) + val cir2 = Picture.circle(50).fillColor(Color.green) + + val joint = rect.beside(cir).beside(cir2) + + val bruh = raster(10,10) @JSExport def draw(mount: String) = - circles(7).drawWithFrame(Frame(mount)) + joint.drawWithFrame(Frame(mount)) } diff --git a/examples/js/src/main/scala/doodle/examples/canvas/ParametricSpiral.scala b/examples/js/src/main/scala/doodle/examples/canvas/ParametricSpiral.scala new file mode 100644 index 00000000..3bae540b --- /dev/null +++ b/examples/js/src/main/scala/doodle/examples/canvas/ParametricSpiral.scala @@ -0,0 +1,98 @@ +/* + * 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.examples.canvas + +/* + * 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. + */ + +import cats.effect.unsafe.implicits.global +import doodle.canvas.{*, given} +import doodle.core.* +import doodle.syntax.all.* + +import scala.scalajs.js.annotation.* + +// @JSExportTopLevel("Example2") +// object ParametricSpiral { + +// def parametricSpiral(angle: Angle): Point = +// Point((Math.exp(angle.toTurns) - 1) * 200, angle) + +// def drawCurve( +// points: Int, +// marker: Point => Picture[Unit], +// curve: Angle => Point +// ): Picture[Unit] = { +// // Angle.one is one complete turn. I.e. 360 degrees +// val turn = Angle.one / points.toDouble +// def loop(count: Int): Picture[Unit] = { +// count match { +// case 0 => +// val pt = curve(Angle.zero) +// marker(pt).at(pt) +// case n => +// val pt = curve(turn * count.toDouble) +// marker(pt).at(pt).on(loop(n - 1)) +// } +// } + +// loop(points) +// } + +// @JSExport +// def draw(id: String): Unit = { +// val marker = (point: Point) => +// Picture +// .circle(point.r * 0.125 + 7) +// .fillColor(Color.red.spin(point.angle / 4.0)) +// .noStroke + +// drawCurve(20, marker, parametricSpiral _).drawWithFrame(Frame(id)) +// } +// } + +@JSExportTopLevel("Example2") +object Rectangle2 { + val rect = Picture.rectangle(50, 10).fillColor(Color.blue) + val rect2 = Picture.rectangle(50, 10).fillColor(Color.red) + + val joint = rect.below(rect2) + + // val immediate = (gc: GraphicsContext) => { + // gc.setColor(Color.red) + // gc.fillRect(0, 0, 50, 10) + // gc.setColor(Color.blue) + // gc.fillRect(0, 10, 50, 10) + // } + + @JSExport + def draw(mount: String) = + joint.drawWithFrame(Frame(mount)) +} From ac31aad5d0fda079c27f0e8b0df1d4af761e0c41 Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Sun, 23 Jun 2024 14:04:05 +0530 Subject: [PATCH 02/43] Formatting and undoing experimental changes --- build.sbt | 4 +- .../doodle/canvas/algebra/CanvasAlgebra.scala | 4 +- .../scala/doodle/canvas/algebra/Raster.scala | 16 +++ .../scala/doodle/canvas/algebra/Shape.scala | 9 +- .../main/scala/doodle/syntax/package.scala | 2 - .../main/scala/doodle/syntax/package.scala | 4 +- .../main/scala/doodle/algebra/Raster.scala | 16 +++ .../algebra/generic/GenericRaster.scala | 16 +++ .../doodle/algebra/generic/GenericShape.scala | 6 +- .../examples/canvas/ConcentricCircles.scala | 39 +++----- .../examples/canvas/ParametricSpiral.scala | 98 ------------------- 11 files changed, 70 insertions(+), 144 deletions(-) delete mode 100644 examples/js/src/main/scala/doodle/examples/canvas/ParametricSpiral.scala diff --git a/build.sbt b/build.sbt index aab773ce..4f521a7d 100644 --- a/build.sbt +++ b/build.sbt @@ -152,9 +152,9 @@ lazy val docs = val dest1 = mdocOut.value val dest2 = (laikaSite / target).value val cmd1 = - s"cmd /c npx tailwindcss -i ${src.toString}/creative-scala.css -o ${dest1.toString}/creative-scala.css" + s"npx tailwindcss -i ${src.toString}/creative-scala.css -o ${dest1.toString}/creative-scala.css" val cmd2 = - s"cmd /c npx tailwindcss -i ${src.toString}/creative-scala.css -o ${dest2.toString}/creative-scala.css" + s"npx tailwindcss -i ${src.toString}/creative-scala.css -o ${dest2.toString}/creative-scala.css" cmd1 ! cmd2 ! diff --git a/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala b/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala index 3276d48c..3e195dcf 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala @@ -29,11 +29,11 @@ final case class CanvasAlgebra( applyDrawing: Apply[CanvasDrawing] = Apply.apply[CanvasDrawing], functorDrawing: Functor[CanvasDrawing] = Apply.apply[CanvasDrawing] ) extends Path, - Shape, Raster, - GenericRaster[CanvasDrawing], + Shape, GenericDebug[CanvasDrawing], GenericLayout[CanvasDrawing], + GenericRaster[CanvasDrawing], GenericSize[CanvasDrawing], GenericStyle[CanvasDrawing], GenericTransform[CanvasDrawing], diff --git a/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala b/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala index 19ac65b3..8b66bad1 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala @@ -1,3 +1,19 @@ +/* + * 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.canvas.algebra import doodle.algebra.Algebra diff --git a/canvas/src/main/scala/doodle/canvas/algebra/Shape.scala b/canvas/src/main/scala/doodle/canvas/algebra/Shape.scala index ac86c8be..a43c325c 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/Shape.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/Shape.scala @@ -32,14 +32,13 @@ trait Shape extends GenericShape[CanvasDrawing] { stroke: Option[Stroke], width: Double, height: Double - ): CanvasDrawing[Unit] = { + ): CanvasDrawing[Unit] = CanvasDrawing.setTransform(tx) >> CanvasDrawing.withStroke(stroke) { CanvasDrawing.withFill(fill) { CanvasDrawing.rectangle(width, height) } } - } def triangle( tx: Tx, @@ -47,15 +46,13 @@ trait Shape extends GenericShape[CanvasDrawing] { stroke: Option[Stroke], width: Double, height: Double - ): CanvasDrawing[Unit] = { - println("Inside triangle here") + ): CanvasDrawing[Unit] = CanvasDrawing.setTransform(tx) >> CanvasDrawing.withStroke(stroke) { CanvasDrawing.withFill(fill) { CanvasDrawing.closedPath(ClosedPath.triangle(width, height)) } } - } def circle( tx: Tx, @@ -73,4 +70,4 @@ trait Shape extends GenericShape[CanvasDrawing] { def unit: CanvasDrawing[Unit] = CanvasDrawing.unit } -} +} \ No newline at end of file diff --git a/core/js/src/main/scala/doodle/syntax/package.scala b/core/js/src/main/scala/doodle/syntax/package.scala index 6cf6cd22..103a8e12 100644 --- a/core/js/src/main/scala/doodle/syntax/package.scala +++ b/core/js/src/main/scala/doodle/syntax/package.scala @@ -16,8 +16,6 @@ package doodle -import java.awt.image.Raster - package object syntax { object all extends AngleSyntax diff --git a/core/jvm/src/main/scala/doodle/syntax/package.scala b/core/jvm/src/main/scala/doodle/syntax/package.scala index d4583cd2..ee8b89ee 100644 --- a/core/jvm/src/main/scala/doodle/syntax/package.scala +++ b/core/jvm/src/main/scala/doodle/syntax/package.scala @@ -16,8 +16,6 @@ package doodle -import java.awt.image.Raster - package object syntax { object all extends AngleSyntax @@ -29,8 +27,8 @@ package object syntax { with LayoutSyntax with NormalizedSyntax with PathSyntax - with RendererSyntax with RasterSyntax + with RendererSyntax with ShapeSyntax with SizeSyntax with StyleSyntax diff --git a/core/shared/src/main/scala/doodle/algebra/Raster.scala b/core/shared/src/main/scala/doodle/algebra/Raster.scala index 76b909f2..6549de38 100644 --- a/core/shared/src/main/scala/doodle/algebra/Raster.scala +++ b/core/shared/src/main/scala/doodle/algebra/Raster.scala @@ -1,3 +1,19 @@ +/* + * 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 algebra diff --git a/core/shared/src/main/scala/doodle/algebra/generic/GenericRaster.scala b/core/shared/src/main/scala/doodle/algebra/generic/GenericRaster.scala index c607910d..56557dc9 100644 --- a/core/shared/src/main/scala/doodle/algebra/generic/GenericRaster.scala +++ b/core/shared/src/main/scala/doodle/algebra/generic/GenericRaster.scala @@ -1,3 +1,19 @@ +/* + * 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 algebra package generic diff --git a/core/shared/src/main/scala/doodle/algebra/generic/GenericShape.scala b/core/shared/src/main/scala/doodle/algebra/generic/GenericShape.scala index a10a6b00..42bc2b74 100644 --- a/core/shared/src/main/scala/doodle/algebra/generic/GenericShape.scala +++ b/core/shared/src/main/scala/doodle/algebra/generic/GenericShape.scala @@ -33,7 +33,6 @@ trait GenericShape[G[_]] extends Shape { width: Double, height: Double ): G[Unit] - def triangle( tx: Tx, fill: Option[Fill], @@ -52,7 +51,7 @@ trait GenericShape[G[_]] extends Shape { def ShapeApi: ShapeApi - def rectangle(width: Double, height: Double): Finalized[G, Unit] = { + def rectangle(width: Double, height: Double): Finalized[G, Unit] = Finalized.leaf { dc => val strokeWidth = dc.strokeWidth.getOrElse(0.0) val bb = BoundingBox.centered(strokeWidth + width, strokeWidth + height) @@ -63,7 +62,6 @@ trait GenericShape[G[_]] extends Shape { ) ) } - } def square(width: Double): Finalized[G, Unit] = rectangle(width, width) @@ -96,4 +94,4 @@ trait GenericShape[G[_]] extends Shape { Finalized.leaf { _ => (BoundingBox.empty, Renderable.unit(ShapeApi.unit)) } -} +} \ No newline at end of file diff --git a/examples/js/src/main/scala/doodle/examples/canvas/ConcentricCircles.scala b/examples/js/src/main/scala/doodle/examples/canvas/ConcentricCircles.scala index 0eb9fa7f..a4727eff 100644 --- a/examples/js/src/main/scala/doodle/examples/canvas/ConcentricCircles.scala +++ b/examples/js/src/main/scala/doodle/examples/canvas/ConcentricCircles.scala @@ -23,33 +23,18 @@ import doodle.syntax.all.* import scala.scalajs.js.annotation.* -// @JSExportTopLevel("Example1") -// object ConcentricCircles { -// def circles(count: Int): Picture[Unit] = -// if count == 0 then -// Picture.circle(20).fillColor(Color.hsl(0.degrees, 0.7, 0.6)) -// else -// Picture -// .circle(count.toDouble * 20.0) -// .fillColor(Color.hsl((count * 15).degrees, 0.7, 0.6)) -// .under(circles(count - 1)) - -// @JSExport -// def draw(mount: String) = -// circles(7).drawWithFrame(Frame(mount)) -// } - -@JSExportTopLevel("Example1") -object Rectangle { - val rect = Picture.rectangle(50, 50).fillColor(Color.red) - val cir = Picture.circle(50).fillColor(Color.blue) - val cir2 = Picture.circle(50).fillColor(Color.green) - - val joint = rect.beside(cir).beside(cir2) - - val bruh = raster(10,10) +@JSExportTopLevel("CanvasConcentricCircles") +object ConcentricCircles { + def circles(count: Int): Picture[Unit] = + if count == 0 then + Picture.circle(20).fillColor(Color.hsl(0.degrees, 0.7, 0.6)) + else + Picture + .circle(count.toDouble * 20.0) + .fillColor(Color.hsl((count * 15).degrees, 0.7, 0.6)) + .under(circles(count - 1)) @JSExport def draw(mount: String) = - joint.drawWithFrame(Frame(mount)) -} + circles(7).drawWithFrame(Frame(mount)) +} \ No newline at end of file diff --git a/examples/js/src/main/scala/doodle/examples/canvas/ParametricSpiral.scala b/examples/js/src/main/scala/doodle/examples/canvas/ParametricSpiral.scala deleted file mode 100644 index 3bae540b..00000000 --- a/examples/js/src/main/scala/doodle/examples/canvas/ParametricSpiral.scala +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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.examples.canvas - -/* - * 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. - */ - -import cats.effect.unsafe.implicits.global -import doodle.canvas.{*, given} -import doodle.core.* -import doodle.syntax.all.* - -import scala.scalajs.js.annotation.* - -// @JSExportTopLevel("Example2") -// object ParametricSpiral { - -// def parametricSpiral(angle: Angle): Point = -// Point((Math.exp(angle.toTurns) - 1) * 200, angle) - -// def drawCurve( -// points: Int, -// marker: Point => Picture[Unit], -// curve: Angle => Point -// ): Picture[Unit] = { -// // Angle.one is one complete turn. I.e. 360 degrees -// val turn = Angle.one / points.toDouble -// def loop(count: Int): Picture[Unit] = { -// count match { -// case 0 => -// val pt = curve(Angle.zero) -// marker(pt).at(pt) -// case n => -// val pt = curve(turn * count.toDouble) -// marker(pt).at(pt).on(loop(n - 1)) -// } -// } - -// loop(points) -// } - -// @JSExport -// def draw(id: String): Unit = { -// val marker = (point: Point) => -// Picture -// .circle(point.r * 0.125 + 7) -// .fillColor(Color.red.spin(point.angle / 4.0)) -// .noStroke - -// drawCurve(20, marker, parametricSpiral _).drawWithFrame(Frame(id)) -// } -// } - -@JSExportTopLevel("Example2") -object Rectangle2 { - val rect = Picture.rectangle(50, 10).fillColor(Color.blue) - val rect2 = Picture.rectangle(50, 10).fillColor(Color.red) - - val joint = rect.below(rect2) - - // val immediate = (gc: GraphicsContext) => { - // gc.setColor(Color.red) - // gc.fillRect(0, 0, 50, 10) - // gc.setColor(Color.blue) - // gc.fillRect(0, 10, 50, 10) - // } - - @JSExport - def draw(mount: String) = - joint.drawWithFrame(Frame(mount)) -} From 0deaf63765cd0bb9f89f15ee11782cd879dff8e4 Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Sun, 23 Jun 2024 14:05:44 +0530 Subject: [PATCH 03/43] Edited name of the file --- docs/src/pages/canvas/examples.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/pages/canvas/examples.md b/docs/src/pages/canvas/examples.md index 9ee02dcd..55b3df82 100644 --- a/docs/src/pages/canvas/examples.md +++ b/docs/src/pages/canvas/examples.md @@ -5,7 +5,7 @@ The source for these examples is [in the repository](https://github.com/creative ## Concentric Circles -@:doodle("Example1", "Example1.draw") +@:doodle("concentric-circles", "CanvasConcentricCircles.draw") ## Parametric Spiral From ed749d31f9f8aaf073a6586d8788956a41e7ee76 Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Mon, 24 Jun 2024 00:00:28 +0530 Subject: [PATCH 04/43] Implemeting dummy --- build.sbt | 4 +- .../main/scala/doodle/language/Basic.scala | 1 + .../examples/canvas/ConcentricCircles.scala | 16 ++++---- .../src/main/scala/doodle/image/Image.scala | 6 +++ .../scala/doodle/java2d/algebra/Algebra.scala | 2 + .../java2d/algebra/reified/Reified.scala | 11 +++++ .../algebra/reified/ReifiedRaster.scala | 41 +++++++++++++++++++ 7 files changed, 70 insertions(+), 11 deletions(-) create mode 100644 java2d/src/main/scala/doodle/java2d/algebra/reified/ReifiedRaster.scala diff --git a/build.sbt b/build.sbt index 4f521a7d..28c75b2d 100644 --- a/build.sbt +++ b/build.sbt @@ -152,9 +152,9 @@ lazy val docs = val dest1 = mdocOut.value val dest2 = (laikaSite / target).value val cmd1 = - s"npx tailwindcss -i ${src.toString}/creative-scala.css -o ${dest1.toString}/creative-scala.css" + s"cmd c/ npx tailwindcss -i ${src.toString}/creative-scala.css -o ${dest1.toString}/creative-scala.css" val cmd2 = - s"npx tailwindcss -i ${src.toString}/creative-scala.css -o ${dest2.toString}/creative-scala.css" + s"cmd c/ npx tailwindcss -i ${src.toString}/creative-scala.css -o ${dest2.toString}/creative-scala.css" cmd1 ! cmd2 ! diff --git a/core/shared/src/main/scala/doodle/language/Basic.scala b/core/shared/src/main/scala/doodle/language/Basic.scala index d959e532..43ce73ef 100644 --- a/core/shared/src/main/scala/doodle/language/Basic.scala +++ b/core/shared/src/main/scala/doodle/language/Basic.scala @@ -24,6 +24,7 @@ trait Basic with Debug with Layout with Path + with Raster with Shape with Size with Style diff --git a/examples/js/src/main/scala/doodle/examples/canvas/ConcentricCircles.scala b/examples/js/src/main/scala/doodle/examples/canvas/ConcentricCircles.scala index a4727eff..34a37098 100644 --- a/examples/js/src/main/scala/doodle/examples/canvas/ConcentricCircles.scala +++ b/examples/js/src/main/scala/doodle/examples/canvas/ConcentricCircles.scala @@ -20,21 +20,19 @@ import cats.effect.unsafe.implicits.global import doodle.canvas.{*, given} import doodle.core.* import doodle.syntax.all.* +import doodle.image.* import scala.scalajs.js.annotation.* @JSExportTopLevel("CanvasConcentricCircles") object ConcentricCircles { - def circles(count: Int): Picture[Unit] = - if count == 0 then - Picture.circle(20).fillColor(Color.hsl(0.degrees, 0.7, 0.6)) - else - Picture - .circle(count.toDouble * 20.0) - .fillColor(Color.hsl((count * 15).degrees, 0.7, 0.6)) - .under(circles(count - 1)) + + def rect1 = Image.rectangle(100, 100).fillColor(Color.red) + def rect2 = Image.rectangle(100, 100).fillColor(Color.blue) + + val joint = rect1.beside(rect2).raster(10,20) @JSExport def draw(mount: String) = - circles(7).drawWithFrame(Frame(mount)) + joint.drawWithFrame(Frame(mount)) } \ No newline at end of file diff --git a/image/shared/src/main/scala/doodle/image/Image.scala b/image/shared/src/main/scala/doodle/image/Image.scala index baf963a4..fdb69829 100644 --- a/image/shared/src/main/scala/doodle/image/Image.scala +++ b/image/shared/src/main/scala/doodle/image/Image.scala @@ -178,6 +178,7 @@ object Image { final case class ClosedPath(path: doodle.core.ClosedPath) extends Image final case class Text(get: String) extends Image final case class Circle(d: Double) extends Image + final case class Raster[A](w: Int, h: Int, f: A => Unit) extends Image final case class Rectangle(w: Double, h: Double) extends Image final case class Triangle(w: Double, h: Double) extends Image // final case class Draw(w: Double, h: Double, f: Canvas => Unit) extends Image @@ -240,6 +241,9 @@ object Image { def pie(diameter: Double, angle: Angle): Image = path(doodle.core.ClosedPath.pie(0.0, 0.0, diameter, angle)) + def raster[A](width: Int, height: Int)(f: A => Unit): Image = + Raster(width, height, f) + def rectangle(width: Double, height: Double): Image = Rectangle(width, height) @@ -311,6 +315,8 @@ object Image { case Circle(d) => algebra.circle(d) + case Raster(w, h, f) => + algebra.raster(w, h)(f) case Rectangle(w, h) => algebra.rectangle(w, h) case Triangle(w, h) => diff --git a/java2d/src/main/scala/doodle/java2d/algebra/Algebra.scala b/java2d/src/main/scala/doodle/java2d/algebra/Algebra.scala index 0f4091e5..67afd446 100644 --- a/java2d/src/main/scala/doodle/java2d/algebra/Algebra.scala +++ b/java2d/src/main/scala/doodle/java2d/algebra/Algebra.scala @@ -37,10 +37,12 @@ final case class Algebra( with Java2dFromBase64 with ReifiedBitmap with ReifiedPath + //with ReifiedRaster with ReifiedShape with ReifiedText with GenericDebug[Reification] with GenericLayout[Reification] + with GenericRaster[Reification] with GenericSize[Reification] with GenericStyle[Reification] with GenericTransform[Reification] diff --git a/java2d/src/main/scala/doodle/java2d/algebra/reified/Reified.scala b/java2d/src/main/scala/doodle/java2d/algebra/reified/Reified.scala index 7103f48b..d4a9d179 100644 --- a/java2d/src/main/scala/doodle/java2d/algebra/reified/Reified.scala +++ b/java2d/src/main/scala/doodle/java2d/algebra/reified/Reified.scala @@ -99,6 +99,9 @@ sealed abstract class Reified extends Product with Serializable { case Text(tx, _, stroke, text, font, bounds) => ctx.text(gc)(tx.andThen(finalTransform), stroke, text, font, bounds) + + // case Raster[A](width, height)(f: A => Unit) => + // ctx.raster(gc)(width, height)(f) } } object Reified { @@ -173,6 +176,11 @@ object Reified { bounds: Rectangle2D ) extends Reified + // final case class Raster[A]( + // width: Int, + // height: Int) + // (f: A => Unit) extends Reified + def fillRect( transform: Tx, fill: Fill, @@ -239,4 +247,7 @@ object Reified { bounds: Rectangle2D ): Reified = Text(transform, fill, stroke, text, font, bounds) + + // def raster[A](width: Int, height: Int)(f: A => Unit): Reified = + // Raster(width, height)(f) } diff --git a/java2d/src/main/scala/doodle/java2d/algebra/reified/ReifiedRaster.scala b/java2d/src/main/scala/doodle/java2d/algebra/reified/ReifiedRaster.scala new file mode 100644 index 00000000..e546e85f --- /dev/null +++ b/java2d/src/main/scala/doodle/java2d/algebra/reified/ReifiedRaster.scala @@ -0,0 +1,41 @@ +// /* +// * 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 java2d +// package algebra +// package reified + +// import cats.data.WriterT +// import doodle.algebra.generic.* +// import doodle.core.* + +// trait ReifiedRaster extends GenericRaster[Reification] { +// self: Algebra { type Drawing[A] <: doodle.java2d.Drawing[A] } => + +// object RasterApi extends RasterApi { +// def raster[A](width: Int, height: Int)(f: A => Unit): Reification[Unit] = +// WriterT.tell( +// List( +// Reified +// .raster(width, height)(f) +// ) +// ) +// } + +// def raster[A](width: Int, height: Int)(f: A => Unit): Reification[Unit] = +// RasterApi.raster(width, height)(f) +// } From 6a95f8d7f32cde2c38f01638b60af9e84bfbf76b Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Tue, 25 Jun 2024 16:53:51 +0530 Subject: [PATCH 05/43] Removed junk code --- .../java2d/algebra/reified/Reified.scala | 253 ------------------ .../algebra/reified/ReifiedRaster.scala | 41 --- 2 files changed, 294 deletions(-) delete mode 100644 java2d/src/main/scala/doodle/java2d/algebra/reified/Reified.scala delete mode 100644 java2d/src/main/scala/doodle/java2d/algebra/reified/ReifiedRaster.scala diff --git a/java2d/src/main/scala/doodle/java2d/algebra/reified/Reified.scala b/java2d/src/main/scala/doodle/java2d/algebra/reified/Reified.scala deleted file mode 100644 index d4a9d179..00000000 --- a/java2d/src/main/scala/doodle/java2d/algebra/reified/Reified.scala +++ /dev/null @@ -1,253 +0,0 @@ -/* - * 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 java2d -package algebra -package reified - -import doodle.algebra.generic.Fill -import doodle.algebra.generic.Stroke -import doodle.core.PathElement -import doodle.core.Point -import doodle.core.font.Font -import doodle.core.{Transform as Tx} - -import java.awt.geom.Rectangle2D -import java.awt.image.BufferedImage - -/** Each element of `Reified` is an instruction to draw something on the screen - * or to otherwise alter the state the graphics context. - * - * Each instruction should be atomic: there should be *no* nesting of - * instructions inside instructions. In compiler terms, this is a "linear IR", - * not a "tree IR". - * - * When defining a `Picture`, there are many operations that apply to some - * group of elements. For example, rotating a `Picture` or setting a stroke - * color applies to all the elements within the `Picture` on which the method - * is called. This has a natural representation as a tree, but we need to use a - * different strategy to represent it as a linear list of instructions. There - * are two implementation approaches: - * - * - Push all these operations into the atomic instructions. This is the - * approach currently taken, with each element containing the transform, - * and fill or stroke as appropriate. The advantage of this approach is - * that each reified instruction is independent of any other. The - * disadvantage is that this doesn't scale as the amount of context grows, - * as each instruction needs to have additional fields added. - * - * - Have stateful operations to add and remove some context. For example, - * push and pop a transform or stroke color. This is the approach taken in - * the `Graphics2D` Java API. For example, calling the `transform` method - * on Graphics2D adds the transform to the already existing transforms. - * (However, the API lacks methods to undo these operations, which makes it - * a bit limited.) - */ -sealed abstract class Reified extends Product with Serializable { - import Reified.* - - def transform: Tx - - /** finalTransform gives an transform applied after any other reified - * transform. Usually this is a transform from logical to screen coordinates. - */ - def render[A](gc: A, finalTransform: Tx)(implicit - ctx: GraphicsContext[A] - ): Unit = - this match { - case FillOpenPath(tx, fill, elements) => - ctx.fillOpenPath(gc)(tx.andThen(finalTransform), fill, elements) - case StrokeOpenPath(tx, stroke, elements) => - ctx.strokeOpenPath(gc)(tx.andThen(finalTransform), stroke, elements) - - case FillClosedPath(tx, fill, elements) => - ctx.fillClosedPath(gc)(tx.andThen(finalTransform), fill, elements) - case StrokeClosedPath(tx, stroke, elements) => - ctx.strokeClosedPath(gc)(tx.andThen(finalTransform), stroke, elements) - - case FillCircle(tx, fill, diameter) => - ctx.fillCircle(gc)(tx.andThen(finalTransform), fill, diameter) - case StrokeCircle(tx, stroke, diameter) => - ctx.strokeCircle(gc)(tx.andThen(finalTransform), stroke, diameter) - - case FillRect(tx, fill, width, height) => - ctx.fillRect(gc)(tx.andThen(finalTransform), fill, width, height) - case StrokeRect(tx, stroke, width, height) => - ctx.strokeRect(gc)(tx.andThen(finalTransform), stroke, width, height) - - case FillPolygon(tx, fill, points) => - ctx.fillPolygon(gc)(tx.andThen(finalTransform), fill, points) - case StrokePolygon(tx, stroke, points) => - ctx.strokePolygon(gc)(tx.andThen(finalTransform), stroke, points) - - case Bitmap(tx, image) => - ctx.bitmap(gc)(tx.andThen(finalTransform), image) - - case Text(tx, _, stroke, text, font, bounds) => - ctx.text(gc)(tx.andThen(finalTransform), stroke, text, font, bounds) - - // case Raster[A](width, height)(f: A => Unit) => - // ctx.raster(gc)(width, height)(f) - } -} -object Reified { - import PathElement.* - - def transform(tx: Tx, elements: List[PathElement]): List[PathElement] = - elements.map { - case MoveTo(to) => MoveTo(tx(to)) - case LineTo(to) => LineTo(tx(to)) - case BezierCurveTo(cp1, cp2, to) => - BezierCurveTo(tx(cp1), tx(cp2), tx(to)) - } - def transform(tx: Tx, points: Array[Point]): Array[Point] = - points.map { pt => - tx(pt) - } - - final case class FillRect( - transform: Tx, - fill: Fill, - width: Double, - height: Double - ) extends Reified - final case class StrokeRect( - transform: Tx, - stroke: Stroke, - width: Double, - height: Double - ) extends Reified - - final case class FillCircle(transform: Tx, fill: Fill, diameter: Double) - extends Reified - final case class StrokeCircle(transform: Tx, stroke: Stroke, diameter: Double) - extends Reified - - final case class FillPolygon(transform: Tx, fill: Fill, points: Array[Point]) - extends Reified - final case class StrokePolygon( - transform: Tx, - stroke: Stroke, - points: Array[Point] - ) extends Reified - - final case class FillClosedPath( - transform: Tx, - fill: Fill, - elements: List[PathElement] - ) extends Reified - final case class StrokeClosedPath( - transform: Tx, - stroke: Stroke, - elements: List[PathElement] - ) extends Reified - - final case class FillOpenPath( - transform: Tx, - fill: Fill, - elements: List[PathElement] - ) extends Reified - final case class StrokeOpenPath( - transform: Tx, - stroke: Stroke, - elements: List[PathElement] - ) extends Reified - final case class Bitmap(transform: Tx, image: BufferedImage) extends Reified - final case class Text( - transform: Tx, - fill: Option[Fill], - stroke: Option[Stroke], - text: String, - font: Font, - bounds: Rectangle2D - ) extends Reified - - // final case class Raster[A]( - // width: Int, - // height: Int) - // (f: A => Unit) extends Reified - - def fillRect( - transform: Tx, - fill: Fill, - width: Double, - height: Double - ): Reified = - FillRect(transform, fill, width, height) - def strokeRect( - transform: Tx, - stroke: Stroke, - width: Double, - height: Double - ): Reified = - StrokeRect(transform, stroke, width, height) - - def fillCircle(transform: Tx, fill: Fill, diameter: Double): Reified = - FillCircle(transform, fill, diameter) - def strokeCircle(transform: Tx, stroke: Stroke, diameter: Double): Reified = - StrokeCircle(transform, stroke, diameter) - - def fillPolygon(transform: Tx, fill: Fill, points: Array[Point]): Reified = - FillPolygon(transform, fill, points) - def strokePolygon( - transform: Tx, - stroke: Stroke, - points: Array[Point] - ): Reified = - StrokePolygon(transform, stroke, points) - - def fillClosedPath( - transform: Tx, - fill: Fill, - elements: List[PathElement] - ): Reified = - FillClosedPath(transform, fill, elements) - def strokeClosedPath( - transform: Tx, - stroke: Stroke, - elements: List[PathElement] - ): Reified = - StrokeClosedPath(transform, stroke, elements) - - def fillOpenPath( - transform: Tx, - fill: Fill, - elements: List[PathElement] - ): Reified = - FillOpenPath(transform, fill, elements) - def strokeOpenPath( - transform: Tx, - stroke: Stroke, - elements: List[PathElement] - ): Reified = - StrokeOpenPath(transform, stroke, elements) - def bitmap(transform: Tx, image: BufferedImage): Reified = - Bitmap(transform, image) - - def text( - transform: Tx, - fill: Option[Fill], - stroke: Option[Stroke], - text: String, - font: Font, - bounds: Rectangle2D - ): Reified = - Text(transform, fill, stroke, text, font, bounds) - - // def raster[A](width: Int, height: Int)(f: A => Unit): Reified = - // Raster(width, height)(f) -} diff --git a/java2d/src/main/scala/doodle/java2d/algebra/reified/ReifiedRaster.scala b/java2d/src/main/scala/doodle/java2d/algebra/reified/ReifiedRaster.scala deleted file mode 100644 index e546e85f..00000000 --- a/java2d/src/main/scala/doodle/java2d/algebra/reified/ReifiedRaster.scala +++ /dev/null @@ -1,41 +0,0 @@ -// /* -// * 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 java2d -// package algebra -// package reified - -// import cats.data.WriterT -// import doodle.algebra.generic.* -// import doodle.core.* - -// trait ReifiedRaster extends GenericRaster[Reification] { -// self: Algebra { type Drawing[A] <: doodle.java2d.Drawing[A] } => - -// object RasterApi extends RasterApi { -// def raster[A](width: Int, height: Int)(f: A => Unit): Reification[Unit] = -// WriterT.tell( -// List( -// Reified -// .raster(width, height)(f) -// ) -// ) -// } - -// def raster[A](width: Int, height: Int)(f: A => Unit): Reification[Unit] = -// RasterApi.raster(width, height)(f) -// } From 739b907798a503060d038e5d62703f2198c9d2cf Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Wed, 26 Jun 2024 00:06:03 +0530 Subject: [PATCH 06/43] Fixing Reified file --- .../java2d/algebra/reified/Reified.scala | 242 ++++++++++++++++++ 1 file changed, 242 insertions(+) create mode 100644 java2d/src/main/scala/doodle/java2d/algebra/reified/Reified.scala diff --git a/java2d/src/main/scala/doodle/java2d/algebra/reified/Reified.scala b/java2d/src/main/scala/doodle/java2d/algebra/reified/Reified.scala new file mode 100644 index 00000000..7103f48b --- /dev/null +++ b/java2d/src/main/scala/doodle/java2d/algebra/reified/Reified.scala @@ -0,0 +1,242 @@ +/* + * 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 java2d +package algebra +package reified + +import doodle.algebra.generic.Fill +import doodle.algebra.generic.Stroke +import doodle.core.PathElement +import doodle.core.Point +import doodle.core.font.Font +import doodle.core.{Transform as Tx} + +import java.awt.geom.Rectangle2D +import java.awt.image.BufferedImage + +/** Each element of `Reified` is an instruction to draw something on the screen + * or to otherwise alter the state the graphics context. + * + * Each instruction should be atomic: there should be *no* nesting of + * instructions inside instructions. In compiler terms, this is a "linear IR", + * not a "tree IR". + * + * When defining a `Picture`, there are many operations that apply to some + * group of elements. For example, rotating a `Picture` or setting a stroke + * color applies to all the elements within the `Picture` on which the method + * is called. This has a natural representation as a tree, but we need to use a + * different strategy to represent it as a linear list of instructions. There + * are two implementation approaches: + * + * - Push all these operations into the atomic instructions. This is the + * approach currently taken, with each element containing the transform, + * and fill or stroke as appropriate. The advantage of this approach is + * that each reified instruction is independent of any other. The + * disadvantage is that this doesn't scale as the amount of context grows, + * as each instruction needs to have additional fields added. + * + * - Have stateful operations to add and remove some context. For example, + * push and pop a transform or stroke color. This is the approach taken in + * the `Graphics2D` Java API. For example, calling the `transform` method + * on Graphics2D adds the transform to the already existing transforms. + * (However, the API lacks methods to undo these operations, which makes it + * a bit limited.) + */ +sealed abstract class Reified extends Product with Serializable { + import Reified.* + + def transform: Tx + + /** finalTransform gives an transform applied after any other reified + * transform. Usually this is a transform from logical to screen coordinates. + */ + def render[A](gc: A, finalTransform: Tx)(implicit + ctx: GraphicsContext[A] + ): Unit = + this match { + case FillOpenPath(tx, fill, elements) => + ctx.fillOpenPath(gc)(tx.andThen(finalTransform), fill, elements) + case StrokeOpenPath(tx, stroke, elements) => + ctx.strokeOpenPath(gc)(tx.andThen(finalTransform), stroke, elements) + + case FillClosedPath(tx, fill, elements) => + ctx.fillClosedPath(gc)(tx.andThen(finalTransform), fill, elements) + case StrokeClosedPath(tx, stroke, elements) => + ctx.strokeClosedPath(gc)(tx.andThen(finalTransform), stroke, elements) + + case FillCircle(tx, fill, diameter) => + ctx.fillCircle(gc)(tx.andThen(finalTransform), fill, diameter) + case StrokeCircle(tx, stroke, diameter) => + ctx.strokeCircle(gc)(tx.andThen(finalTransform), stroke, diameter) + + case FillRect(tx, fill, width, height) => + ctx.fillRect(gc)(tx.andThen(finalTransform), fill, width, height) + case StrokeRect(tx, stroke, width, height) => + ctx.strokeRect(gc)(tx.andThen(finalTransform), stroke, width, height) + + case FillPolygon(tx, fill, points) => + ctx.fillPolygon(gc)(tx.andThen(finalTransform), fill, points) + case StrokePolygon(tx, stroke, points) => + ctx.strokePolygon(gc)(tx.andThen(finalTransform), stroke, points) + + case Bitmap(tx, image) => + ctx.bitmap(gc)(tx.andThen(finalTransform), image) + + case Text(tx, _, stroke, text, font, bounds) => + ctx.text(gc)(tx.andThen(finalTransform), stroke, text, font, bounds) + } +} +object Reified { + import PathElement.* + + def transform(tx: Tx, elements: List[PathElement]): List[PathElement] = + elements.map { + case MoveTo(to) => MoveTo(tx(to)) + case LineTo(to) => LineTo(tx(to)) + case BezierCurveTo(cp1, cp2, to) => + BezierCurveTo(tx(cp1), tx(cp2), tx(to)) + } + def transform(tx: Tx, points: Array[Point]): Array[Point] = + points.map { pt => + tx(pt) + } + + final case class FillRect( + transform: Tx, + fill: Fill, + width: Double, + height: Double + ) extends Reified + final case class StrokeRect( + transform: Tx, + stroke: Stroke, + width: Double, + height: Double + ) extends Reified + + final case class FillCircle(transform: Tx, fill: Fill, diameter: Double) + extends Reified + final case class StrokeCircle(transform: Tx, stroke: Stroke, diameter: Double) + extends Reified + + final case class FillPolygon(transform: Tx, fill: Fill, points: Array[Point]) + extends Reified + final case class StrokePolygon( + transform: Tx, + stroke: Stroke, + points: Array[Point] + ) extends Reified + + final case class FillClosedPath( + transform: Tx, + fill: Fill, + elements: List[PathElement] + ) extends Reified + final case class StrokeClosedPath( + transform: Tx, + stroke: Stroke, + elements: List[PathElement] + ) extends Reified + + final case class FillOpenPath( + transform: Tx, + fill: Fill, + elements: List[PathElement] + ) extends Reified + final case class StrokeOpenPath( + transform: Tx, + stroke: Stroke, + elements: List[PathElement] + ) extends Reified + final case class Bitmap(transform: Tx, image: BufferedImage) extends Reified + final case class Text( + transform: Tx, + fill: Option[Fill], + stroke: Option[Stroke], + text: String, + font: Font, + bounds: Rectangle2D + ) extends Reified + + def fillRect( + transform: Tx, + fill: Fill, + width: Double, + height: Double + ): Reified = + FillRect(transform, fill, width, height) + def strokeRect( + transform: Tx, + stroke: Stroke, + width: Double, + height: Double + ): Reified = + StrokeRect(transform, stroke, width, height) + + def fillCircle(transform: Tx, fill: Fill, diameter: Double): Reified = + FillCircle(transform, fill, diameter) + def strokeCircle(transform: Tx, stroke: Stroke, diameter: Double): Reified = + StrokeCircle(transform, stroke, diameter) + + def fillPolygon(transform: Tx, fill: Fill, points: Array[Point]): Reified = + FillPolygon(transform, fill, points) + def strokePolygon( + transform: Tx, + stroke: Stroke, + points: Array[Point] + ): Reified = + StrokePolygon(transform, stroke, points) + + def fillClosedPath( + transform: Tx, + fill: Fill, + elements: List[PathElement] + ): Reified = + FillClosedPath(transform, fill, elements) + def strokeClosedPath( + transform: Tx, + stroke: Stroke, + elements: List[PathElement] + ): Reified = + StrokeClosedPath(transform, stroke, elements) + + def fillOpenPath( + transform: Tx, + fill: Fill, + elements: List[PathElement] + ): Reified = + FillOpenPath(transform, fill, elements) + def strokeOpenPath( + transform: Tx, + stroke: Stroke, + elements: List[PathElement] + ): Reified = + StrokeOpenPath(transform, stroke, elements) + def bitmap(transform: Tx, image: BufferedImage): Reified = + Bitmap(transform, image) + + def text( + transform: Tx, + fill: Option[Fill], + stroke: Option[Stroke], + text: String, + font: Font, + bounds: Rectangle2D + ): Reified = + Text(transform, fill, stroke, text, font, bounds) +} From c52c5b69d7332471a72b43fe2b5991c195de1f68 Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Wed, 26 Jun 2024 00:15:31 +0530 Subject: [PATCH 07/43] Removing junk files --- build.sbt | 4 ++-- core/jvm/src/main/scala/doodle/syntax/package.scala | 2 -- java2d/src/main/scala/doodle/java2d/algebra/Algebra.scala | 2 -- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/build.sbt b/build.sbt index 28c75b2d..4f521a7d 100644 --- a/build.sbt +++ b/build.sbt @@ -152,9 +152,9 @@ lazy val docs = val dest1 = mdocOut.value val dest2 = (laikaSite / target).value val cmd1 = - s"cmd c/ npx tailwindcss -i ${src.toString}/creative-scala.css -o ${dest1.toString}/creative-scala.css" + s"npx tailwindcss -i ${src.toString}/creative-scala.css -o ${dest1.toString}/creative-scala.css" val cmd2 = - s"cmd c/ npx tailwindcss -i ${src.toString}/creative-scala.css -o ${dest2.toString}/creative-scala.css" + s"npx tailwindcss -i ${src.toString}/creative-scala.css -o ${dest2.toString}/creative-scala.css" cmd1 ! cmd2 ! diff --git a/core/jvm/src/main/scala/doodle/syntax/package.scala b/core/jvm/src/main/scala/doodle/syntax/package.scala index ee8b89ee..bdb53859 100644 --- a/core/jvm/src/main/scala/doodle/syntax/package.scala +++ b/core/jvm/src/main/scala/doodle/syntax/package.scala @@ -27,7 +27,6 @@ package object syntax { with LayoutSyntax with NormalizedSyntax with PathSyntax - with RasterSyntax with RendererSyntax with ShapeSyntax with SizeSyntax @@ -47,7 +46,6 @@ package object syntax { object layout extends LayoutSyntax object normalized extends NormalizedSyntax object path extends PathSyntax - object raster extends RasterSyntax object renderer extends RendererSyntax object shape extends ShapeSyntax object size extends SizeSyntax diff --git a/java2d/src/main/scala/doodle/java2d/algebra/Algebra.scala b/java2d/src/main/scala/doodle/java2d/algebra/Algebra.scala index 67afd446..0f4091e5 100644 --- a/java2d/src/main/scala/doodle/java2d/algebra/Algebra.scala +++ b/java2d/src/main/scala/doodle/java2d/algebra/Algebra.scala @@ -37,12 +37,10 @@ final case class Algebra( with Java2dFromBase64 with ReifiedBitmap with ReifiedPath - //with ReifiedRaster with ReifiedShape with ReifiedText with GenericDebug[Reification] with GenericLayout[Reification] - with GenericRaster[Reification] with GenericSize[Reification] with GenericStyle[Reification] with GenericTransform[Reification] From 5418467899bf675d2d004c972f1671abdd22e384 Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Wed, 26 Jun 2024 00:21:50 +0530 Subject: [PATCH 08/43] Removing junk files --- core/shared/src/main/scala/doodle/language/Basic.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/core/shared/src/main/scala/doodle/language/Basic.scala b/core/shared/src/main/scala/doodle/language/Basic.scala index 43ce73ef..d959e532 100644 --- a/core/shared/src/main/scala/doodle/language/Basic.scala +++ b/core/shared/src/main/scala/doodle/language/Basic.scala @@ -24,7 +24,6 @@ trait Basic with Debug with Layout with Path - with Raster with Shape with Size with Style From 9e05dcfd9c38e0b48dbd4c3f9b0b5a5f8d1886bb Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Wed, 26 Jun 2024 15:54:12 +0530 Subject: [PATCH 09/43] Changed type parameter to raster[A] to trait[A] --- .../doodle/canvas/algebra/CanvasAlgebra.scala | 2 +- .../scala/doodle/canvas/algebra/Raster.scala | 12 ++++----- .../main/scala/doodle/algebra/Raster.scala | 12 +++++---- .../algebra/generic/GenericRaster.scala | 25 ++++--------------- .../scala/doodle/syntax/RasterSyntax.scala | 4 +-- .../src/main/scala/doodle/image/Image.scala | 12 ++++----- 6 files changed, 27 insertions(+), 40 deletions(-) diff --git a/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala b/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala index 3e195dcf..337e1b3b 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala @@ -33,7 +33,7 @@ final case class CanvasAlgebra( Shape, GenericDebug[CanvasDrawing], GenericLayout[CanvasDrawing], - GenericRaster[CanvasDrawing], + GenericRaster[CanvasDrawing, Unit], GenericSize[CanvasDrawing], GenericStyle[CanvasDrawing], GenericTransform[CanvasDrawing], diff --git a/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala b/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala index 8b66bad1..63f580b0 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala @@ -17,15 +17,15 @@ package doodle.canvas.algebra import doodle.algebra.Algebra -import doodle.algebra.generic.* +import doodle.algebra.generic._ + +trait Raster extends GenericRaster[CanvasDrawing, Unit] { + self: Algebra { type Drawing[U] = Finalized[CanvasDrawing, U] } => -trait Raster extends GenericRaster[CanvasDrawing] { - self: Algebra { type Drawing[Unit] = Finalized[CanvasDrawing, Unit] } => object RasterApi extends RasterApi { - def raster[A](width: Int, height: Int)( - f: A => Unit - ): CanvasDrawing[Unit] = { + def raster(width: Int, height: Int)(f: Unit => Unit): CanvasDrawing[Unit] = { CanvasDrawing.raster(width, height)(f) } } } + diff --git a/core/shared/src/main/scala/doodle/algebra/Raster.scala b/core/shared/src/main/scala/doodle/algebra/Raster.scala index 6549de38..f07ab24c 100644 --- a/core/shared/src/main/scala/doodle/algebra/Raster.scala +++ b/core/shared/src/main/scala/doodle/algebra/Raster.scala @@ -17,16 +17,18 @@ package doodle package algebra -trait Raster extends Algebra { - def raster[A](width: Int, height: Int)(f: A => Unit): Drawing[Unit] +trait Raster[A] extends Algebra { + def raster(width: Int, height: Int)(f: A => Unit): Drawing[Unit] } -trait RasterConstructor { - self: BaseConstructor { type Algebra <: Raster } => +trait RasterConstructor[A] { + self: BaseConstructor { type Algebra <: Raster[A] } => - def raster[A](width: Int, height: Int)(f: A => Unit): Picture[Unit] = + def raster(width: Int, height: Int)(f: A => Unit): Picture[Unit] = new Picture[Unit] { def apply(implicit algebra: Algebra): algebra.Drawing[Unit] = algebra.raster(width, height)(f) } } + + diff --git a/core/shared/src/main/scala/doodle/algebra/generic/GenericRaster.scala b/core/shared/src/main/scala/doodle/algebra/generic/GenericRaster.scala index 56557dc9..f488ae8e 100644 --- a/core/shared/src/main/scala/doodle/algebra/generic/GenericRaster.scala +++ b/core/shared/src/main/scala/doodle/algebra/generic/GenericRaster.scala @@ -1,19 +1,3 @@ -/* - * 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 algebra package generic @@ -21,15 +5,16 @@ package generic import cats.data.State import doodle.core.BoundingBox -trait GenericRaster[G[_]] extends Raster { - self: Algebra { type Drawing[Unit] = Finalized[G, Unit] } => +trait GenericRaster[G[_], A] extends Raster[A] { + self: Algebra { type Drawing[U] = Finalized[G, U] } => + trait RasterApi { - def raster[A](width: Int, height: Int)(f: A => Unit): G[Unit] + def raster(width: Int, height: Int)(f: A => Unit): G[Unit] } def RasterApi: RasterApi - def raster[A](width: Int, height: Int)(f: A => Unit): Finalized[G, Unit] = { + def raster(width: Int, height: Int)(f: A => Unit): Finalized[G, Unit] = { Finalized.leaf { dc => val bb = BoundingBox.centered(width, height) ( diff --git a/core/shared/src/main/scala/doodle/syntax/RasterSyntax.scala b/core/shared/src/main/scala/doodle/syntax/RasterSyntax.scala index 8452b69f..101e1afa 100644 --- a/core/shared/src/main/scala/doodle/syntax/RasterSyntax.scala +++ b/core/shared/src/main/scala/doodle/syntax/RasterSyntax.scala @@ -5,7 +5,7 @@ import doodle.algebra.Picture import doodle.algebra.Raster trait RasterSyntax { - def raster[Alg <: Raster, A]( + def raster[Alg <: Raster[A], A]( width: Int, height: Int )(f: A => Unit): Picture[Alg, Unit] = @@ -13,4 +13,4 @@ trait RasterSyntax { def apply(implicit algebra: Alg): algebra.Drawing[Unit] = algebra.raster(width, height)(f) } -} \ No newline at end of file +} diff --git a/image/shared/src/main/scala/doodle/image/Image.scala b/image/shared/src/main/scala/doodle/image/Image.scala index fdb69829..cb0f1dee 100644 --- a/image/shared/src/main/scala/doodle/image/Image.scala +++ b/image/shared/src/main/scala/doodle/image/Image.scala @@ -112,6 +112,9 @@ sealed abstract class Image extends Product with Serializable { def transform(tx: core.Transform): Image = Transform(tx, this) + def raster[A](width: Int, height: Int)(f: A => Unit): Image = + Raster(width, height, f) + def rotate(angle: Angle): Image = this.transform(core.Transform.rotate(angle)) @@ -239,10 +242,7 @@ object Image { Circle(diameter) def pie(diameter: Double, angle: Angle): Image = - path(doodle.core.ClosedPath.pie(0.0, 0.0, diameter, angle)) - - def raster[A](width: Int, height: Int)(f: A => Unit): Image = - Raster(width, height, f) + path(doodle.core.ClosedPath.pie(0.0, 0.0, diameter, angle)) def rectangle(width: Double, height: Double): Image = Rectangle(width, height) @@ -315,8 +315,8 @@ object Image { case Circle(d) => algebra.circle(d) - case Raster(w, h, f) => - algebra.raster(w, h)(f) + // case Raster(w, h, f) => + // algebra.raster(w, h)(f) case Rectangle(w, h) => algebra.rectangle(w, h) case Triangle(w, h) => From b22fabdf3cf13913563d7b6855374dd412681338 Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Wed, 26 Jun 2024 16:40:43 +0530 Subject: [PATCH 10/43] Changed type parameter to raster[A] to trait[A] --- .../scala/doodle/examples/canvas/ConcentricCircles.scala | 3 ++- image/shared/src/main/scala/doodle/image/Image.scala | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/examples/js/src/main/scala/doodle/examples/canvas/ConcentricCircles.scala b/examples/js/src/main/scala/doodle/examples/canvas/ConcentricCircles.scala index 34a37098..29b7da11 100644 --- a/examples/js/src/main/scala/doodle/examples/canvas/ConcentricCircles.scala +++ b/examples/js/src/main/scala/doodle/examples/canvas/ConcentricCircles.scala @@ -30,7 +30,8 @@ object ConcentricCircles { def rect1 = Image.rectangle(100, 100).fillColor(Color.red) def rect2 = Image.rectangle(100, 100).fillColor(Color.blue) - val joint = rect1.beside(rect2).raster(10,20) + val joint = rect1.beside(rect2) + //.raster(10,20) @JSExport def draw(mount: String) = diff --git a/image/shared/src/main/scala/doodle/image/Image.scala b/image/shared/src/main/scala/doodle/image/Image.scala index cb0f1dee..50fdd396 100644 --- a/image/shared/src/main/scala/doodle/image/Image.scala +++ b/image/shared/src/main/scala/doodle/image/Image.scala @@ -112,8 +112,8 @@ sealed abstract class Image extends Product with Serializable { def transform(tx: core.Transform): Image = Transform(tx, this) - def raster[A](width: Int, height: Int)(f: A => Unit): Image = - Raster(width, height, f) + def raster(width: Int, height: Int): Image = + Raster(width, height) def rotate(angle: Angle): Image = this.transform(core.Transform.rotate(angle)) @@ -181,7 +181,7 @@ object Image { final case class ClosedPath(path: doodle.core.ClosedPath) extends Image final case class Text(get: String) extends Image final case class Circle(d: Double) extends Image - final case class Raster[A](w: Int, h: Int, f: A => Unit) extends Image + final case class Raster(w: Int, h: Int) extends Image final case class Rectangle(w: Double, h: Double) extends Image final case class Triangle(w: Double, h: Double) extends Image // final case class Draw(w: Double, h: Double, f: Canvas => Unit) extends Image From d882c6effa66e11f9a1fe10b05611747ce799328 Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Wed, 26 Jun 2024 16:47:58 +0530 Subject: [PATCH 11/43] Removed junk file --- .../src/main/scala/doodle/algebra/generic/GenericShape.scala | 2 +- image/shared/src/main/scala/doodle/image/Image.scala | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/core/shared/src/main/scala/doodle/algebra/generic/GenericShape.scala b/core/shared/src/main/scala/doodle/algebra/generic/GenericShape.scala index 42bc2b74..ef2d6026 100644 --- a/core/shared/src/main/scala/doodle/algebra/generic/GenericShape.scala +++ b/core/shared/src/main/scala/doodle/algebra/generic/GenericShape.scala @@ -94,4 +94,4 @@ trait GenericShape[G[_]] extends Shape { Finalized.leaf { _ => (BoundingBox.empty, Renderable.unit(ShapeApi.unit)) } -} \ No newline at end of file + } \ No newline at end of file diff --git a/image/shared/src/main/scala/doodle/image/Image.scala b/image/shared/src/main/scala/doodle/image/Image.scala index 50fdd396..88975b9e 100644 --- a/image/shared/src/main/scala/doodle/image/Image.scala +++ b/image/shared/src/main/scala/doodle/image/Image.scala @@ -315,8 +315,6 @@ object Image { case Circle(d) => algebra.circle(d) - // case Raster(w, h, f) => - // algebra.raster(w, h)(f) case Rectangle(w, h) => algebra.rectangle(w, h) case Triangle(w, h) => From d9c5745765887efe6e72aeedd34807912d182c42 Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Thu, 27 Jun 2024 12:15:39 +0530 Subject: [PATCH 12/43] Removed Raster from Image and added an experiment example --- .../doodle/canvas/algebra/CanvasAlgebra.scala | 2 +- .../doodle/canvas/algebra/CanvasDrawing.scala | 2 +- .../scala/doodle/canvas/algebra/Raster.scala | 6 ++-- docs/src/pages/svg/README.md | 3 ++ .../examples/canvas/ConcentricCircles.scala | 17 ++++----- .../doodle/examples/canvas/Experiment.scala | 36 +++++++++++++++++++ .../src/main/scala/doodle/image/Image.scala | 3 -- 7 files changed, 54 insertions(+), 15 deletions(-) create mode 100644 examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala diff --git a/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala b/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala index 337e1b3b..79bfa315 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala @@ -33,7 +33,7 @@ final case class CanvasAlgebra( Shape, GenericDebug[CanvasDrawing], GenericLayout[CanvasDrawing], - GenericRaster[CanvasDrawing, Unit], + GenericRaster[CanvasDrawing, CanvasRenderingContext2D], GenericSize[CanvasDrawing], GenericStyle[CanvasDrawing], GenericTransform[CanvasDrawing], diff --git a/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala b/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala index a5cdcf8e..f5fd0ad9 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala @@ -121,7 +121,7 @@ object CanvasDrawing { } } - def raster[CanvasRenderingContext2D](width: Int, height: Int)( + def raster(width: Int, height: Int)( f: CanvasRenderingContext2D => Unit ): CanvasDrawing[Unit] = { CanvasDrawing.raster(width, height)(f) diff --git a/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala b/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala index 63f580b0..03cabf24 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala @@ -18,12 +18,14 @@ package doodle.canvas.algebra import doodle.algebra.Algebra import doodle.algebra.generic._ +import org.scalajs.dom.CanvasRenderingContext2D -trait Raster extends GenericRaster[CanvasDrawing, Unit] { + +trait Raster extends GenericRaster[CanvasDrawing, CanvasRenderingContext2D] { self: Algebra { type Drawing[U] = Finalized[CanvasDrawing, U] } => object RasterApi extends RasterApi { - def raster(width: Int, height: Int)(f: Unit => Unit): CanvasDrawing[Unit] = { + def raster(width: Int, height: Int)(f: CanvasRenderingContext2D => Unit): CanvasDrawing[Unit] = { CanvasDrawing.raster(width, height)(f) } } diff --git a/docs/src/pages/svg/README.md b/docs/src/pages/svg/README.md index f7272af8..019fb841 100644 --- a/docs/src/pages/svg/README.md +++ b/docs/src/pages/svg/README.md @@ -61,3 +61,6 @@ The source for these examples is [in the repository](https://github.com/creative ### Parametric Spiral @:doodle("parametric-spiral", "SvgParametricSpiral.draw") + +### Experimenting +@:doodle("Experiment", "Experiment.draw") diff --git a/examples/js/src/main/scala/doodle/examples/canvas/ConcentricCircles.scala b/examples/js/src/main/scala/doodle/examples/canvas/ConcentricCircles.scala index 29b7da11..a4727eff 100644 --- a/examples/js/src/main/scala/doodle/examples/canvas/ConcentricCircles.scala +++ b/examples/js/src/main/scala/doodle/examples/canvas/ConcentricCircles.scala @@ -20,20 +20,21 @@ import cats.effect.unsafe.implicits.global import doodle.canvas.{*, given} import doodle.core.* import doodle.syntax.all.* -import doodle.image.* import scala.scalajs.js.annotation.* @JSExportTopLevel("CanvasConcentricCircles") object ConcentricCircles { - - def rect1 = Image.rectangle(100, 100).fillColor(Color.red) - def rect2 = Image.rectangle(100, 100).fillColor(Color.blue) - - val joint = rect1.beside(rect2) - //.raster(10,20) + def circles(count: Int): Picture[Unit] = + if count == 0 then + Picture.circle(20).fillColor(Color.hsl(0.degrees, 0.7, 0.6)) + else + Picture + .circle(count.toDouble * 20.0) + .fillColor(Color.hsl((count * 15).degrees, 0.7, 0.6)) + .under(circles(count - 1)) @JSExport def draw(mount: String) = - joint.drawWithFrame(Frame(mount)) + circles(7).drawWithFrame(Frame(mount)) } \ No newline at end of file diff --git a/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala b/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala new file mode 100644 index 00000000..02affddf --- /dev/null +++ b/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala @@ -0,0 +1,36 @@ +/* + * 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.examples.canvas + +import cats.effect.unsafe.implicits.global +import doodle.canvas.{*, given} +import doodle.core.* +import doodle.syntax.all.* + +import scala.scalajs.js.annotation.* + +@JSExportTopLevel("Experiment") +object Experiment { + def rect1 = Picture.rectangle(100, 100).fillColor(Color.red) + def rect2 = Picture.rectangle(100, 100).fillColor(Color.blue) + + val joint = rect1.beside(rect2).raster(10,20) + + @JSExport + def draw(mount: String) = + joint.drawWithFrame(Frame(mount)) +} \ No newline at end of file diff --git a/image/shared/src/main/scala/doodle/image/Image.scala b/image/shared/src/main/scala/doodle/image/Image.scala index 88975b9e..dc71a6ad 100644 --- a/image/shared/src/main/scala/doodle/image/Image.scala +++ b/image/shared/src/main/scala/doodle/image/Image.scala @@ -112,9 +112,6 @@ sealed abstract class Image extends Product with Serializable { def transform(tx: core.Transform): Image = Transform(tx, this) - def raster(width: Int, height: Int): Image = - Raster(width, height) - def rotate(angle: Angle): Image = this.transform(core.Transform.rotate(angle)) From f4deb936ef5d5317003017f24c33697f79aa03d1 Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Thu, 27 Jun 2024 12:17:20 +0530 Subject: [PATCH 13/43] Removed Raster from Image and added an experiment example --- image/shared/src/main/scala/doodle/image/Image.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/image/shared/src/main/scala/doodle/image/Image.scala b/image/shared/src/main/scala/doodle/image/Image.scala index dc71a6ad..fe8655f2 100644 --- a/image/shared/src/main/scala/doodle/image/Image.scala +++ b/image/shared/src/main/scala/doodle/image/Image.scala @@ -178,7 +178,6 @@ object Image { final case class ClosedPath(path: doodle.core.ClosedPath) extends Image final case class Text(get: String) extends Image final case class Circle(d: Double) extends Image - final case class Raster(w: Int, h: Int) extends Image final case class Rectangle(w: Double, h: Double) extends Image final case class Triangle(w: Double, h: Double) extends Image // final case class Draw(w: Double, h: Double, f: Canvas => Unit) extends Image From 879be9284a7235b144bbfab432b13ce42e3a668e Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Thu, 27 Jun 2024 14:17:13 +0530 Subject: [PATCH 14/43] Added Headers to 2 files --- .../doodle/algebra/generic/GenericRaster.scala | 16 ++++++++++++++++ .../main/scala/doodle/syntax/RasterSyntax.scala | 16 ++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/core/shared/src/main/scala/doodle/algebra/generic/GenericRaster.scala b/core/shared/src/main/scala/doodle/algebra/generic/GenericRaster.scala index f488ae8e..b2ab0816 100644 --- a/core/shared/src/main/scala/doodle/algebra/generic/GenericRaster.scala +++ b/core/shared/src/main/scala/doodle/algebra/generic/GenericRaster.scala @@ -1,3 +1,19 @@ +/* + * 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 algebra package generic diff --git a/core/shared/src/main/scala/doodle/syntax/RasterSyntax.scala b/core/shared/src/main/scala/doodle/syntax/RasterSyntax.scala index 101e1afa..17805be0 100644 --- a/core/shared/src/main/scala/doodle/syntax/RasterSyntax.scala +++ b/core/shared/src/main/scala/doodle/syntax/RasterSyntax.scala @@ -1,3 +1,19 @@ +/* + * 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 syntax From ce43de14393ed263d96e8672e9f4f9a378bf5343 Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Thu, 27 Jun 2024 16:56:09 +0530 Subject: [PATCH 15/43] Adding 2nd parameter in example --- .../main/scala/doodle/examples/canvas/Experiment.scala | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala b/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala index 02affddf..4d03bbb5 100644 --- a/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala +++ b/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala @@ -20,6 +20,7 @@ import cats.effect.unsafe.implicits.global import doodle.canvas.{*, given} import doodle.core.* import doodle.syntax.all.* +import org.scalajs.dom.CanvasRenderingContext2D import scala.scalajs.js.annotation.* @@ -27,8 +28,14 @@ import scala.scalajs.js.annotation.* object Experiment { def rect1 = Picture.rectangle(100, 100).fillColor(Color.red) def rect2 = Picture.rectangle(100, 100).fillColor(Color.blue) + + val drawFunction = + (gc: CanvasRenderingContext2D) => { + gc.fillRect(130, 190, 40, 60) + gc.fillRect(100, 160, 10, 30) + } - val joint = rect1.beside(rect2).raster(10,20) + val joint = rect1.beside(rect2).raster(10,20)(drawFunction) @JSExport def draw(mount: String) = From 7d53610fc1c558d9d480a9da7e1224caca93172a Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Thu, 27 Jun 2024 22:55:21 +0530 Subject: [PATCH 16/43] prePR formatted the code --- .../scala/doodle/canvas/algebra/CanvasDrawing.scala | 7 ++++--- .../src/main/scala/doodle/canvas/algebra/Raster.scala | 8 ++++---- .../src/main/scala/doodle/canvas/algebra/Shape.scala | 2 +- core/shared/src/main/scala/doodle/algebra/Raster.scala | 2 -- .../scala/doodle/algebra/generic/GenericShape.scala | 2 +- .../doodle/examples/canvas/ConcentricCircles.scala | 2 +- .../main/scala/doodle/examples/canvas/Experiment.scala | 10 +++++----- image/shared/src/main/scala/doodle/image/Image.scala | 2 +- 8 files changed, 17 insertions(+), 18 deletions(-) diff --git a/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala b/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala index f5fd0ad9..97ea0408 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala @@ -123,9 +123,10 @@ object CanvasDrawing { def raster(width: Int, height: Int)( f: CanvasRenderingContext2D => Unit - ): CanvasDrawing[Unit] = { - CanvasDrawing.raster(width, height)(f) - } + ): CanvasDrawing[Unit] = + CanvasDrawing { ctx => + f(ctx) + } def setFill(fill: Option[Fill]): CanvasDrawing[Unit] = fill.map(setFill).getOrElse(unit) diff --git a/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala b/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala index 03cabf24..c28d8dbd 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala @@ -17,17 +17,17 @@ package doodle.canvas.algebra import doodle.algebra.Algebra -import doodle.algebra.generic._ +import doodle.algebra.generic.* import org.scalajs.dom.CanvasRenderingContext2D - trait Raster extends GenericRaster[CanvasDrawing, CanvasRenderingContext2D] { self: Algebra { type Drawing[U] = Finalized[CanvasDrawing, U] } => object RasterApi extends RasterApi { - def raster(width: Int, height: Int)(f: CanvasRenderingContext2D => Unit): CanvasDrawing[Unit] = { + def raster(width: Int, height: Int)( + f: CanvasRenderingContext2D => Unit + ): CanvasDrawing[Unit] = { CanvasDrawing.raster(width, height)(f) } } } - diff --git a/canvas/src/main/scala/doodle/canvas/algebra/Shape.scala b/canvas/src/main/scala/doodle/canvas/algebra/Shape.scala index a43c325c..803f7965 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/Shape.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/Shape.scala @@ -70,4 +70,4 @@ trait Shape extends GenericShape[CanvasDrawing] { def unit: CanvasDrawing[Unit] = CanvasDrawing.unit } -} \ No newline at end of file +} diff --git a/core/shared/src/main/scala/doodle/algebra/Raster.scala b/core/shared/src/main/scala/doodle/algebra/Raster.scala index f07ab24c..8d41a707 100644 --- a/core/shared/src/main/scala/doodle/algebra/Raster.scala +++ b/core/shared/src/main/scala/doodle/algebra/Raster.scala @@ -30,5 +30,3 @@ trait RasterConstructor[A] { algebra.raster(width, height)(f) } } - - diff --git a/core/shared/src/main/scala/doodle/algebra/generic/GenericShape.scala b/core/shared/src/main/scala/doodle/algebra/generic/GenericShape.scala index ef2d6026..3d3891eb 100644 --- a/core/shared/src/main/scala/doodle/algebra/generic/GenericShape.scala +++ b/core/shared/src/main/scala/doodle/algebra/generic/GenericShape.scala @@ -94,4 +94,4 @@ trait GenericShape[G[_]] extends Shape { Finalized.leaf { _ => (BoundingBox.empty, Renderable.unit(ShapeApi.unit)) } - } \ No newline at end of file +} diff --git a/examples/js/src/main/scala/doodle/examples/canvas/ConcentricCircles.scala b/examples/js/src/main/scala/doodle/examples/canvas/ConcentricCircles.scala index a4727eff..0e8b69f8 100644 --- a/examples/js/src/main/scala/doodle/examples/canvas/ConcentricCircles.scala +++ b/examples/js/src/main/scala/doodle/examples/canvas/ConcentricCircles.scala @@ -37,4 +37,4 @@ object ConcentricCircles { @JSExport def draw(mount: String) = circles(7).drawWithFrame(Frame(mount)) -} \ No newline at end of file +} diff --git a/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala b/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala index 4d03bbb5..fadd00b5 100644 --- a/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala +++ b/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala @@ -28,16 +28,16 @@ import scala.scalajs.js.annotation.* object Experiment { def rect1 = Picture.rectangle(100, 100).fillColor(Color.red) def rect2 = Picture.rectangle(100, 100).fillColor(Color.blue) - + val drawFunction = (gc: CanvasRenderingContext2D) => { - gc.fillRect(130, 190, 40, 60) - gc.fillRect(100, 160, 10, 30) + gc.fillRect(130, 190, 40, 60) + gc.fillRect(100, 160, 10, 30) } - val joint = rect1.beside(rect2).raster(10,20)(drawFunction) + val joint = rect1.beside(rect2).raster(10, 20)(drawFunction) @JSExport def draw(mount: String) = joint.drawWithFrame(Frame(mount)) -} \ No newline at end of file +} diff --git a/image/shared/src/main/scala/doodle/image/Image.scala b/image/shared/src/main/scala/doodle/image/Image.scala index fe8655f2..baf963a4 100644 --- a/image/shared/src/main/scala/doodle/image/Image.scala +++ b/image/shared/src/main/scala/doodle/image/Image.scala @@ -238,7 +238,7 @@ object Image { Circle(diameter) def pie(diameter: Double, angle: Angle): Image = - path(doodle.core.ClosedPath.pie(0.0, 0.0, diameter, angle)) + path(doodle.core.ClosedPath.pie(0.0, 0.0, diameter, angle)) def rectangle(width: Double, height: Double): Image = Rectangle(width, height) From 4c4c4a6a2f363f31e4736f8376b5f07703de3e4d Mon Sep 17 00:00:00 2001 From: Noel Welsh Date: Thu, 27 Jun 2024 20:39:30 +0100 Subject: [PATCH 17/43] Fix ScalaFix configuration This makes it work with Scala 3.3. again --- .scalafix.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/.scalafix.conf b/.scalafix.conf index 7c3dd80a..4eedd04e 100644 --- a/.scalafix.conf +++ b/.scalafix.conf @@ -1,4 +1,5 @@ rules = [ OrganizeImports ] +OrganizeImports.removeUnused = false OrganizeImports.coalesceToWildcardImportThreshold = 5 From 5b51b481afb616bba5235b8cdc3dbbac09323beb Mon Sep 17 00:00:00 2001 From: Noel Welsh Date: Thu, 27 Jun 2024 20:39:51 +0100 Subject: [PATCH 18/43] Fix raster syntax Previous implementation didn't do anything. This implementation correctly adds an extension method. --- .../scala/doodle/syntax/RasterSyntax.scala | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/core/shared/src/main/scala/doodle/syntax/RasterSyntax.scala b/core/shared/src/main/scala/doodle/syntax/RasterSyntax.scala index 17805be0..e7fef224 100644 --- a/core/shared/src/main/scala/doodle/syntax/RasterSyntax.scala +++ b/core/shared/src/main/scala/doodle/syntax/RasterSyntax.scala @@ -17,16 +17,18 @@ package doodle package syntax +import doodle.algebra.Algebra import doodle.algebra.Picture import doodle.algebra.Raster trait RasterSyntax { - def raster[Alg <: Raster[A], A]( - width: Int, - height: Int - )(f: A => Unit): Picture[Alg, Unit] = - new Picture[Alg, Unit] { - def apply(implicit algebra: Alg): algebra.Drawing[Unit] = - algebra.raster(width, height)(f) - } + extension [Alg <: Algebra, A](picture: Picture[Alg, A]) { + def raster[R](width: Int, height: Int)( + f: R => Unit + ): Picture[Alg & Raster[R], Unit] = + new Picture[Alg & Raster[R], Unit] { + def apply(implicit algebra: Alg & Raster[R]): algebra.Drawing[Unit] = + algebra.raster(width, height)(f) + } + } } From 300f5fa0f74bd59e430435f54336beb176cdf8ad Mon Sep 17 00:00:00 2001 From: Noel Welsh Date: Thu, 27 Jun 2024 20:42:56 +0100 Subject: [PATCH 19/43] Add raster syntax to JVM syntax No reason it should only be available for JS --- core/jvm/src/main/scala/doodle/syntax/package.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/jvm/src/main/scala/doodle/syntax/package.scala b/core/jvm/src/main/scala/doodle/syntax/package.scala index bdb53859..ee8b89ee 100644 --- a/core/jvm/src/main/scala/doodle/syntax/package.scala +++ b/core/jvm/src/main/scala/doodle/syntax/package.scala @@ -27,6 +27,7 @@ package object syntax { with LayoutSyntax with NormalizedSyntax with PathSyntax + with RasterSyntax with RendererSyntax with ShapeSyntax with SizeSyntax @@ -46,6 +47,7 @@ package object syntax { object layout extends LayoutSyntax object normalized extends NormalizedSyntax object path extends PathSyntax + object raster extends RasterSyntax object renderer extends RendererSyntax object shape extends ShapeSyntax object size extends SizeSyntax From 635298df07c2403b3da69a41cd1d652d8c6d3635 Mon Sep 17 00:00:00 2001 From: Noel Welsh Date: Thu, 27 Jun 2024 20:43:20 +0100 Subject: [PATCH 20/43] Organize imports Change made by scalafix --- .../scala/doodle/examples/canvas/CanvasFrameBackground.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/js/src/main/scala/doodle/examples/canvas/CanvasFrameBackground.scala b/examples/js/src/main/scala/doodle/examples/canvas/CanvasFrameBackground.scala index 21c171cf..9d1c5930 100644 --- a/examples/js/src/main/scala/doodle/examples/canvas/CanvasFrameBackground.scala +++ b/examples/js/src/main/scala/doodle/examples/canvas/CanvasFrameBackground.scala @@ -17,8 +17,8 @@ package doodle.examples.canvas import cats.effect.unsafe.implicits.global -import doodle.core.Color import doodle.canvas.{*, given} +import doodle.core.Color import doodle.syntax.all.* import scala.scalajs.js.annotation.* From cab9c2fa1a0d7fe8a8bbc5614e397dfa3e40d57c Mon Sep 17 00:00:00 2001 From: Noel Welsh Date: Thu, 27 Jun 2024 20:43:38 +0100 Subject: [PATCH 21/43] Update workflows --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0169ea18..1ad7e909 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -71,11 +71,11 @@ jobs: - name: Make target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') - run: mkdir -p reactor/js/target image/js/target unidocs/target js/target reactor/jvm/target core/js/target examples/js/target turtle/js/target core/jvm/target jvm/target interact/js/target interact/jvm/target java2d/target examples/jvm/target turtle/jvm/target image/jvm/target svg/jvm/target svg/js/target golden/target target canvas/target project/target + run: mkdir -p reactor/js/target image/js/target unidocs/target js/target reactor/jvm/target core/js/target examples/js/target turtle/js/target core/jvm/target jvm/target interact/js/target interact/jvm/target java2d/target examples/jvm/target target turtle/jvm/target image/jvm/target svg/jvm/target svg/js/target golden/target canvas/target project/target - name: Compress target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') - run: tar cf targets.tar reactor/js/target image/js/target unidocs/target js/target reactor/jvm/target core/js/target examples/js/target turtle/js/target core/jvm/target jvm/target interact/js/target interact/jvm/target java2d/target examples/jvm/target turtle/jvm/target image/jvm/target svg/jvm/target svg/js/target golden/target target canvas/target project/target + run: tar cf targets.tar reactor/js/target image/js/target unidocs/target js/target reactor/jvm/target core/js/target examples/js/target turtle/js/target core/jvm/target jvm/target interact/js/target interact/jvm/target java2d/target examples/jvm/target target turtle/jvm/target image/jvm/target svg/jvm/target svg/js/target golden/target canvas/target project/target - name: Upload target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') From dada9640249c78a10c92685dc3ec84fec8e907e6 Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Mon, 1 Jul 2024 01:01:37 +0530 Subject: [PATCH 22/43] Changing Algrabra to something of combinator --- build.sbt | 4 ++-- .../doodle/canvas/algebra/CanvasDrawing.scala | 20 +++++++++++++++-- .../scala/doodle/canvas/algebra/Raster.scala | 4 ++-- .../main/scala/doodle/algebra/Raster.scala | 10 +-------- .../algebra/generic/GenericRaster.scala | 6 ++--- .../scala/doodle/syntax/RasterSyntax.scala | 10 +++++---- docs/src/pages/canvas/examples.md | 4 ++++ .../doodle/examples/canvas/Experiment.scala | 22 ++++++++++++++----- 8 files changed, 52 insertions(+), 28 deletions(-) diff --git a/build.sbt b/build.sbt index 4f521a7d..aab773ce 100644 --- a/build.sbt +++ b/build.sbt @@ -152,9 +152,9 @@ lazy val docs = val dest1 = mdocOut.value val dest2 = (laikaSite / target).value val cmd1 = - s"npx tailwindcss -i ${src.toString}/creative-scala.css -o ${dest1.toString}/creative-scala.css" + s"cmd /c npx tailwindcss -i ${src.toString}/creative-scala.css -o ${dest1.toString}/creative-scala.css" val cmd2 = - s"npx tailwindcss -i ${src.toString}/creative-scala.css -o ${dest2.toString}/creative-scala.css" + s"cmd /c npx tailwindcss -i ${src.toString}/creative-scala.css -o ${dest2.toString}/creative-scala.css" cmd1 ! cmd2 ! diff --git a/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala b/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala index 97ea0408..177b9953 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala @@ -16,6 +16,9 @@ package doodle.canvas.algebra +import doodle.algebra.Algebra +import doodle.algebra.generic.* + import cats.Apply import doodle.algebra.generic.Fill import doodle.algebra.generic.Fill.ColorFill @@ -37,6 +40,10 @@ import doodle.core.font.FontStyle import doodle.core.font.FontWeight import org.scalajs.dom.CanvasRenderingContext2D +import doodle.algebra.Algebra +import doodle.algebra.Raster +import doodle.algebra.Picture + import scala.scalajs.js import scala.scalajs.js.JSConverters.* @@ -121,12 +128,21 @@ object CanvasDrawing { } } - def raster(width: Int, height: Int)( + // def raster(img: Drawing[Unit])(width: Int, height: Int)( + // f: CanvasRenderingContext2D => Unit + // ): CanvasDrawing[Unit] = { + // CanvasDrawing { ctx => + // f(ctx) + // } + // } + + def raster(img: Picture[Nothing, Unit])(width: Int, height: Int)( f: CanvasRenderingContext2D => Unit - ): CanvasDrawing[Unit] = + ): CanvasDrawing[Unit] = { CanvasDrawing { ctx => f(ctx) } + } def setFill(fill: Option[Fill]): CanvasDrawing[Unit] = fill.map(setFill).getOrElse(unit) diff --git a/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala b/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala index c28d8dbd..938d399e 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala @@ -24,10 +24,10 @@ trait Raster extends GenericRaster[CanvasDrawing, CanvasRenderingContext2D] { self: Algebra { type Drawing[U] = Finalized[CanvasDrawing, U] } => object RasterApi extends RasterApi { - def raster(width: Int, height: Int)( + def raster(img: Drawing[Unit])(width: Int, height: Int)( f: CanvasRenderingContext2D => Unit ): CanvasDrawing[Unit] = { - CanvasDrawing.raster(width, height)(f) + CanvasDrawing.raster(img)(width, height)(f) } } } diff --git a/core/shared/src/main/scala/doodle/algebra/Raster.scala b/core/shared/src/main/scala/doodle/algebra/Raster.scala index 8d41a707..16190a1a 100644 --- a/core/shared/src/main/scala/doodle/algebra/Raster.scala +++ b/core/shared/src/main/scala/doodle/algebra/Raster.scala @@ -18,15 +18,7 @@ package doodle package algebra trait Raster[A] extends Algebra { - def raster(width: Int, height: Int)(f: A => Unit): Drawing[Unit] + def raster(img: Drawing[Unit])(width: Int, height: Int)(f: A => Unit): Drawing[Unit] } -trait RasterConstructor[A] { - self: BaseConstructor { type Algebra <: Raster[A] } => - def raster(width: Int, height: Int)(f: A => Unit): Picture[Unit] = - new Picture[Unit] { - def apply(implicit algebra: Algebra): algebra.Drawing[Unit] = - algebra.raster(width, height)(f) - } -} diff --git a/core/shared/src/main/scala/doodle/algebra/generic/GenericRaster.scala b/core/shared/src/main/scala/doodle/algebra/generic/GenericRaster.scala index b2ab0816..7eea7d90 100644 --- a/core/shared/src/main/scala/doodle/algebra/generic/GenericRaster.scala +++ b/core/shared/src/main/scala/doodle/algebra/generic/GenericRaster.scala @@ -25,17 +25,17 @@ trait GenericRaster[G[_], A] extends Raster[A] { self: Algebra { type Drawing[U] = Finalized[G, U] } => trait RasterApi { - def raster(width: Int, height: Int)(f: A => Unit): G[Unit] + def raster(img: Drawing[Unit])(width: Int, height: Int)(f: A => Unit): G[Unit] } def RasterApi: RasterApi - def raster(width: Int, height: Int)(f: A => Unit): Finalized[G, Unit] = { + def raster(img: Drawing[Unit])(width: Int, height: Int)(f: A => Unit): Finalized[G, Unit] = { Finalized.leaf { dc => val bb = BoundingBox.centered(width, height) ( bb, - State.inspect(_ => RasterApi.raster(width, height)(f)) + State.inspect(_ => RasterApi.raster(img)(width, height)(f)) ) } } diff --git a/core/shared/src/main/scala/doodle/syntax/RasterSyntax.scala b/core/shared/src/main/scala/doodle/syntax/RasterSyntax.scala index e7fef224..8d1da206 100644 --- a/core/shared/src/main/scala/doodle/syntax/RasterSyntax.scala +++ b/core/shared/src/main/scala/doodle/syntax/RasterSyntax.scala @@ -18,17 +18,19 @@ package doodle package syntax import doodle.algebra.Algebra -import doodle.algebra.Picture import doodle.algebra.Raster +import doodle.algebra.Picture trait RasterSyntax { - extension [Alg <: Algebra, A](picture: Picture[Alg, A]) { - def raster[R](width: Int, height: Int)( + extension [Alg <: Algebra, A]( + picture: Picture[Alg, A] + ) { + def raster[R](img: Picture[Alg, Unit])(width: Int, height: Int)( f: R => Unit ): Picture[Alg & Raster[R], Unit] = new Picture[Alg & Raster[R], Unit] { def apply(implicit algebra: Alg & Raster[R]): algebra.Drawing[Unit] = - algebra.raster(width, height)(f) + algebra.raster(img(algebra))(width, height)(f) } } } diff --git a/docs/src/pages/canvas/examples.md b/docs/src/pages/canvas/examples.md index 55b3df82..74f971a6 100644 --- a/docs/src/pages/canvas/examples.md +++ b/docs/src/pages/canvas/examples.md @@ -12,6 +12,10 @@ The source for these examples is [in the repository](https://github.com/creative @:doodle("parametric-spiral", "CanvasParametricSpiral.draw") +## Experiment + +@:doodle("Experiment", "Experiment.draw") + ## Frame Background diff --git a/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala b/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala index fadd00b5..ed7c22ae 100644 --- a/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala +++ b/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala @@ -26,16 +26,26 @@ import scala.scalajs.js.annotation.* @JSExportTopLevel("Experiment") object Experiment { - def rect1 = Picture.rectangle(100, 100).fillColor(Color.red) - def rect2 = Picture.rectangle(100, 100).fillColor(Color.blue) + def circle = Picture.circle(1000).fillColor(Color.red) val drawFunction = - (gc: CanvasRenderingContext2D) => { - gc.fillRect(130, 190, 40, 60) - gc.fillRect(100, 160, 10, 30) + (ctx: CanvasRenderingContext2D) => { + ctx.lineWidth = 10; + ctx.strokeRect(75, 140, 150, 110); + ctx.fillRect(130, 190, 40, 60); + ctx.beginPath(); + ctx.moveTo(50, 140); + ctx.lineTo(150, 60); + ctx.lineTo(250, 140); + ctx.ellipse(100, 100, 50, 75, Math.PI / 4, 0, 2 * Math.PI); + ctx.closePath(); + ctx.stroke(); } - val joint = rect1.beside(rect2).raster(10, 20)(drawFunction) + val joint = circle.raster(300, 250)(drawFunction) + println(joint.getClass) + println(drawFunction.getClass) + println(circle.getClass) @JSExport def draw(mount: String) = From 5ab7c2c79a123ea9823414a038f7b97bb74e04e0 Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Tue, 2 Jul 2024 02:33:11 +0530 Subject: [PATCH 23/43] Raster as constructor --- .../doodle/canvas/algebra/CanvasDrawing.scala | 13 ++-------- .../scala/doodle/canvas/algebra/Raster.scala | 4 +-- .../main/scala/doodle/algebra/Raster.scala | 12 ++++++++- .../algebra/generic/GenericRaster.scala | 6 ++--- .../scala/doodle/syntax/RasterSyntax.scala | 25 +++++++++---------- .../doodle/examples/canvas/Experiment.scala | 16 +++++++----- 6 files changed, 40 insertions(+), 36 deletions(-) diff --git a/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala b/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala index 177b9953..ecb684db 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala @@ -128,21 +128,12 @@ object CanvasDrawing { } } - // def raster(img: Drawing[Unit])(width: Int, height: Int)( - // f: CanvasRenderingContext2D => Unit - // ): CanvasDrawing[Unit] = { - // CanvasDrawing { ctx => - // f(ctx) - // } - // } - - def raster(img: Picture[Nothing, Unit])(width: Int, height: Int)( + def raster(width: Int, height: Int)( f: CanvasRenderingContext2D => Unit - ): CanvasDrawing[Unit] = { + ): CanvasDrawing[Unit] = CanvasDrawing { ctx => f(ctx) } - } def setFill(fill: Option[Fill]): CanvasDrawing[Unit] = fill.map(setFill).getOrElse(unit) diff --git a/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala b/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala index 938d399e..c28d8dbd 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala @@ -24,10 +24,10 @@ trait Raster extends GenericRaster[CanvasDrawing, CanvasRenderingContext2D] { self: Algebra { type Drawing[U] = Finalized[CanvasDrawing, U] } => object RasterApi extends RasterApi { - def raster(img: Drawing[Unit])(width: Int, height: Int)( + def raster(width: Int, height: Int)( f: CanvasRenderingContext2D => Unit ): CanvasDrawing[Unit] = { - CanvasDrawing.raster(img)(width, height)(f) + CanvasDrawing.raster(width, height)(f) } } } diff --git a/core/shared/src/main/scala/doodle/algebra/Raster.scala b/core/shared/src/main/scala/doodle/algebra/Raster.scala index 16190a1a..f07ab24c 100644 --- a/core/shared/src/main/scala/doodle/algebra/Raster.scala +++ b/core/shared/src/main/scala/doodle/algebra/Raster.scala @@ -18,7 +18,17 @@ package doodle package algebra trait Raster[A] extends Algebra { - def raster(img: Drawing[Unit])(width: Int, height: Int)(f: A => Unit): Drawing[Unit] + def raster(width: Int, height: Int)(f: A => Unit): Drawing[Unit] +} + +trait RasterConstructor[A] { + self: BaseConstructor { type Algebra <: Raster[A] } => + + def raster(width: Int, height: Int)(f: A => Unit): Picture[Unit] = + new Picture[Unit] { + def apply(implicit algebra: Algebra): algebra.Drawing[Unit] = + algebra.raster(width, height)(f) + } } diff --git a/core/shared/src/main/scala/doodle/algebra/generic/GenericRaster.scala b/core/shared/src/main/scala/doodle/algebra/generic/GenericRaster.scala index 7eea7d90..b2ab0816 100644 --- a/core/shared/src/main/scala/doodle/algebra/generic/GenericRaster.scala +++ b/core/shared/src/main/scala/doodle/algebra/generic/GenericRaster.scala @@ -25,17 +25,17 @@ trait GenericRaster[G[_], A] extends Raster[A] { self: Algebra { type Drawing[U] = Finalized[G, U] } => trait RasterApi { - def raster(img: Drawing[Unit])(width: Int, height: Int)(f: A => Unit): G[Unit] + def raster(width: Int, height: Int)(f: A => Unit): G[Unit] } def RasterApi: RasterApi - def raster(img: Drawing[Unit])(width: Int, height: Int)(f: A => Unit): Finalized[G, Unit] = { + def raster(width: Int, height: Int)(f: A => Unit): Finalized[G, Unit] = { Finalized.leaf { dc => val bb = BoundingBox.centered(width, height) ( bb, - State.inspect(_ => RasterApi.raster(img)(width, height)(f)) + State.inspect(_ => RasterApi.raster(width, height)(f)) ) } } diff --git a/core/shared/src/main/scala/doodle/syntax/RasterSyntax.scala b/core/shared/src/main/scala/doodle/syntax/RasterSyntax.scala index 8d1da206..3145288c 100644 --- a/core/shared/src/main/scala/doodle/syntax/RasterSyntax.scala +++ b/core/shared/src/main/scala/doodle/syntax/RasterSyntax.scala @@ -18,19 +18,18 @@ package doodle package syntax import doodle.algebra.Algebra -import doodle.algebra.Raster import doodle.algebra.Picture +import doodle.algebra.Raster trait RasterSyntax { - extension [Alg <: Algebra, A]( - picture: Picture[Alg, A] - ) { - def raster[R](img: Picture[Alg, Unit])(width: Int, height: Int)( - f: R => Unit - ): Picture[Alg & Raster[R], Unit] = - new Picture[Alg & Raster[R], Unit] { - def apply(implicit algebra: Alg & Raster[R]): algebra.Drawing[Unit] = - algebra.raster(img(algebra))(width, height)(f) - } - } -} + def raster[Alg <: Algebra, R]( + width: Int, + height: Int + )( + f: R => Unit + ): Picture[Alg & Raster[R], Unit] = + new Picture[Alg & Raster[R], Unit] { + def apply(implicit algebra: Alg & Raster[R]): algebra.Drawing[Unit] = + algebra.raster(width, height)(f) + } +} \ No newline at end of file diff --git a/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala b/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala index ed7c22ae..b39a80c8 100644 --- a/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala +++ b/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala @@ -26,7 +26,7 @@ import scala.scalajs.js.annotation.* @JSExportTopLevel("Experiment") object Experiment { - def circle = Picture.circle(1000).fillColor(Color.red) + def circle = Picture.circle(100).fillColor(Color.red) val drawFunction = (ctx: CanvasRenderingContext2D) => { @@ -37,15 +37,19 @@ object Experiment { ctx.moveTo(50, 140); ctx.lineTo(150, 60); ctx.lineTo(250, 140); - ctx.ellipse(100, 100, 50, 75, Math.PI / 4, 0, 2 * Math.PI); ctx.closePath(); ctx.stroke(); } - val joint = circle.raster(300, 250)(drawFunction) - println(joint.getClass) - println(drawFunction.getClass) - println(circle.getClass) + val drawFunction2 = + (ctx: CanvasRenderingContext2D) => { + ctx.fillRect(100, 100, 150, 110); + } + + val joint = circle.on(raster(300, 250)(drawFunction)) + + val joint2 = Picture.circle(50).fillColor(Color.blue) + .on(raster(250, 250)(drawFunction2)) @JSExport def draw(mount: String) = From eddca62777413c8b8d8c2bbbdad2517f4c48cc0d Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Wed, 3 Jul 2024 23:35:48 +0530 Subject: [PATCH 24/43] Added transformation to the Raster --- .../doodle/canvas/algebra/CanvasAlgebra.scala | 10 ++++++-- .../doodle/canvas/algebra/CanvasDrawing.scala | 23 ++++--------------- .../scala/doodle/canvas/algebra/Raster.scala | 17 ++++++++++---- .../algebra/generic/GenericRaster.scala | 17 ++++++++++++-- .../doodle/examples/canvas/Experiment.scala | 13 +++++++---- 5 files changed, 49 insertions(+), 31 deletions(-) diff --git a/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala b/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala index 79bfa315..3dd3020b 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala @@ -20,7 +20,7 @@ import cats.Apply import cats.Eval import cats.Functor import cats.Monad -import doodle.algebra.generic.* +import doodle.algebra.generic._ import doodle.core.BoundingBox import org.scalajs.dom.CanvasRenderingContext2D @@ -34,6 +34,7 @@ final case class CanvasAlgebra( GenericDebug[CanvasDrawing], GenericLayout[CanvasDrawing], GenericRaster[CanvasDrawing, CanvasRenderingContext2D], + GenericShape[CanvasDrawing], GenericSize[CanvasDrawing], GenericStyle[CanvasDrawing], GenericTransform[CanvasDrawing], @@ -41,6 +42,12 @@ final case class CanvasAlgebra( GivenFunctor[CanvasDrawing], doodle.algebra.Algebra { type Drawing[A] = doodle.canvas.Drawing[A] + + override def empty: Finalized[CanvasDrawing, Unit] = + Finalized.leaf(_ => + (BoundingBox.empty, Renderable.unit(CanvasDrawing.unit)) + ) + implicit val drawingInstance: Monad[Drawing] = new Monad[Drawing] { def pure[A](x: A): Drawing[A] = @@ -69,5 +76,4 @@ final case class CanvasAlgebra( ) } } - } diff --git a/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala b/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala index ecb684db..923258e7 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala @@ -1,19 +1,3 @@ -/* - * 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.canvas.algebra import doodle.algebra.Algebra @@ -32,7 +16,7 @@ import doodle.core.OpenPath import doodle.core.PathElement.BezierCurveTo import doodle.core.PathElement.LineTo import doodle.core.PathElement.MoveTo -import doodle.core.Transform +import doodle.core.Transform import doodle.core.font.Font import doodle.core.font.FontFamily import doodle.core.font.FontSize @@ -129,11 +113,12 @@ object CanvasDrawing { } def raster(width: Int, height: Int)( - f: CanvasRenderingContext2D => Unit - ): CanvasDrawing[Unit] = + f: CanvasRenderingContext2D => Unit + ): CanvasDrawing[Unit] = { CanvasDrawing { ctx => f(ctx) } + } def setFill(fill: Option[Fill]): CanvasDrawing[Unit] = fill.map(setFill).getOrElse(unit) diff --git a/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala b/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala index c28d8dbd..12f56fec 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala @@ -19,15 +19,24 @@ package doodle.canvas.algebra import doodle.algebra.Algebra import doodle.algebra.generic.* import org.scalajs.dom.CanvasRenderingContext2D +import doodle.core.Transform as Tx trait Raster extends GenericRaster[CanvasDrawing, CanvasRenderingContext2D] { self: Algebra { type Drawing[U] = Finalized[CanvasDrawing, U] } => object RasterApi extends RasterApi { - def raster(width: Int, height: Int)( - f: CanvasRenderingContext2D => Unit - ): CanvasDrawing[Unit] = { - CanvasDrawing.raster(width, height)(f) + def raster( + tx: Tx, + width: Int, + height: Int + )(f: CanvasRenderingContext2D => Unit): CanvasDrawing[Unit] = { + CanvasDrawing.setTransform(tx) >> + CanvasDrawing.raster(width, height)(f) + } + + def unit: CanvasDrawing[Unit] = { + CanvasDrawing.unit } } } + diff --git a/core/shared/src/main/scala/doodle/algebra/generic/GenericRaster.scala b/core/shared/src/main/scala/doodle/algebra/generic/GenericRaster.scala index b2ab0816..50ced06b 100644 --- a/core/shared/src/main/scala/doodle/algebra/generic/GenericRaster.scala +++ b/core/shared/src/main/scala/doodle/algebra/generic/GenericRaster.scala @@ -20,12 +20,18 @@ package generic import cats.data.State import doodle.core.BoundingBox +import doodle.core.{Transform as Tx} trait GenericRaster[G[_], A] extends Raster[A] { self: Algebra { type Drawing[U] = Finalized[G, U] } => trait RasterApi { - def raster(width: Int, height: Int)(f: A => Unit): G[Unit] + def raster( + tx: Tx, + width: Int, + height: Int + )(f: A => Unit): G[Unit] + def unit: G[Unit] } def RasterApi: RasterApi @@ -35,8 +41,15 @@ trait GenericRaster[G[_], A] extends Raster[A] { val bb = BoundingBox.centered(width, height) ( bb, - State.inspect(_ => RasterApi.raster(width, height)(f)) + State.inspect(tx => + RasterApi.raster(tx, width, height)(f) + ) ) } } + + def empty: Finalized[G, Unit] = + Finalized.leaf { _ => + (BoundingBox.empty, Renderable.unit(RasterApi.unit)) + } } diff --git a/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala b/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala index b39a80c8..17dbf34d 100644 --- a/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala +++ b/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala @@ -45,13 +45,18 @@ object Experiment { (ctx: CanvasRenderingContext2D) => { ctx.fillRect(100, 100, 150, 110); } + + val joint = circle.on(raster(250, 250)(drawFunction)) + val joint3 = circle.above((raster(250, 250)(drawFunction))) + val joint2 = circle.debug.fillColor(Color.blue) + .on(raster(250, 250)(drawFunction).debug) - val joint = circle.on(raster(300, 250)(drawFunction)) + val joint4 = (raster(250, 250)(drawFunction).debug).beside(circle.debug) + val joint5 = (circle.debug).beside(raster(250, 250)(drawFunction).debug) - val joint2 = Picture.circle(50).fillColor(Color.blue) - .on(raster(250, 250)(drawFunction2)) + val jjoint = (joint4).above(joint5) @JSExport def draw(mount: String) = - joint.drawWithFrame(Frame(mount)) + jjoint.drawWithFrame(Frame(mount)) } From a8be38bc06995700353a44842bd5d782f07d6e04 Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Wed, 10 Jul 2024 12:24:43 +0530 Subject: [PATCH 25/43] Replacing Canvas Methods with Doodle's --- .../doodle/canvas/algebra/CanvasAlgebra.scala | 1 + .../doodle/canvas/algebra/Immediate.scala | 87 +++++++++++++++++++ .../doodle/examples/canvas/Experiment.scala | 60 ++++++++++++- 3 files changed, 145 insertions(+), 3 deletions(-) create mode 100644 canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala diff --git a/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala b/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala index 3dd3020b..63e1fe4b 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala @@ -31,6 +31,7 @@ final case class CanvasAlgebra( ) extends Path, Raster, Shape, + immediate, GenericDebug[CanvasDrawing], GenericLayout[CanvasDrawing], GenericRaster[CanvasDrawing, CanvasRenderingContext2D], diff --git a/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala b/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala new file mode 100644 index 00000000..ca5eaf7d --- /dev/null +++ b/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala @@ -0,0 +1,87 @@ +/* + * 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.canvas.algebra + +import cats.effect.IO +import doodle.algebra.generic.Finalized +import doodle.canvas.Picture +import doodle.canvas.algebra.CanvasAlgebra +import doodle.canvas.algebra.CanvasDrawing +import doodle.core.Transform +import doodle.core.Color +import org.scalajs.dom + +import org.scalajs.dom.CanvasRenderingContext2D + +trait immediate { + def circle(x: Double, y: Double, radius: Double): Unit = + CanvasDrawing { ctx => + ctx.arc(x, y, radius, 0, 2 * Math.PI) + } + + def ellipse(x: Double, y: Double, radiusX: Double, radiusY: Double, rotation: Double, startAngle: Double, endAngle: Double): Unit = + CanvasDrawing { ctx => + ctx.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle) + } + + def elllipse(x: Double, y: Double, radiusX: Double, radiusY: Double, rotation: Double, startAngle: Double, endAngle: Double, counterclockwise: Boolean): Unit = + CanvasDrawing { ctx => + ctx.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise) + } + + def fillColor(color: Color): Unit = + CanvasDrawing { ctx => + ctx.fillStyle = CanvasDrawing.colorToCSS(color) + } + + def line(x1: Double, y1: Double, x2: Double, y2: Double): Unit = + CanvasDrawing { ctx => + ctx.beginPath() + ctx.moveTo(x1, y1) + ctx.lineTo(x2, y2) + ctx.closePath() + } + + def rectangle(x: Double, y: Double, width: Double, height: Double): Unit = + CanvasDrawing { ctx => + ctx.strokeRect(x, y, width, height) + } + + def rotate(angle: Double): Unit = + CanvasDrawing { ctx => + ctx.rotate(angle) + } + + def transform(a: Double, b: Double, c: Double, d: Double, e: Double, f: Double): Unit = + CanvasDrawing { ctx => + ctx.transform(a, b, c, d, e, f) + } + + def translate(x: Double, y: Double): Unit = + CanvasDrawing { ctx => + ctx.translate(x, y) + } + + def triangle(x1: Double, y1: Double, x2: Double, y2: Double, x3: Double, y3: Double): Unit = + CanvasDrawing { ctx => + ctx.beginPath() + ctx.moveTo(x1, y1) + ctx.lineTo(x2, y2) + ctx.lineTo(x3, y3) + ctx.closePath() + } +} diff --git a/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala b/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala index 17dbf34d..ef21999d 100644 --- a/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala +++ b/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala @@ -18,6 +18,7 @@ package doodle.examples.canvas import cats.effect.unsafe.implicits.global import doodle.canvas.{*, given} +import doodle.canvas.algebra.immediate import doodle.core.* import doodle.syntax.all.* import org.scalajs.dom.CanvasRenderingContext2D @@ -46,17 +47,70 @@ object Experiment { ctx.fillRect(100, 100, 150, 110); } + val drawFunctionImmediate = + (ctx: immediate) => { + ctx.fillColor(Color.green); + ctx.rectangle(100, 100, 150, 110); + } + + + val drawFunction3 = + (ctx: CanvasRenderingContext2D) => { + //ctx.fillColor(Color.green); + ctx.fillStyle = "green"; + ctx.fillRect(-100, -100, 200, 200); + } + + val drawFunction4 = + (ctx: CanvasRenderingContext2D) => { + ctx.fillStyle = "green"; + ctx.fillRect(-50, -50, 100, 100); + } + val joint = circle.on(raster(250, 250)(drawFunction)) val joint3 = circle.above((raster(250, 250)(drawFunction))) val joint2 = circle.debug.fillColor(Color.blue) .on(raster(250, 250)(drawFunction).debug) val joint4 = (raster(250, 250)(drawFunction).debug).beside(circle.debug) - val joint5 = (circle.debug).beside(raster(250, 250)(drawFunction).debug) + val joint5 = (circle.debug).beside(raster(250, 250)(drawFunctionImmediate).debug) + + val circle2 = Picture.circle(200).fillColor(Color.blue) + + val joint6 = (circle2.debug).beside(raster(200, 200)(drawFunction3).debug) + val joint7 = (raster(200, 200)(drawFunction3).debug).beside(circle2.debug) - val jjoint = (joint4).above(joint5) + val joint8 = (circle2.debug).on(raster(200, 200)(drawFunction3).debug) + + val joint9 = (circle2.debug).on(raster(200, 200)(drawFunction3).debug) + val joint10 = (raster(200, 200)(drawFunction3).debug).on(circle2.debug) + val joint11 = (raster(200, 200)(drawFunction4).debug).on(circle2.debug) + val jjoint = (joint9).above(joint10).beside(joint11) + + val circle3 = Picture.circle(200).fillColor(Color.blue).at(100, 100) + val jjoint2 = (circle3.debug).beside(raster(200, 200)(drawFunction3).debug) + + def tri = Picture.triangle(100,50).fillColor(Color.red) + def tri90 = tri.rotate(90.degrees) + def tri180 = tri.rotate(180.degrees) + def tri270 = tri.rotate(270.degrees) + + val joint12 = (tri.above((tri90.beside(raster(200, 200)(drawFunction3))) + .above(tri180))).beside(tri270) + + val drawFunction5 = + (ctx: CanvasRenderingContext2D) => { + ctx.beginPath(); + ctx.moveTo(30, 140); + ctx.lineTo(130, 60); + ctx.lineTo(230, 140); + ctx.closePath(); + ctx.stroke(); + } + + val joint13 = (raster(250, 250)(drawFunction5).debug).beside(circle.debug) @JSExport def draw(mount: String) = - jjoint.drawWithFrame(Frame(mount)) + joint5.drawWithFrame(Frame(mount)) } From 4a1268a2d636f606f888a3448b843a456c7924ca Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Wed, 10 Jul 2024 12:40:47 +0530 Subject: [PATCH 26/43] Replacing Canvas Methods with Doodle's --- .../main/scala/doodle/canvas/algebra/CanvasAlgebra.scala | 2 +- canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala | 2 +- .../src/main/scala/doodle/examples/canvas/Experiment.scala | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala b/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala index 63e1fe4b..bc37d292 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala @@ -31,7 +31,7 @@ final case class CanvasAlgebra( ) extends Path, Raster, Shape, - immediate, + Immediate, GenericDebug[CanvasDrawing], GenericLayout[CanvasDrawing], GenericRaster[CanvasDrawing, CanvasRenderingContext2D], diff --git a/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala b/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala index ca5eaf7d..c7c2ad6f 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala @@ -27,7 +27,7 @@ import org.scalajs.dom import org.scalajs.dom.CanvasRenderingContext2D -trait immediate { +trait Immediate { def circle(x: Double, y: Double, radius: Double): Unit = CanvasDrawing { ctx => ctx.arc(x, y, radius, 0, 2 * Math.PI) diff --git a/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala b/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala index ef21999d..24e8131e 100644 --- a/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala +++ b/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala @@ -18,7 +18,7 @@ package doodle.examples.canvas import cats.effect.unsafe.implicits.global import doodle.canvas.{*, given} -import doodle.canvas.algebra.immediate +import doodle.canvas.algebra.Immediate import doodle.core.* import doodle.syntax.all.* import org.scalajs.dom.CanvasRenderingContext2D @@ -48,7 +48,7 @@ object Experiment { } val drawFunctionImmediate = - (ctx: immediate) => { + (ctx: Immediate) => { ctx.fillColor(Color.green); ctx.rectangle(100, 100, 150, 110); } @@ -73,7 +73,7 @@ object Experiment { .on(raster(250, 250)(drawFunction).debug) val joint4 = (raster(250, 250)(drawFunction).debug).beside(circle.debug) - val joint5 = (circle.debug).beside(raster(250, 250)(drawFunctionImmediate).debug) + val joint5 = (circle).beside(raster(250, 250)(drawFunctionImmediate)) val circle2 = Picture.circle(200).fillColor(Color.blue) From 8b9d0a380060791b9dfb5e37f6e67a000b186c6a Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Fri, 12 Jul 2024 17:46:31 +0530 Subject: [PATCH 27/43] Passing canvasContext as implicit --- .../doodle/canvas/algebra/CanvasAlgebra.scala | 2 +- .../doodle/canvas/algebra/CanvasDrawing.scala | 9 ++- .../doodle/canvas/algebra/Immediate.scala | 65 ++----------------- .../scala/doodle/canvas/algebra/Raster.scala | 4 +- .../doodle/examples/canvas/Experiment.scala | 3 +- 5 files changed, 17 insertions(+), 66 deletions(-) diff --git a/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala b/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala index bc37d292..96f2df8d 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala @@ -34,7 +34,7 @@ final case class CanvasAlgebra( Immediate, GenericDebug[CanvasDrawing], GenericLayout[CanvasDrawing], - GenericRaster[CanvasDrawing, CanvasRenderingContext2D], + GenericRaster[CanvasDrawing, Immediate], GenericShape[CanvasDrawing], GenericSize[CanvasDrawing], GenericStyle[CanvasDrawing], diff --git a/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala b/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala index 923258e7..4a8f7ed7 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala @@ -35,6 +35,7 @@ import scala.scalajs.js.JSConverters.* * type `A` and has the side-effect of drawing on the canvas. */ opaque type CanvasDrawing[A] = Function[CanvasRenderingContext2D, A] + object CanvasDrawing { given Apply[CanvasDrawing] with { def ap[A, B](ff: CanvasDrawing[A => B])( @@ -113,13 +114,15 @@ object CanvasDrawing { } def raster(width: Int, height: Int)( - f: CanvasRenderingContext2D => Unit + f: Immediate => Unit ): CanvasDrawing[Unit] = { CanvasDrawing { ctx => - f(ctx) + implicit val implicitCtx: CanvasRenderingContext2D = ctx + val immediate = new Immediate {} + f(immediate) } } - + def setFill(fill: Option[Fill]): CanvasDrawing[Unit] = fill.map(setFill).getOrElse(unit) diff --git a/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala b/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala index c7c2ad6f..5fc0a063 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala @@ -16,72 +16,21 @@ package doodle.canvas.algebra -import cats.effect.IO -import doodle.algebra.generic.Finalized -import doodle.canvas.Picture -import doodle.canvas.algebra.CanvasAlgebra -import doodle.canvas.algebra.CanvasDrawing -import doodle.core.Transform import doodle.core.Color -import org.scalajs.dom - import org.scalajs.dom.CanvasRenderingContext2D trait Immediate { - def circle(x: Double, y: Double, radius: Double): Unit = - CanvasDrawing { ctx => - ctx.arc(x, y, radius, 0, 2 * Math.PI) - } - - def ellipse(x: Double, y: Double, radiusX: Double, radiusY: Double, rotation: Double, startAngle: Double, endAngle: Double): Unit = - CanvasDrawing { ctx => - ctx.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle) - } - - def elllipse(x: Double, y: Double, radiusX: Double, radiusY: Double, rotation: Double, startAngle: Double, endAngle: Double, counterclockwise: Boolean): Unit = - CanvasDrawing { ctx => - ctx.ellipse(x, y, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise) - } + def fillColor(color: Color)(implicit ctx: CanvasRenderingContext2D): Unit = { + ctx.fillStyle = CanvasDrawing.colorToCSS(color) + } - def fillColor(color: Color): Unit = - CanvasDrawing { ctx => - ctx.fillStyle = CanvasDrawing.colorToCSS(color) - } + def rectangle(x: Double, y: Double, width: Double, height: Double)(implicit ctx: CanvasRenderingContext2D): Unit = { + ctx.fillRect(x, y, width, height) + } +} - def line(x1: Double, y1: Double, x2: Double, y2: Double): Unit = - CanvasDrawing { ctx => - ctx.beginPath() - ctx.moveTo(x1, y1) - ctx.lineTo(x2, y2) - ctx.closePath() - } - def rectangle(x: Double, y: Double, width: Double, height: Double): Unit = - CanvasDrawing { ctx => - ctx.strokeRect(x, y, width, height) - } - def rotate(angle: Double): Unit = - CanvasDrawing { ctx => - ctx.rotate(angle) - } - def transform(a: Double, b: Double, c: Double, d: Double, e: Double, f: Double): Unit = - CanvasDrawing { ctx => - ctx.transform(a, b, c, d, e, f) - } - def translate(x: Double, y: Double): Unit = - CanvasDrawing { ctx => - ctx.translate(x, y) - } - def triangle(x1: Double, y1: Double, x2: Double, y2: Double, x3: Double, y3: Double): Unit = - CanvasDrawing { ctx => - ctx.beginPath() - ctx.moveTo(x1, y1) - ctx.lineTo(x2, y2) - ctx.lineTo(x3, y3) - ctx.closePath() - } -} diff --git a/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala b/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala index 12f56fec..e7839f51 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala @@ -21,7 +21,7 @@ import doodle.algebra.generic.* import org.scalajs.dom.CanvasRenderingContext2D import doodle.core.Transform as Tx -trait Raster extends GenericRaster[CanvasDrawing, CanvasRenderingContext2D] { +trait Raster extends GenericRaster[CanvasDrawing, Immediate] { self: Algebra { type Drawing[U] = Finalized[CanvasDrawing, U] } => object RasterApi extends RasterApi { @@ -29,7 +29,7 @@ trait Raster extends GenericRaster[CanvasDrawing, CanvasRenderingContext2D] { tx: Tx, width: Int, height: Int - )(f: CanvasRenderingContext2D => Unit): CanvasDrawing[Unit] = { + )(f: Immediate => Unit): CanvasDrawing[Unit] = { CanvasDrawing.setTransform(tx) >> CanvasDrawing.raster(width, height)(f) } diff --git a/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala b/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala index 24e8131e..e2fa90f5 100644 --- a/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala +++ b/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala @@ -56,7 +56,6 @@ object Experiment { val drawFunction3 = (ctx: CanvasRenderingContext2D) => { - //ctx.fillColor(Color.green); ctx.fillStyle = "green"; ctx.fillRect(-100, -100, 200, 200); } @@ -78,7 +77,7 @@ object Experiment { val circle2 = Picture.circle(200).fillColor(Color.blue) val joint6 = (circle2.debug).beside(raster(200, 200)(drawFunction3).debug) - val joint7 = (raster(200, 200)(drawFunction3).debug).beside(circle2.debug) + val joint7 = (raster(200, 200)(drawFunction3).debug).beside(circle2) val joint8 = (circle2.debug).on(raster(200, 200)(drawFunction3).debug) From 176c3702990b69effd60ed871d2fad8e218ae060 Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Sat, 13 Jul 2024 17:03:50 +0530 Subject: [PATCH 28/43] First working Immediate model --- .../doodle/canvas/algebra/CanvasAlgebra.scala | 2 +- .../doodle/canvas/algebra/CanvasDrawing.scala | 19 +++++++++++++++++-- .../doodle/canvas/algebra/Immediate.scala | 10 ++++++++-- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala b/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala index 96f2df8d..0b2875f9 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala @@ -31,7 +31,6 @@ final case class CanvasAlgebra( ) extends Path, Raster, Shape, - Immediate, GenericDebug[CanvasDrawing], GenericLayout[CanvasDrawing], GenericRaster[CanvasDrawing, Immediate], @@ -78,3 +77,4 @@ final case class CanvasAlgebra( } } } + diff --git a/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala b/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala index 4a8f7ed7..9179cfc8 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala @@ -1,3 +1,19 @@ +/* + * 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.canvas.algebra import doodle.algebra.Algebra @@ -117,8 +133,7 @@ object CanvasDrawing { f: Immediate => Unit ): CanvasDrawing[Unit] = { CanvasDrawing { ctx => - implicit val implicitCtx: CanvasRenderingContext2D = ctx - val immediate = new Immediate {} + val immediate = new ImmediateImpl(ctx) f(immediate) } } diff --git a/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala b/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala index 5fc0a063..142920e5 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala @@ -20,11 +20,16 @@ import doodle.core.Color import org.scalajs.dom.CanvasRenderingContext2D trait Immediate { - def fillColor(color: Color)(implicit ctx: CanvasRenderingContext2D): Unit = { + def fillColor(color: Color): Unit + def rectangle(x: Double, y: Double, width: Double, height: Double): Unit +} + +class ImmediateImpl(ctx: CanvasRenderingContext2D) extends Immediate { + def fillColor(color: Color): Unit = { ctx.fillStyle = CanvasDrawing.colorToCSS(color) } - def rectangle(x: Double, y: Double, width: Double, height: Double)(implicit ctx: CanvasRenderingContext2D): Unit = { + def rectangle(x: Double, y: Double, width: Double, height: Double): Unit = { ctx.fillRect(x, y, width, height) } } @@ -34,3 +39,4 @@ trait Immediate { + From 18c6a84ecd8e60020c3e0df54dba4b5b579c9f20 Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Wed, 17 Jul 2024 15:30:35 +0530 Subject: [PATCH 29/43] meh5 --- .../main/scala/doodle/canvas/algebra/Immediate.scala | 11 +++++++++++ .../scala/doodle/examples/canvas/Experiment.scala | 5 +++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala b/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala index 142920e5..619ee389 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala @@ -22,6 +22,7 @@ import org.scalajs.dom.CanvasRenderingContext2D trait Immediate { def fillColor(color: Color): Unit def rectangle(x: Double, y: Double, width: Double, height: Double): Unit + def line(x1: Double, y1: Double, x2: Double, y2: Double): Unit } class ImmediateImpl(ctx: CanvasRenderingContext2D) extends Immediate { @@ -30,8 +31,18 @@ class ImmediateImpl(ctx: CanvasRenderingContext2D) extends Immediate { } def rectangle(x: Double, y: Double, width: Double, height: Double): Unit = { + val x0 = x - width / 2 + val y0 = y - height / 2 ctx.fillRect(x, y, width, height) } + + def line(x1: Double, y1: Double, x2: Double, y2: Double): Unit = + CanvasDrawing { ctx => + ctx.beginPath() + ctx.moveTo(x1, y1) + ctx.lineTo(x2, y2) + ctx.closePath() + } } diff --git a/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala b/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala index e2fa90f5..d61132a1 100644 --- a/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala +++ b/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala @@ -50,7 +50,8 @@ object Experiment { val drawFunctionImmediate = (ctx: Immediate) => { ctx.fillColor(Color.green); - ctx.rectangle(100, 100, 150, 110); + ctx.rectangle(50, 50, 150, 110); + //ctx.line(0,0,100,100) } @@ -72,7 +73,7 @@ object Experiment { .on(raster(250, 250)(drawFunction).debug) val joint4 = (raster(250, 250)(drawFunction).debug).beside(circle.debug) - val joint5 = (circle).beside(raster(250, 250)(drawFunctionImmediate)) + val joint5 = (circle.debug).beside(raster(250, 250)(drawFunctionImmediate).debug) val circle2 = Picture.circle(200).fillColor(Color.blue) From 1fbc41fdfc5a9d35436286d72c69602c2796e5c5 Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Fri, 19 Jul 2024 01:04:02 +0530 Subject: [PATCH 30/43] Implemented few Immediate methods --- .../doodle/canvas/algebra/Immediate.scala | 69 ++++++++++++++++--- .../doodle/examples/canvas/Experiment.scala | 18 ++--- 2 files changed, 70 insertions(+), 17 deletions(-) diff --git a/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala b/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala index 619ee389..38ee71bd 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala @@ -20,29 +20,79 @@ import doodle.core.Color import org.scalajs.dom.CanvasRenderingContext2D trait Immediate { + def circle(x: Double, y: Double, radius: Double): Unit + def ellipse(x: Double, y: Double, width: Double, height: Double): Unit + def ellipse(x: Double, y: Double, radiusX: Double, radiusY: Double, rotation: Double, startAngle: Double, endAngle: Double): Unit + def ellipse(x: Double, y: Double, radiusX: Double, radiusY: Double, rotation: Double, startAngle: Double, endAngle: Double, counterclockwise: Boolean): Unit def fillColor(color: Color): Unit - def rectangle(x: Double, y: Double, width: Double, height: Double): Unit def line(x1: Double, y1: Double, x2: Double, y2: Double): Unit + def rectangle(x: Double, y: Double, width: Double, height: Double): Unit + def rotate(angle: Double): Unit + def transform(a: Double, b: Double, c: Double, d: Double, e: Double, f: Double): Unit + def translate(x: Double, y: Double): Unit } class ImmediateImpl(ctx: CanvasRenderingContext2D) extends Immediate { + def circle(x: Double, y: Double, radius: Double): Unit = { + val x0 = x - radius / 2.0 + val y0 = y - radius / 2.0 + ctx.beginPath() + ctx.arc(x0, y0, radius, 0, 2 * Math.PI) + ctx.stroke() + } + + def ellipse(x: Double, y: Double, width: Double, height: Double): Unit = { + val x0 = x - width / 2 + val y0 = y - height / 2 + ctx.beginPath() + ctx.ellipse(x0, y0, width / 2, height / 2, 0, 0, 2 * Math.PI) + ctx.stroke() + } + + def ellipse(x: Double, y: Double, radiusX: Double, radiusY: Double, rotation: Double, startAngle: Double, endAngle: Double): Unit = { + val x0 = x - radiusX / 2 + val y0 = y - radiusY / 2 + ctx.beginPath() + ctx.ellipse(x0, y0, radiusX, radiusY, rotation, startAngle, endAngle) + ctx.stroke() + } + + def ellipse(x: Double, y: Double, radiusX: Double, radiusY: Double, rotation: Double, startAngle: Double, endAngle: Double, counterclockwise: Boolean): Unit = { + val x0 = x - radiusX / 2 + val y0 = y - radiusY / 2 + ctx.beginPath() + ctx.ellipse(x0, y0, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise) + ctx.stroke() + } + def fillColor(color: Color): Unit = { ctx.fillStyle = CanvasDrawing.colorToCSS(color) } + def line(x1: Double, y1: Double, x2: Double, y2: Double): Unit = { + ctx.beginPath() + ctx.moveTo(x1, y1) + ctx.lineTo(x2, y2) + ctx.stroke() + } + def rectangle(x: Double, y: Double, width: Double, height: Double): Unit = { val x0 = x - width / 2 val y0 = y - height / 2 - ctx.fillRect(x, y, width, height) + ctx.fillRect(x0, y0, width, height) + } + + def rotate(angle: Double): Unit = { + ctx.rotate(angle) + } + + def transform(a: Double, b: Double, c: Double, d: Double, e: Double, f: Double): Unit = { + ctx.transform(a, b, c, d, e, f) } - def line(x1: Double, y1: Double, x2: Double, y2: Double): Unit = - CanvasDrawing { ctx => - ctx.beginPath() - ctx.moveTo(x1, y1) - ctx.lineTo(x2, y2) - ctx.closePath() - } + def translate(x: Double, y: Double): Unit = { + ctx.translate(x, y) + } } @@ -51,3 +101,4 @@ class ImmediateImpl(ctx: CanvasRenderingContext2D) extends Immediate { + diff --git a/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala b/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala index d61132a1..5a272a0d 100644 --- a/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala +++ b/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala @@ -47,14 +47,6 @@ object Experiment { ctx.fillRect(100, 100, 150, 110); } - val drawFunctionImmediate = - (ctx: Immediate) => { - ctx.fillColor(Color.green); - ctx.rectangle(50, 50, 150, 110); - //ctx.line(0,0,100,100) - } - - val drawFunction3 = (ctx: CanvasRenderingContext2D) => { ctx.fillStyle = "green"; @@ -110,6 +102,16 @@ object Experiment { val joint13 = (raster(250, 250)(drawFunction5).debug).beside(circle.debug) + def circle = Picture.circle(100).fillColor(Color.red) + + val drawFunctionImmediate = + (ctx: Immediate) => { + ctx.fillColor(Color.green); + ctx.rectangle(0, 0, 100, 100); + ctx.line(0,0,50,50) + ctx.ellipse(50, 50, 50, 50); + } + @JSExport def draw(mount: String) = joint5.drawWithFrame(Frame(mount)) From 826e35f69459d0509663deb0a3234e7acb807305 Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Wed, 24 Jul 2024 15:58:51 +0530 Subject: [PATCH 31/43] Added Methods for Immediate --- .../doodle/canvas/algebra/Immediate.scala | 114 +++++++++++++++++- .../doodle/examples/canvas/Experiment.scala | 16 ++- 2 files changed, 121 insertions(+), 9 deletions(-) diff --git a/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala b/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala index 38ee71bd..913c5a5d 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala @@ -20,19 +20,81 @@ import doodle.core.Color import org.scalajs.dom.CanvasRenderingContext2D trait Immediate { + def arc(x: Int, y: Int, radius: Int, startAngle: Double, endAngle: Double): Unit + def arc(x: Int, y: Int, radius: Int, startAngle: Double, endAngle: Double, anticlockwise: Boolean): Unit + def arcTo(x1: Int, y1: Int, x2: Int, y2: Int, radius: Int): Unit + def bezierCurveTo(cp1x: Int, cp1y: Int, cp2x: Int, cp2y: Int, x: Int, y: Int): Unit + def beginPath(): Unit + def clearRect(x: Int, y: Int, width: Int, height: Int): Unit + def clip(x: Int, y: Int, radius: Int, startAngle: Double): Unit + def closePath(): Unit def circle(x: Double, y: Double, radius: Double): Unit def ellipse(x: Double, y: Double, width: Double, height: Double): Unit def ellipse(x: Double, y: Double, radiusX: Double, radiusY: Double, rotation: Double, startAngle: Double, endAngle: Double): Unit def ellipse(x: Double, y: Double, radiusX: Double, radiusY: Double, rotation: Double, startAngle: Double, endAngle: Double, counterclockwise: Boolean): Unit def fillColor(color: Color): Unit + def text(text: String, x: Double, y: Double): Unit def line(x1: Double, y1: Double, x2: Double, y2: Double): Unit + def quadraticCurveTo(cpx: Double, cpy: Double, x: Double, y: Double): Unit def rectangle(x: Double, y: Double, width: Double, height: Double): Unit def rotate(angle: Double): Unit + def roundedRectangle(x: Int, y: Int, width: Int, height: Int, radius: Int): Unit + def stroke(color: Color): Unit + def strokeText(text: String, x: Double, y: Double): Unit def transform(a: Double, b: Double, c: Double, d: Double, e: Double, f: Double): Unit def translate(x: Double, y: Double): Unit + def triangle(x1: Double, y1: Double, x2: Double, y2: Double, x3: Double, y3: Double): Unit } class ImmediateImpl(ctx: CanvasRenderingContext2D) extends Immediate { + def arc(x: Int, y: Int, radius: Int, startAngle: Double, endAngle: Double): Unit = { + ctx.beginPath() + ctx.arc(x, y, radius, startAngle, endAngle) + ctx.stroke() + } + + def arc(x: Int, y: Int, radius: Int, startAngle: Double, endAngle: Double, anticlockwise: Boolean): Unit = { + val x0 = x - radius / 2 + val y0 = y - radius / 2 + ctx.beginPath() + ctx.arc(x0, y0, radius, startAngle, endAngle, anticlockwise) + ctx.stroke() + } + + def arcTo(x1: Int, y1: Int, x2: Int, y2: Int, radius: Int): Unit = { + ctx.beginPath() + ctx.arcTo(x1, y1, x2, y2, radius) + ctx.stroke() + } + + def bezierCurveTo(cp1x: Int, cp1y: Int, cp2x: Int, cp2y: Int, x: Int, y: Int): Unit = { + ctx.beginPath() + ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) + ctx.stroke() + } + + def beginPath(): Unit = { + ctx.beginPath(); + } + + def clearRect(x: Int, y: Int, width: Int, height: Int): Unit = { + val x0 = x / 2 + val y0 = y / 2 + ctx.clearRect(x0, y0, width, height) + } + + def clip(x: Int, y: Int, radius: Int, startAngle: Double): Unit = { + val x0 = x - radius / 2 + val y0 = y - radius / 2 + ctx.beginPath() + ctx.arc(x0, y0, radius, startAngle, 2 * Math.PI) + ctx.clip() + } + + def closePath(): Unit = { + ctx.closePath() + } + def circle(x: Double, y: Double, radius: Double): Unit = { val x0 = x - radius / 2.0 val y0 = y - radius / 2.0 @@ -46,7 +108,7 @@ class ImmediateImpl(ctx: CanvasRenderingContext2D) extends Immediate { val y0 = y - height / 2 ctx.beginPath() ctx.ellipse(x0, y0, width / 2, height / 2, 0, 0, 2 * Math.PI) - ctx.stroke() + ctx.fill() } def ellipse(x: Double, y: Double, radiusX: Double, radiusY: Double, rotation: Double, startAngle: Double, endAngle: Double): Unit = { @@ -54,7 +116,7 @@ class ImmediateImpl(ctx: CanvasRenderingContext2D) extends Immediate { val y0 = y - radiusY / 2 ctx.beginPath() ctx.ellipse(x0, y0, radiusX, radiusY, rotation, startAngle, endAngle) - ctx.stroke() + ctx.fill() } def ellipse(x: Double, y: Double, radiusX: Double, radiusY: Double, rotation: Double, startAngle: Double, endAngle: Double, counterclockwise: Boolean): Unit = { @@ -62,11 +124,17 @@ class ImmediateImpl(ctx: CanvasRenderingContext2D) extends Immediate { val y0 = y - radiusY / 2 ctx.beginPath() ctx.ellipse(x0, y0, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise) - ctx.stroke() + ctx.fill() } def fillColor(color: Color): Unit = { ctx.fillStyle = CanvasDrawing.colorToCSS(color) + ctx.fill() + } + + def text(text: String, x: Double, y: Double): Unit = { + val x0 = x - text.length * 2 + ctx.fillText(text, x0, y) } def line(x1: Double, y1: Double, x2: Double, y2: Double): Unit = { @@ -76,16 +144,45 @@ class ImmediateImpl(ctx: CanvasRenderingContext2D) extends Immediate { ctx.stroke() } + def quadraticCurveTo(cpx: Double, cpy: Double, x: Double, y: Double): Unit = { + ctx.beginPath() + ctx.quadraticCurveTo(cpx, cpy, x, y) + ctx.stroke() + } + def rectangle(x: Double, y: Double, width: Double, height: Double): Unit = { val x0 = x - width / 2 val y0 = y - height / 2 - ctx.fillRect(x0, y0, width, height) + ctx.beginPath() + ctx.rect(x0, y0, width, height) } def rotate(angle: Double): Unit = { ctx.rotate(angle) } + def roundedRectangle(x: Int, y: Int, width: Int, height: Int, radius: Int): Unit = { + val x0 = x - width / 2 + val y0 = y - height / 2 + ctx.beginPath() + ctx.moveTo(x0 + radius, y0) + ctx.arcTo(x0 + width, y0, x0 + width, y0 + height, radius) + ctx.arcTo(x0 + width, y0 + height, x0, y0 + height, radius) + ctx.arcTo(x0, y0 + height, x0, y0, radius) + ctx.arcTo(x0, y0, x0 + width, y0, radius) + ctx.fill() + } + + def stroke(color: Color): Unit = { + ctx.strokeStyle = CanvasDrawing.colorToCSS(color) + ctx.stroke() + } + + def strokeText(text: String, x: Double, y: Double): Unit = { + val x0 = x - text.length * 2 + ctx.strokeText(text, x0, y) + } + def transform(a: Double, b: Double, c: Double, d: Double, e: Double, f: Double): Unit = { ctx.transform(a, b, c, d, e, f) } @@ -93,6 +190,15 @@ class ImmediateImpl(ctx: CanvasRenderingContext2D) extends Immediate { def translate(x: Double, y: Double): Unit = { ctx.translate(x, y) } + + def triangle(x1: Double, y1: Double, x2: Double, y2: Double, x3: Double, y3: Double): Unit = { + ctx.beginPath() + ctx.moveTo(x1, y1) + ctx.lineTo(x2, y2) + ctx.lineTo(x3, y3) + ctx.lineTo(x1, y1) + ctx.fill() + } } diff --git a/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala b/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala index 5a272a0d..d440f6d0 100644 --- a/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala +++ b/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala @@ -27,7 +27,7 @@ import scala.scalajs.js.annotation.* @JSExportTopLevel("Experiment") object Experiment { - def circle = Picture.circle(100).fillColor(Color.red) + //def circle = Picture.circle(100).fillColor(Color.red) val drawFunction = (ctx: CanvasRenderingContext2D) => { @@ -65,7 +65,6 @@ object Experiment { .on(raster(250, 250)(drawFunction).debug) val joint4 = (raster(250, 250)(drawFunction).debug).beside(circle.debug) - val joint5 = (circle.debug).beside(raster(250, 250)(drawFunctionImmediate).debug) val circle2 = Picture.circle(200).fillColor(Color.blue) @@ -106,12 +105,19 @@ object Experiment { val drawFunctionImmediate = (ctx: Immediate) => { - ctx.fillColor(Color.green); ctx.rectangle(0, 0, 100, 100); - ctx.line(0,0,50,50) - ctx.ellipse(50, 50, 50, 50); + ctx.fillColor(Color.green); + ctx.stroke(Color.red); + ctx.triangle(-50, 50, 50, 50, 0, 80); + ctx.fillColor(Color.blue); + ctx.transform(1, 0.2, 0.8, 1, 0, 0); + ctx.translate(10,10); + ctx.ellipse(0,0,50,30); + ctx.fillColor(Color.pink); } + val joint5 = (circle).beside(raster(200, 200)(drawFunctionImmediate)) + @JSExport def draw(mount: String) = joint5.drawWithFrame(Frame(mount)) From 52ddb0973d479a9dc10af70a01e128306717b108 Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Fri, 2 Aug 2024 15:51:26 +0530 Subject: [PATCH 32/43] fillColor to fill --- .../scala/doodle/canvas/algebra/Immediate.scala | 17 ++++++++++++++--- .../doodle/examples/canvas/Experiment.scala | 10 +++++----- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala b/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala index 913c5a5d..6f588bb9 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala @@ -17,6 +17,8 @@ package doodle.canvas.algebra import doodle.core.Color +import doodle.core.* +import doodle.syntax.all.* import org.scalajs.dom.CanvasRenderingContext2D trait Immediate { @@ -32,9 +34,10 @@ trait Immediate { def ellipse(x: Double, y: Double, width: Double, height: Double): Unit def ellipse(x: Double, y: Double, radiusX: Double, radiusY: Double, rotation: Double, startAngle: Double, endAngle: Double): Unit def ellipse(x: Double, y: Double, radiusX: Double, radiusY: Double, rotation: Double, startAngle: Double, endAngle: Double, counterclockwise: Boolean): Unit - def fillColor(color: Color): Unit + def fill(color: Color): Unit def text(text: String, x: Double, y: Double): Unit def line(x1: Double, y1: Double, x2: Double, y2: Double): Unit + def line(x1: Double, y1: Double, x2: Double, y2: Double, closedPath: Boolean): Unit def quadraticCurveTo(cpx: Double, cpy: Double, x: Double, y: Double): Unit def rectangle(x: Double, y: Double, width: Double, height: Double): Unit def rotate(angle: Double): Unit @@ -127,7 +130,7 @@ class ImmediateImpl(ctx: CanvasRenderingContext2D) extends Immediate { ctx.fill() } - def fillColor(color: Color): Unit = { + def fill(color: Color): Unit = { ctx.fillStyle = CanvasDrawing.colorToCSS(color) ctx.fill() } @@ -138,9 +141,17 @@ class ImmediateImpl(ctx: CanvasRenderingContext2D) extends Immediate { } def line(x1: Double, y1: Double, x2: Double, y2: Double): Unit = { - ctx.beginPath() + //ctx.beginPath() + ctx.moveTo(x1, y1) + ctx.lineTo(x2, y2) + //ctx.stroke() + } + + def line(x1: Double, y1: Double, x2: Double, y2: Double, closedPath: Boolean): Unit = { + //ctx.beginPath() ctx.moveTo(x1, y1) ctx.lineTo(x2, y2) + if(closedPath) ctx.closePath() ctx.stroke() } diff --git a/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala b/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala index d440f6d0..85c046d0 100644 --- a/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala +++ b/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala @@ -106,19 +106,19 @@ object Experiment { val drawFunctionImmediate = (ctx: Immediate) => { ctx.rectangle(0, 0, 100, 100); - ctx.fillColor(Color.green); + ctx.fill(Color.green); ctx.stroke(Color.red); ctx.triangle(-50, 50, 50, 50, 0, 80); - ctx.fillColor(Color.blue); + ctx.fill(Color.blue); ctx.transform(1, 0.2, 0.8, 1, 0, 0); ctx.translate(10,10); ctx.ellipse(0,0,50,30); - ctx.fillColor(Color.pink); + ctx.fill(Color.pink); } - val joint5 = (circle).beside(raster(200, 200)(drawFunctionImmediate)) + val joint5 = (circle).beside(raster(200, 200)(drawFunction5)) @JSExport def draw(mount: String) = - joint5.drawWithFrame(Frame(mount)) + joint13.drawWithFrame(Frame(mount)) } From acc892110ab979a929614c9cde8743008b5af75a Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Wed, 7 Aug 2024 15:09:21 +0530 Subject: [PATCH 33/43] Methods added --- .../doodle/canvas/algebra/CanvasDrawing.scala | 4 +- .../doodle/canvas/algebra/Immediate.scala | 76 +++++++++---- .../doodle/examples/canvas/Experiment.scala | 107 ++++++------------ 3 files changed, 86 insertions(+), 101 deletions(-) diff --git a/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala b/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala index 9179cfc8..a26c7571 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala @@ -39,6 +39,7 @@ import doodle.core.font.FontSize import doodle.core.font.FontStyle import doodle.core.font.FontWeight import org.scalajs.dom.CanvasRenderingContext2D +import org.scalajs.dom.Path2D import doodle.algebra.Algebra import doodle.algebra.Raster @@ -133,7 +134,8 @@ object CanvasDrawing { f: Immediate => Unit ): CanvasDrawing[Unit] = { CanvasDrawing { ctx => - val immediate = new ImmediateImpl(ctx) + val path = new Path2D() + val immediate = new ImmediateImpl(ctx,path) f(immediate) } } diff --git a/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala b/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala index 6f588bb9..972ccde9 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala @@ -20,15 +20,19 @@ import doodle.core.Color import doodle.core.* import doodle.syntax.all.* import org.scalajs.dom.CanvasRenderingContext2D +import org.scalajs.dom.Path2D +import scala.annotation.tailrec + trait Immediate { - def arc(x: Int, y: Int, radius: Int, startAngle: Double, endAngle: Double): Unit - def arc(x: Int, y: Int, radius: Int, startAngle: Double, endAngle: Double, anticlockwise: Boolean): Unit + def arc(x: Int, y: Int, radius: Int, startAngle: Double, endAngle: Double, closedPath: Boolean = false): Unit def arcTo(x1: Int, y1: Int, x2: Int, y2: Int, radius: Int): Unit def bezierCurveTo(cp1x: Int, cp1y: Int, cp2x: Int, cp2y: Int, x: Int, y: Int): Unit def beginPath(): Unit def clearRect(x: Int, y: Int, width: Int, height: Int): Unit - def clip(x: Int, y: Int, radius: Int, startAngle: Double): Unit + def clip(): Unit + def clipArc(x: Int, y: Int, radius: Int, startAngle: Double): Unit + def clipRect(x: Int, y: Int, width: Int, height: Int): Unit def closePath(): Unit def circle(x: Double, y: Double, radius: Double): Unit def ellipse(x: Double, y: Double, width: Double, height: Double): Unit @@ -36,8 +40,9 @@ trait Immediate { def ellipse(x: Double, y: Double, radiusX: Double, radiusY: Double, rotation: Double, startAngle: Double, endAngle: Double, counterclockwise: Boolean): Unit def fill(color: Color): Unit def text(text: String, x: Double, y: Double): Unit + def line(x: Double, y: Double, closedPath: Boolean = false): Unit def line(x1: Double, y1: Double, x2: Double, y2: Double): Unit - def line(x1: Double, y1: Double, x2: Double, y2: Double, closedPath: Boolean): Unit + def pentagon(x: Double, y: Double, radius: Double): Unit def quadraticCurveTo(cpx: Double, cpy: Double, x: Double, y: Double): Unit def rectangle(x: Double, y: Double, width: Double, height: Double): Unit def rotate(angle: Double): Unit @@ -49,18 +54,13 @@ trait Immediate { def triangle(x1: Double, y1: Double, x2: Double, y2: Double, x3: Double, y3: Double): Unit } -class ImmediateImpl(ctx: CanvasRenderingContext2D) extends Immediate { - def arc(x: Int, y: Int, radius: Int, startAngle: Double, endAngle: Double): Unit = { - ctx.beginPath() - ctx.arc(x, y, radius, startAngle, endAngle) - ctx.stroke() - } - - def arc(x: Int, y: Int, radius: Int, startAngle: Double, endAngle: Double, anticlockwise: Boolean): Unit = { +class ImmediateImpl(ctx: CanvasRenderingContext2D, region: Path2D) extends Immediate { + def arc(x: Int, y: Int, radius: Int, startAngle: Double, endAngle: Double, closedPath: Boolean = false): Unit = { val x0 = x - radius / 2 val y0 = y - radius / 2 ctx.beginPath() - ctx.arc(x0, y0, radius, startAngle, endAngle, anticlockwise) + ctx.arc(x0, y0, radius, startAngle, endAngle) + if(closedPath) ctx.closePath(); ctx.stroke() } @@ -86,12 +86,20 @@ class ImmediateImpl(ctx: CanvasRenderingContext2D) extends Immediate { ctx.clearRect(x0, y0, width, height) } - def clip(x: Int, y: Int, radius: Int, startAngle: Double): Unit = { + def clip(): Unit = { + ctx.clip(region) + } + + def clipArc(x: Int, y: Int, radius: Int, startAngle: Double): Unit = { val x0 = x - radius / 2 val y0 = y - radius / 2 - ctx.beginPath() - ctx.arc(x0, y0, radius, startAngle, 2 * Math.PI) - ctx.clip() + region.arc(x0, y0, radius, startAngle, 2 * Math.PI) + } + + def clipRect(x: Int, y: Int, width: Int, height: Int): Unit = { + val x0 = x - width / 2 + val y0 = y - height / 2 + region.rect(x0, y0, width, height) } def closePath(): Unit = { @@ -140,21 +148,39 @@ class ImmediateImpl(ctx: CanvasRenderingContext2D) extends Immediate { ctx.fillText(text, x0, y) } - def line(x1: Double, y1: Double, x2: Double, y2: Double): Unit = { - //ctx.beginPath() - ctx.moveTo(x1, y1) - ctx.lineTo(x2, y2) - //ctx.stroke() + def line(x: Double, y: Double, closedPath: Boolean = false): Unit = { + ctx.lineTo(x,y) + if(closedPath) ctx.closePath() + ctx.stroke() } - def line(x1: Double, y1: Double, x2: Double, y2: Double, closedPath: Boolean): Unit = { - //ctx.beginPath() + def line(x1: Double, y1: Double, x2: Double, y2: Double): Unit = { + ctx.beginPath(); ctx.moveTo(x1, y1) ctx.lineTo(x2, y2) - if(closedPath) ctx.closePath() ctx.stroke() } + def pentagon(x: Double, y: Double, radius: Double): Unit = { + val x0 = x - radius / 2 + val y0 = y - radius / 2 + + @tailrec + def drawSide(i: Int): Unit = { + if (i <= 5) { + val angle = 2 * Math.PI * i / 5 + val x1 = x0 + radius * Math.cos(angle) + val y1 = y0 + radius * Math.sin(angle) + ctx.lineTo(x1, y1) + drawSide(i + 1) + } + } + ctx.beginPath() + ctx.moveTo(x0 + radius, y0) + drawSide(1) + //ctx.fill() + } + def quadraticCurveTo(cpx: Double, cpy: Double, x: Double, y: Double): Unit = { ctx.beginPath() ctx.quadraticCurveTo(cpx, cpy, x, y) diff --git a/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala b/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala index 85c046d0..a74eac1b 100644 --- a/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala +++ b/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala @@ -27,84 +27,14 @@ import scala.scalajs.js.annotation.* @JSExportTopLevel("Experiment") object Experiment { - //def circle = Picture.circle(100).fillColor(Color.red) - - val drawFunction = - (ctx: CanvasRenderingContext2D) => { - ctx.lineWidth = 10; - ctx.strokeRect(75, 140, 150, 110); - ctx.fillRect(130, 190, 40, 60); - ctx.beginPath(); - ctx.moveTo(50, 140); - ctx.lineTo(150, 60); - ctx.lineTo(250, 140); - ctx.closePath(); - ctx.stroke(); - } - - val drawFunction2 = - (ctx: CanvasRenderingContext2D) => { - ctx.fillRect(100, 100, 150, 110); - } - - val drawFunction3 = - (ctx: CanvasRenderingContext2D) => { - ctx.fillStyle = "green"; - ctx.fillRect(-100, -100, 200, 200); - } - - val drawFunction4 = - (ctx: CanvasRenderingContext2D) => { - ctx.fillStyle = "green"; - ctx.fillRect(-50, -50, 100, 100); - } - - val joint = circle.on(raster(250, 250)(drawFunction)) - val joint3 = circle.above((raster(250, 250)(drawFunction))) - val joint2 = circle.debug.fillColor(Color.blue) - .on(raster(250, 250)(drawFunction).debug) - - val joint4 = (raster(250, 250)(drawFunction).debug).beside(circle.debug) - - val circle2 = Picture.circle(200).fillColor(Color.blue) - - val joint6 = (circle2.debug).beside(raster(200, 200)(drawFunction3).debug) - val joint7 = (raster(200, 200)(drawFunction3).debug).beside(circle2) - - val joint8 = (circle2.debug).on(raster(200, 200)(drawFunction3).debug) - - val joint9 = (circle2.debug).on(raster(200, 200)(drawFunction3).debug) - val joint10 = (raster(200, 200)(drawFunction3).debug).on(circle2.debug) - val joint11 = (raster(200, 200)(drawFunction4).debug).on(circle2.debug) - val jjoint = (joint9).above(joint10).beside(joint11) - - val circle3 = Picture.circle(200).fillColor(Color.blue).at(100, 100) - val jjoint2 = (circle3.debug).beside(raster(200, 200)(drawFunction3).debug) - - def tri = Picture.triangle(100,50).fillColor(Color.red) - def tri90 = tri.rotate(90.degrees) - def tri180 = tri.rotate(180.degrees) - def tri270 = tri.rotate(270.degrees) - - val joint12 = (tri.above((tri90.beside(raster(200, 200)(drawFunction3))) - .above(tri180))).beside(tri270) - - val drawFunction5 = - (ctx: CanvasRenderingContext2D) => { - ctx.beginPath(); - ctx.moveTo(30, 140); - ctx.lineTo(130, 60); - ctx.lineTo(230, 140); - ctx.closePath(); - ctx.stroke(); - } - - val joint13 = (raster(250, 250)(drawFunction5).debug).beside(circle.debug) def circle = Picture.circle(100).fillColor(Color.red) val drawFunctionImmediate = (ctx: Immediate) => { + ctx.clipRect(0,0,50,50); + ctx.clipArc(0, -50, 75, 0); + ctx.clip(); ctx.rectangle(0, 0, 100, 100); ctx.fill(Color.green); ctx.stroke(Color.red); @@ -116,9 +46,36 @@ object Experiment { ctx.fill(Color.pink); } - val joint5 = (circle).beside(raster(200, 200)(drawFunction5)) + val drawFunctionClosedPath = + (ctx: Immediate) => { + ctx.line(0,0,0,50); + ctx.line(50,50); + ctx.line(50,0); + ctx.line(25,-25, true); + ctx.fill(Color.red) + } + + def drawFunctionPolygon = { + (ctx: Immediate) => { + ctx.pentagon(0,0,50); + ctx.fill(Color.green); + ctx.stroke(Color.blue) + } + } + + def drawFunctionClip = { + (ctx: Immediate) => { + ctx.clipArc(0,0,50,0); + ctx.clipRect(65,65,15,15); + ctx.clip(); + ctx.rectangle(0,0,100,100); + ctx.fill(Color.blue) + } + } + + val joint = (circle).beside(raster(200, 200)(drawFunctionClip)) @JSExport def draw(mount: String) = - joint13.drawWithFrame(Frame(mount)) + joint.drawWithFrame(Frame(mount)) } From 7906eeb52495419132522373476674294548b016 Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Wed, 14 Aug 2024 18:44:53 +0530 Subject: [PATCH 34/43] Added text method --- .../doodle/canvas/algebra/CanvasDrawing.scala | 5 +++++ .../scala/doodle/canvas/algebra/Immediate.scala | 13 ++++++++++--- .../main/scala/doodle/canvas/algebra/Text.scala | 10 ++++++++-- .../scala/doodle/examples/canvas/Experiment.scala | 14 ++++++++++++-- 4 files changed, 35 insertions(+), 7 deletions(-) diff --git a/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala b/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala index a26c7571..eb1f75ed 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala @@ -139,6 +139,11 @@ object CanvasDrawing { f(immediate) } } + + def text(text: String, x: Double, y: Double): CanvasDrawing[Unit] = + CanvasDrawing { ctx => + ctx.fillText(text, x, y) + } def setFill(fill: Option[Fill]): CanvasDrawing[Unit] = fill.map(setFill).getOrElse(unit) diff --git a/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala b/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala index 972ccde9..ed014df2 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala @@ -39,7 +39,7 @@ trait Immediate { def ellipse(x: Double, y: Double, radiusX: Double, radiusY: Double, rotation: Double, startAngle: Double, endAngle: Double): Unit def ellipse(x: Double, y: Double, radiusX: Double, radiusY: Double, rotation: Double, startAngle: Double, endAngle: Double, counterclockwise: Boolean): Unit def fill(color: Color): Unit - def text(text: String, x: Double, y: Double): Unit + def text(text: String, x: Double, y: Double, color: Color = Color.black, font: String = "50px serif"): Unit def line(x: Double, y: Double, closedPath: Boolean = false): Unit def line(x1: Double, y1: Double, x2: Double, y2: Double): Unit def pentagon(x: Double, y: Double, radius: Double): Unit @@ -143,9 +143,16 @@ class ImmediateImpl(ctx: CanvasRenderingContext2D, region: Path2D) extends Immed ctx.fill() } - def text(text: String, x: Double, y: Double): Unit = { + def text(text: String, x: Double, y: Double, color: Color = Color.black, font: String = "50px serif"): Unit = { val x0 = x - text.length * 2 - ctx.fillText(text, x0, y) + ctx.fillStyle = CanvasDrawing.colorToCSS(color) + ctx.font = font; + ctx.beginPath() + ctx.translate(x0, y) + ctx.rotate(Math.PI) + ctx.scale(-1, 1) + ctx.fillText(text, -ctx.measureText(text).width / 2, 0) + ctx.fill() } def line(x: Double, y: Double, closedPath: Boolean = false): Unit = { diff --git a/canvas/src/main/scala/doodle/canvas/algebra/Text.scala b/canvas/src/main/scala/doodle/canvas/algebra/Text.scala index 1338345f..2938d7ae 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/Text.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/Text.scala @@ -18,6 +18,7 @@ package doodle.canvas.algebra import doodle.algebra.Algebra import doodle.algebra.generic.* +import doodle.core.* import doodle.core.BoundingBox import doodle.core.font.Font import doodle.core.{Transform as Tx} @@ -36,8 +37,13 @@ trait Text extends GenericText[CanvasDrawing] { font: Font, text: String, bounds: Bounds - ): CanvasDrawing[Unit] = - ??? + ): CanvasDrawing[Unit] = ??? + // CanvasDrawing.setTransform(tx) >> + // CanvasDrawing.withFill(fill) { + // CanvasDrawing.withStroke(stroke) { + // CanvasDrawing.text(text, bounds.width, bounds.actualBoundingBoxLeft) + // } + // } def textBoundingBox(text: String, font: Font): (BoundingBox, Bounds) = { ??? diff --git a/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala b/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala index a74eac1b..ac453002 100644 --- a/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala +++ b/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala @@ -59,7 +59,7 @@ object Experiment { (ctx: Immediate) => { ctx.pentagon(0,0,50); ctx.fill(Color.green); - ctx.stroke(Color.blue) + ctx.stroke(Color.blue); } } @@ -73,7 +73,17 @@ object Experiment { } } - val joint = (circle).beside(raster(200, 200)(drawFunctionClip)) + def drawFunctionText = { + (ctx: Immediate) => { + ctx.text("Hello, world!", 0, 0, font = "25px serif"); + ctx.rectangle(60,60,20,20); + ctx.fill(Color.yellow) + ctx.rectangle(60,40,20,20); + ctx.fill(Color.blue) + } + } + + val joint = (circle).beside(raster(200, 200)(drawFunctionText)) @JSExport def draw(mount: String) = From ad318409f0230acef1064c3397a5e006ff00a143 Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Fri, 16 Aug 2024 21:23:44 +0530 Subject: [PATCH 35/43] Added dash Shapes and text --- .../doodle/canvas/algebra/Immediate.scala | 63 ++++++++++--------- .../doodle/examples/canvas/Experiment.scala | 3 +- 2 files changed, 35 insertions(+), 31 deletions(-) diff --git a/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala b/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala index ed014df2..de4ad205 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala @@ -23,39 +23,41 @@ import org.scalajs.dom.CanvasRenderingContext2D import org.scalajs.dom.Path2D import scala.annotation.tailrec +import scala.scalajs.js.JSConverters.* + trait Immediate { - def arc(x: Int, y: Int, radius: Int, startAngle: Double, endAngle: Double, closedPath: Boolean = false): Unit + def arc(x: Int, y: Int, radius: Int, startAngle: Double, endAngle: Double, closedPath: Boolean = false, segments: Array[Double] = Array.empty[Double]): Unit def arcTo(x1: Int, y1: Int, x2: Int, y2: Int, radius: Int): Unit - def bezierCurveTo(cp1x: Int, cp1y: Int, cp2x: Int, cp2y: Int, x: Int, y: Int): Unit + def bezierCurveTo(cp1x: Int, cp1y: Int, cp2x: Int, cp2y: Int, x: Int, y: Int, segments: Array[Double] = Array.empty[Double]): Unit def beginPath(): Unit def clearRect(x: Int, y: Int, width: Int, height: Int): Unit def clip(): Unit def clipArc(x: Int, y: Int, radius: Int, startAngle: Double): Unit def clipRect(x: Int, y: Int, width: Int, height: Int): Unit def closePath(): Unit - def circle(x: Double, y: Double, radius: Double): Unit - def ellipse(x: Double, y: Double, width: Double, height: Double): Unit - def ellipse(x: Double, y: Double, radiusX: Double, radiusY: Double, rotation: Double, startAngle: Double, endAngle: Double): Unit - def ellipse(x: Double, y: Double, radiusX: Double, radiusY: Double, rotation: Double, startAngle: Double, endAngle: Double, counterclockwise: Boolean): Unit + def circle(x: Double, y: Double, radius: Double, segments: Array[Double] = Array.empty[Double]): Unit + def dashLine(x1: Double, y1: Double, x2: Double, y2: Double, segments: Array[Double] = Array.empty[Double]): Unit + def ellipse(x: Double, y: Double, width: Double, height: Double, segments: Array[Double] = Array.empty[Double]): Unit + def ellipseWithRotation(x: Double, y: Double, radiusX: Double, radiusY: Double, rotation: Double, startAngle: Double, endAngle: Double, counterclockwise: Boolean = false, segments: Array[Double] = Array.empty[Double]): Unit def fill(color: Color): Unit def text(text: String, x: Double, y: Double, color: Color = Color.black, font: String = "50px serif"): Unit def line(x: Double, y: Double, closedPath: Boolean = false): Unit - def line(x1: Double, y1: Double, x2: Double, y2: Double): Unit - def pentagon(x: Double, y: Double, radius: Double): Unit + def lineTo(x1: Double, y1: Double, x2: Double, y2: Double): Unit + def pentagon(x: Double, y: Double, radius: Double, segments: Array[Double] = Array.empty[Double]): Unit def quadraticCurveTo(cpx: Double, cpy: Double, x: Double, y: Double): Unit - def rectangle(x: Double, y: Double, width: Double, height: Double): Unit + def rectangle(x: Double, y: Double, width: Double, height: Double, segments: Array[Double] = Array.empty[Double]): Unit def rotate(angle: Double): Unit - def roundedRectangle(x: Int, y: Int, width: Int, height: Int, radius: Int): Unit + def roundedRectangle(x: Int, y: Int, width: Int, height: Int, radius: Int, segments: Array[Double] = Array.empty[Double]): Unit def stroke(color: Color): Unit def strokeText(text: String, x: Double, y: Double): Unit def transform(a: Double, b: Double, c: Double, d: Double, e: Double, f: Double): Unit def translate(x: Double, y: Double): Unit - def triangle(x1: Double, y1: Double, x2: Double, y2: Double, x3: Double, y3: Double): Unit + def triangle(x1: Double, y1: Double, x2: Double, y2: Double, x3: Double, y3: Double, segments: Array[Double] = Array.empty[Double]): Unit } class ImmediateImpl(ctx: CanvasRenderingContext2D, region: Path2D) extends Immediate { - def arc(x: Int, y: Int, radius: Int, startAngle: Double, endAngle: Double, closedPath: Boolean = false): Unit = { + def arc(x: Int, y: Int, radius: Int, startAngle: Double, endAngle: Double, closedPath: Boolean = false, segments: Array[Double] = Array.empty[Double]): Unit = { val x0 = x - radius / 2 val y0 = y - radius / 2 ctx.beginPath() @@ -70,7 +72,7 @@ class ImmediateImpl(ctx: CanvasRenderingContext2D, region: Path2D) extends Immed ctx.stroke() } - def bezierCurveTo(cp1x: Int, cp1y: Int, cp2x: Int, cp2y: Int, x: Int, y: Int): Unit = { + def bezierCurveTo(cp1x: Int, cp1y: Int, cp2x: Int, cp2y: Int, x: Int, y: Int, segments: Array[Double] = Array.empty[Double]): Unit = { ctx.beginPath() ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) ctx.stroke() @@ -106,31 +108,32 @@ class ImmediateImpl(ctx: CanvasRenderingContext2D, region: Path2D) extends Immed ctx.closePath() } - def circle(x: Double, y: Double, radius: Double): Unit = { + def circle(x: Double, y: Double, radius: Double, segments: Array[Double] = Array.empty[Double]): Unit = { val x0 = x - radius / 2.0 val y0 = y - radius / 2.0 ctx.beginPath() + ctx.setLineDash(segments.toJSArray) ctx.arc(x0, y0, radius, 0, 2 * Math.PI) ctx.stroke() } - def ellipse(x: Double, y: Double, width: Double, height: Double): Unit = { - val x0 = x - width / 2 - val y0 = y - height / 2 + def dashLine(x1: Double, y1: Double, x2: Double, y2: Double, segments: Array[Double] = Array.empty[Double]): Unit = { ctx.beginPath() - ctx.ellipse(x0, y0, width / 2, height / 2, 0, 0, 2 * Math.PI) - ctx.fill() + ctx.setLineDash(segments.toJSArray) + ctx.moveTo(x1, y1) + ctx.lineTo(x2, y2) + ctx.stroke() } - def ellipse(x: Double, y: Double, radiusX: Double, radiusY: Double, rotation: Double, startAngle: Double, endAngle: Double): Unit = { - val x0 = x - radiusX / 2 - val y0 = y - radiusY / 2 + def ellipse(x: Double, y: Double, width: Double, height: Double, segments: Array[Double] = Array.empty[Double]): Unit = { + val x0 = x - width / 2 + val y0 = y - height / 2 ctx.beginPath() - ctx.ellipse(x0, y0, radiusX, radiusY, rotation, startAngle, endAngle) + ctx.ellipse(x0, y0, width / 2, height / 2, 0, 0, 2 * Math.PI) ctx.fill() } - def ellipse(x: Double, y: Double, radiusX: Double, radiusY: Double, rotation: Double, startAngle: Double, endAngle: Double, counterclockwise: Boolean): Unit = { + def ellipseWithRotation(x: Double, y: Double, radiusX: Double, radiusY: Double, rotation: Double, startAngle: Double, endAngle: Double, counterclockwise: Boolean = false, segments: Array[Double] = Array.empty[Double]): Unit = { val x0 = x - radiusX / 2 val y0 = y - radiusY / 2 ctx.beginPath() @@ -161,17 +164,17 @@ class ImmediateImpl(ctx: CanvasRenderingContext2D, region: Path2D) extends Immed ctx.stroke() } - def line(x1: Double, y1: Double, x2: Double, y2: Double): Unit = { + def lineTo(x1: Double, y1: Double, x2: Double, y2: Double): Unit = { ctx.beginPath(); ctx.moveTo(x1, y1) ctx.lineTo(x2, y2) ctx.stroke() } - def pentagon(x: Double, y: Double, radius: Double): Unit = { + def pentagon(x: Double, y: Double, radius: Double, segments: Array[Double] = Array.empty[Double]): Unit = { val x0 = x - radius / 2 val y0 = y - radius / 2 - + @tailrec def drawSide(i: Int): Unit = { if (i <= 5) { @@ -194,7 +197,7 @@ class ImmediateImpl(ctx: CanvasRenderingContext2D, region: Path2D) extends Immed ctx.stroke() } - def rectangle(x: Double, y: Double, width: Double, height: Double): Unit = { + def rectangle(x: Double, y: Double, width: Double, height: Double, segments: Array[Double] = Array.empty[Double]): Unit = { val x0 = x - width / 2 val y0 = y - height / 2 ctx.beginPath() @@ -205,7 +208,7 @@ class ImmediateImpl(ctx: CanvasRenderingContext2D, region: Path2D) extends Immed ctx.rotate(angle) } - def roundedRectangle(x: Int, y: Int, width: Int, height: Int, radius: Int): Unit = { + def roundedRectangle(x: Int, y: Int, width: Int, height: Int, radius: Int, segments: Array[Double] = Array.empty[Double]): Unit = { val x0 = x - width / 2 val y0 = y - height / 2 ctx.beginPath() @@ -235,7 +238,7 @@ class ImmediateImpl(ctx: CanvasRenderingContext2D, region: Path2D) extends Immed ctx.translate(x, y) } - def triangle(x1: Double, y1: Double, x2: Double, y2: Double, x3: Double, y3: Double): Unit = { + def triangle(x1: Double, y1: Double, x2: Double, y2: Double, x3: Double, y3: Double, segments: Array[Double] = Array.empty[Double]): Unit = { ctx.beginPath() ctx.moveTo(x1, y1) ctx.lineTo(x2, y2) diff --git a/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala b/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala index ac453002..06086515 100644 --- a/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala +++ b/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala @@ -48,7 +48,7 @@ object Experiment { val drawFunctionClosedPath = (ctx: Immediate) => { - ctx.line(0,0,0,50); + ctx.lineTo(0,0,0,50); ctx.line(50,50); ctx.line(50,0); ctx.line(25,-25, true); @@ -76,6 +76,7 @@ object Experiment { def drawFunctionText = { (ctx: Immediate) => { ctx.text("Hello, world!", 0, 0, font = "25px serif"); + ctx.dashLine(0, 0, 200, 200, Array(5,5)); ctx.rectangle(60,60,20,20); ctx.fill(Color.yellow) ctx.rectangle(60,40,20,20); From 167b5726bfaff55bad32c299c02ff3d2dc4269d2 Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Sat, 17 Aug 2024 14:02:08 +0530 Subject: [PATCH 36/43] Added star --- .../doodle/canvas/algebra/CanvasDrawing.scala | 2 +- .../doodle/canvas/algebra/Immediate.scala | 68 +++++++++----- docs/src/pages/canvas/examples.md | 4 +- .../examples/canvas/CanvasImmediateMode.scala | 57 ++++++++++++ .../doodle/examples/canvas/Experiment.scala | 92 ------------------- 5 files changed, 107 insertions(+), 116 deletions(-) create mode 100644 examples/js/src/main/scala/doodle/examples/canvas/CanvasImmediateMode.scala delete mode 100644 examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala diff --git a/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala b/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala index eb1f75ed..1359f18b 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala @@ -135,7 +135,7 @@ object CanvasDrawing { ): CanvasDrawing[Unit] = { CanvasDrawing { ctx => val path = new Path2D() - val immediate = new ImmediateImpl(ctx,path) + val immediate = new ImmediateImpl(width, height, ctx, path) f(immediate) } } diff --git a/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala b/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala index de4ad205..26512a73 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala @@ -41,7 +41,6 @@ trait Immediate { def ellipse(x: Double, y: Double, width: Double, height: Double, segments: Array[Double] = Array.empty[Double]): Unit def ellipseWithRotation(x: Double, y: Double, radiusX: Double, radiusY: Double, rotation: Double, startAngle: Double, endAngle: Double, counterclockwise: Boolean = false, segments: Array[Double] = Array.empty[Double]): Unit def fill(color: Color): Unit - def text(text: String, x: Double, y: Double, color: Color = Color.black, font: String = "50px serif"): Unit def line(x: Double, y: Double, closedPath: Boolean = false): Unit def lineTo(x1: Double, y1: Double, x2: Double, y2: Double): Unit def pentagon(x: Double, y: Double, radius: Double, segments: Array[Double] = Array.empty[Double]): Unit @@ -49,17 +48,19 @@ trait Immediate { def rectangle(x: Double, y: Double, width: Double, height: Double, segments: Array[Double] = Array.empty[Double]): Unit def rotate(angle: Double): Unit def roundedRectangle(x: Int, y: Int, width: Int, height: Int, radius: Int, segments: Array[Double] = Array.empty[Double]): Unit + def star(x: Double, y: Double, radius: Double, segments: Array[Double] = Array.empty[Double]): Unit def stroke(color: Color): Unit def strokeText(text: String, x: Double, y: Double): Unit + def text(text: String, x: Double, y: Double, color: Color = Color.black, font: String = "25px serif"): Unit def transform(a: Double, b: Double, c: Double, d: Double, e: Double, f: Double): Unit def translate(x: Double, y: Double): Unit def triangle(x1: Double, y1: Double, x2: Double, y2: Double, x3: Double, y3: Double, segments: Array[Double] = Array.empty[Double]): Unit } -class ImmediateImpl(ctx: CanvasRenderingContext2D, region: Path2D) extends Immediate { +class ImmediateImpl(rasterHeight: Int, rasterWidth: Int, ctx: CanvasRenderingContext2D, region: Path2D) extends Immediate { def arc(x: Int, y: Int, radius: Int, startAngle: Double, endAngle: Double, closedPath: Boolean = false, segments: Array[Double] = Array.empty[Double]): Unit = { - val x0 = x - radius / 2 - val y0 = y - radius / 2 + val x0 = x - rasterWidth / 2 + val y0 = y - rasterHeight / 2 ctx.beginPath() ctx.arc(x0, y0, radius, startAngle, endAngle) if(closedPath) ctx.closePath(); @@ -93,14 +94,14 @@ class ImmediateImpl(ctx: CanvasRenderingContext2D, region: Path2D) extends Immed } def clipArc(x: Int, y: Int, radius: Int, startAngle: Double): Unit = { - val x0 = x - radius / 2 - val y0 = y - radius / 2 + val x0 = x - rasterWidth / 2 + val y0 = y - rasterHeight / 2 region.arc(x0, y0, radius, startAngle, 2 * Math.PI) } def clipRect(x: Int, y: Int, width: Int, height: Int): Unit = { - val x0 = x - width / 2 - val y0 = y - height / 2 + val x0 = x - rasterWidth / 2 + val y0 = y - rasterHeight / 2 region.rect(x0, y0, width, height) } @@ -109,8 +110,8 @@ class ImmediateImpl(ctx: CanvasRenderingContext2D, region: Path2D) extends Immed } def circle(x: Double, y: Double, radius: Double, segments: Array[Double] = Array.empty[Double]): Unit = { - val x0 = x - radius / 2.0 - val y0 = y - radius / 2.0 + val x0 = x - rasterWidth / 2.0 + val y0 = y - rasterHeight / 2.0 ctx.beginPath() ctx.setLineDash(segments.toJSArray) ctx.arc(x0, y0, radius, 0, 2 * Math.PI) @@ -126,16 +127,16 @@ class ImmediateImpl(ctx: CanvasRenderingContext2D, region: Path2D) extends Immed } def ellipse(x: Double, y: Double, width: Double, height: Double, segments: Array[Double] = Array.empty[Double]): Unit = { - val x0 = x - width / 2 - val y0 = y - height / 2 + val x0 = x - rasterWidth / 2 + val y0 = y - rasterHeight / 2 ctx.beginPath() ctx.ellipse(x0, y0, width / 2, height / 2, 0, 0, 2 * Math.PI) ctx.fill() } def ellipseWithRotation(x: Double, y: Double, radiusX: Double, radiusY: Double, rotation: Double, startAngle: Double, endAngle: Double, counterclockwise: Boolean = false, segments: Array[Double] = Array.empty[Double]): Unit = { - val x0 = x - radiusX / 2 - val y0 = y - radiusY / 2 + val x0 = x - rasterWidth / 2 + val y0 = y - rasterHeight / 2 ctx.beginPath() ctx.ellipse(x0, y0, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise) ctx.fill() @@ -146,7 +147,7 @@ class ImmediateImpl(ctx: CanvasRenderingContext2D, region: Path2D) extends Immed ctx.fill() } - def text(text: String, x: Double, y: Double, color: Color = Color.black, font: String = "50px serif"): Unit = { + def text(text: String, x: Double, y: Double, color: Color = Color.black, font: String = "25px serif"): Unit = { val x0 = x - text.length * 2 ctx.fillStyle = CanvasDrawing.colorToCSS(color) ctx.font = font; @@ -188,7 +189,6 @@ class ImmediateImpl(ctx: CanvasRenderingContext2D, region: Path2D) extends Immed ctx.beginPath() ctx.moveTo(x0 + radius, y0) drawSide(1) - //ctx.fill() } def quadraticCurveTo(cpx: Double, cpy: Double, x: Double, y: Double): Unit = { @@ -198,8 +198,8 @@ class ImmediateImpl(ctx: CanvasRenderingContext2D, region: Path2D) extends Immed } def rectangle(x: Double, y: Double, width: Double, height: Double, segments: Array[Double] = Array.empty[Double]): Unit = { - val x0 = x - width / 2 - val y0 = y - height / 2 + val x0 = x - rasterWidth / 2 + val y0 = y - rasterHeight / 2 ctx.beginPath() ctx.rect(x0, y0, width, height) } @@ -209,8 +209,8 @@ class ImmediateImpl(ctx: CanvasRenderingContext2D, region: Path2D) extends Immed } def roundedRectangle(x: Int, y: Int, width: Int, height: Int, radius: Int, segments: Array[Double] = Array.empty[Double]): Unit = { - val x0 = x - width / 2 - val y0 = y - height / 2 + val x0 = x - rasterWidth / 2 + val y0 = y - rasterHeight / 2 ctx.beginPath() ctx.moveTo(x0 + radius, y0) ctx.arcTo(x0 + width, y0, x0 + width, y0 + height, radius) @@ -220,13 +220,39 @@ class ImmediateImpl(ctx: CanvasRenderingContext2D, region: Path2D) extends Immed ctx.fill() } + def star(x1: Double, y1: Double, radius: Double, segments: Array[Double] = Array.empty[Double]): Unit = { + val outerRadius = radius + val innerRadius = radius / 2 + val angle = Math.PI / 5 + val x = x1 - rasterWidth/2 + val y = y1 - rasterHeight/2 + + ctx.beginPath() + ctx.moveTo( + x + Math.cos(0) * outerRadius, + y + Math.sin(0) * outerRadius + ) + + for (i <- 1 to 10) { + val isOuter = i % 2 == 0 + val r = if (isOuter) outerRadius else innerRadius + val a = i * angle + ctx.lineTo( + x + Math.cos(a) * r, + y + Math.sin(a) * r + ) + } + ctx.closePath() + ctx.fill() + } + def stroke(color: Color): Unit = { ctx.strokeStyle = CanvasDrawing.colorToCSS(color) ctx.stroke() } def strokeText(text: String, x: Double, y: Double): Unit = { - val x0 = x - text.length * 2 + val x0 = x - rasterWidth * 2 ctx.strokeText(text, x0, y) } diff --git a/docs/src/pages/canvas/examples.md b/docs/src/pages/canvas/examples.md index 74f971a6..f1a38b9d 100644 --- a/docs/src/pages/canvas/examples.md +++ b/docs/src/pages/canvas/examples.md @@ -12,9 +12,9 @@ The source for these examples is [in the repository](https://github.com/creative @:doodle("parametric-spiral", "CanvasParametricSpiral.draw") -## Experiment +## Canvas Immediate Mode -@:doodle("Experiment", "Experiment.draw") +@:doodle("CanvasImmediateMode", "CanvasImmediateMode.draw") ## Frame Background diff --git a/examples/js/src/main/scala/doodle/examples/canvas/CanvasImmediateMode.scala b/examples/js/src/main/scala/doodle/examples/canvas/CanvasImmediateMode.scala new file mode 100644 index 00000000..f40c8254 --- /dev/null +++ b/examples/js/src/main/scala/doodle/examples/canvas/CanvasImmediateMode.scala @@ -0,0 +1,57 @@ +/* + * 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.examples.canvas + +import cats.effect.unsafe.implicits.global +import doodle.canvas.{*, given} +import doodle.canvas.algebra.Immediate +import doodle.core.* +import doodle.syntax.all.* +import org.scalajs.dom.CanvasRenderingContext2D + +import scala.scalajs.js.annotation.* + +@JSExportTopLevel("CanvasImmediateMode") +object Experiment { + + def roof = + Picture + .triangle(200,100) + .fillColor(Color.blue) + .strokeColor(Color.black).debug + + def drawFunction = + (ctx: Immediate) => { + ctx.rectangle(25, 0, 150, 200) + ctx.fill(Color.brown) + ctx.stroke(Color.black) + ctx.rectangle(75,0,40,50) + ctx.fill(Color.black) + ctx.ellipse(100, 175, 40, 20) + ctx.fill(Color.blue) + ctx.stroke(Color.black) + //ctx.text("Doodle !!", 0, 0) + ctx.star(100,100,25) + ctx.fill(Color.yellow) + } + + val joint = roof.above(raster(200, 200)(drawFunction)) + + @JSExport + def draw(mount: String) = + joint.drawWithFrame(Frame(mount)) +} diff --git a/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala b/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala deleted file mode 100644 index 06086515..00000000 --- a/examples/js/src/main/scala/doodle/examples/canvas/Experiment.scala +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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.examples.canvas - -import cats.effect.unsafe.implicits.global -import doodle.canvas.{*, given} -import doodle.canvas.algebra.Immediate -import doodle.core.* -import doodle.syntax.all.* -import org.scalajs.dom.CanvasRenderingContext2D - -import scala.scalajs.js.annotation.* - -@JSExportTopLevel("Experiment") -object Experiment { - - def circle = Picture.circle(100).fillColor(Color.red) - - val drawFunctionImmediate = - (ctx: Immediate) => { - ctx.clipRect(0,0,50,50); - ctx.clipArc(0, -50, 75, 0); - ctx.clip(); - ctx.rectangle(0, 0, 100, 100); - ctx.fill(Color.green); - ctx.stroke(Color.red); - ctx.triangle(-50, 50, 50, 50, 0, 80); - ctx.fill(Color.blue); - ctx.transform(1, 0.2, 0.8, 1, 0, 0); - ctx.translate(10,10); - ctx.ellipse(0,0,50,30); - ctx.fill(Color.pink); - } - - val drawFunctionClosedPath = - (ctx: Immediate) => { - ctx.lineTo(0,0,0,50); - ctx.line(50,50); - ctx.line(50,0); - ctx.line(25,-25, true); - ctx.fill(Color.red) - } - - def drawFunctionPolygon = { - (ctx: Immediate) => { - ctx.pentagon(0,0,50); - ctx.fill(Color.green); - ctx.stroke(Color.blue); - } - } - - def drawFunctionClip = { - (ctx: Immediate) => { - ctx.clipArc(0,0,50,0); - ctx.clipRect(65,65,15,15); - ctx.clip(); - ctx.rectangle(0,0,100,100); - ctx.fill(Color.blue) - } - } - - def drawFunctionText = { - (ctx: Immediate) => { - ctx.text("Hello, world!", 0, 0, font = "25px serif"); - ctx.dashLine(0, 0, 200, 200, Array(5,5)); - ctx.rectangle(60,60,20,20); - ctx.fill(Color.yellow) - ctx.rectangle(60,40,20,20); - ctx.fill(Color.blue) - } - } - - val joint = (circle).beside(raster(200, 200)(drawFunctionText)) - - @JSExport - def draw(mount: String) = - joint.drawWithFrame(Frame(mount)) -} From 66ca33615373e6bf1d5978e6b9e48d147a93dc9e Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Sun, 18 Aug 2024 03:40:17 +0530 Subject: [PATCH 37/43] Added canvasImmediate Landscape example and few fixes --- .../doodle/canvas/algebra/Immediate.scala | 58 +++-- .../examples/canvas/CanvasImmediateMode.scala | 213 ++++++++++++++++-- 2 files changed, 240 insertions(+), 31 deletions(-) diff --git a/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala b/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala index 26512a73..a35cad70 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala @@ -27,19 +27,20 @@ import scala.scalajs.js.JSConverters.* trait Immediate { - def arc(x: Int, y: Int, radius: Int, startAngle: Double, endAngle: Double, closedPath: Boolean = false, segments: Array[Double] = Array.empty[Double]): Unit - def arcTo(x1: Int, y1: Int, x2: Int, y2: Int, radius: Int): Unit + def arc(x: Int, y: Int, radius: Int, startAngle: Double, endAngle: Double, counterclockwise: Boolean = false, closedPath: Boolean = false, segments: Array[Double] = Array.empty[Double]): Unit + def arcTo(x1: Int, y1: Int, x2: Int, y2: Int, radius: Int, counterclockwise: Boolean = false, closedPath: Boolean = false, segments: Array[Double] = Array.empty[Double]): Unit def bezierCurveTo(cp1x: Int, cp1y: Int, cp2x: Int, cp2y: Int, x: Int, y: Int, segments: Array[Double] = Array.empty[Double]): Unit def beginPath(): Unit def clearRect(x: Int, y: Int, width: Int, height: Int): Unit def clip(): Unit - def clipArc(x: Int, y: Int, radius: Int, startAngle: Double): Unit + def clipArc(x: Int, y: Int, radius: Int, startAngle: Double = 0, endAngle: Double = 2 * Math.PI, counterclockwise: Boolean = false): Unit def clipRect(x: Int, y: Int, width: Int, height: Int): Unit def closePath(): Unit def circle(x: Double, y: Double, radius: Double, segments: Array[Double] = Array.empty[Double]): Unit def dashLine(x1: Double, y1: Double, x2: Double, y2: Double, segments: Array[Double] = Array.empty[Double]): Unit def ellipse(x: Double, y: Double, width: Double, height: Double, segments: Array[Double] = Array.empty[Double]): Unit def ellipseWithRotation(x: Double, y: Double, radiusX: Double, radiusY: Double, rotation: Double, startAngle: Double, endAngle: Double, counterclockwise: Boolean = false, segments: Array[Double] = Array.empty[Double]): Unit + def endClip(): Unit def fill(color: Color): Unit def line(x: Double, y: Double, closedPath: Boolean = false): Unit def lineTo(x1: Double, y1: Double, x2: Double, y2: Double): Unit @@ -51,23 +52,24 @@ trait Immediate { def star(x: Double, y: Double, radius: Double, segments: Array[Double] = Array.empty[Double]): Unit def stroke(color: Color): Unit def strokeText(text: String, x: Double, y: Double): Unit + def square(x: Double, y: Double, size: Double, segments: Array[Double] = Array.empty[Double]): Unit def text(text: String, x: Double, y: Double, color: Color = Color.black, font: String = "25px serif"): Unit def transform(a: Double, b: Double, c: Double, d: Double, e: Double, f: Double): Unit def translate(x: Double, y: Double): Unit def triangle(x1: Double, y1: Double, x2: Double, y2: Double, x3: Double, y3: Double, segments: Array[Double] = Array.empty[Double]): Unit } -class ImmediateImpl(rasterHeight: Int, rasterWidth: Int, ctx: CanvasRenderingContext2D, region: Path2D) extends Immediate { - def arc(x: Int, y: Int, radius: Int, startAngle: Double, endAngle: Double, closedPath: Boolean = false, segments: Array[Double] = Array.empty[Double]): Unit = { +class ImmediateImpl(rasterWidth: Int, rasterHeight: Int, ctx: CanvasRenderingContext2D, region: Path2D) extends Immediate { + def arc(x: Int, y: Int, radius: Int, startAngle: Double, endAngle: Double, counterclockwise: Boolean = false, closedPath: Boolean = false, segments: Array[Double] = Array.empty[Double]): Unit = { val x0 = x - rasterWidth / 2 val y0 = y - rasterHeight / 2 ctx.beginPath() - ctx.arc(x0, y0, radius, startAngle, endAngle) + ctx.arc(x0, y0, radius, startAngle, endAngle, counterclockwise) if(closedPath) ctx.closePath(); ctx.stroke() } - def arcTo(x1: Int, y1: Int, x2: Int, y2: Int, radius: Int): Unit = { + def arcTo(x1: Int, y1: Int, x2: Int, y2: Int, radius: Int, counterclockwise: Boolean = false, closedPath: Boolean = false, segments: Array[Double] = Array.empty[Double]): Unit = { ctx.beginPath() ctx.arcTo(x1, y1, x2, y2, radius) ctx.stroke() @@ -90,13 +92,14 @@ class ImmediateImpl(rasterHeight: Int, rasterWidth: Int, ctx: CanvasRenderingCon } def clip(): Unit = { + ctx.save() ctx.clip(region) } - def clipArc(x: Int, y: Int, radius: Int, startAngle: Double): Unit = { + def clipArc(x: Int, y: Int, radius: Int, startAngle: Double = 0, endAngle: Double = 2 * Math.PI, counterclockwise: Boolean = false): Unit = { val x0 = x - rasterWidth / 2 val y0 = y - rasterHeight / 2 - region.arc(x0, y0, radius, startAngle, 2 * Math.PI) + region.arc(x0, y0, radius, startAngle, endAngle, counterclockwise) } def clipRect(x: Int, y: Int, width: Int, height: Int): Unit = { @@ -119,10 +122,14 @@ class ImmediateImpl(rasterHeight: Int, rasterWidth: Int, ctx: CanvasRenderingCon } def dashLine(x1: Double, y1: Double, x2: Double, y2: Double, segments: Array[Double] = Array.empty[Double]): Unit = { + val x0 = x1 - rasterWidth / 2 + val y0 = y1 - rasterHeight / 2 + val x = x2 - rasterWidth / 2 + val y = y2 - rasterHeight / 2 ctx.beginPath() ctx.setLineDash(segments.toJSArray) - ctx.moveTo(x1, y1) - ctx.lineTo(x2, y2) + ctx.moveTo(x0, y0) + ctx.lineTo(x, y) ctx.stroke() } @@ -142,33 +149,44 @@ class ImmediateImpl(rasterHeight: Int, rasterWidth: Int, ctx: CanvasRenderingCon ctx.fill() } + def endClip(): Unit = { + ctx.restore() + } + def fill(color: Color): Unit = { ctx.fillStyle = CanvasDrawing.colorToCSS(color) ctx.fill() } def text(text: String, x: Double, y: Double, color: Color = Color.black, font: String = "25px serif"): Unit = { - val x0 = x - text.length * 2 + ctx.save() + val x0 = x - rasterWidth / 2 + val y0 = y - rasterHeight / 2 ctx.fillStyle = CanvasDrawing.colorToCSS(color) ctx.font = font; ctx.beginPath() - ctx.translate(x0, y) + ctx.translate(x0, y0) ctx.rotate(Math.PI) ctx.scale(-1, 1) ctx.fillText(text, -ctx.measureText(text).width / 2, 0) ctx.fill() + ctx.restore() } def line(x: Double, y: Double, closedPath: Boolean = false): Unit = { - ctx.lineTo(x,y) + ctx.lineTo(x - rasterWidth / 2,y - rasterHeight / 2) if(closedPath) ctx.closePath() ctx.stroke() } def lineTo(x1: Double, y1: Double, x2: Double, y2: Double): Unit = { + val x0 = x1 - rasterWidth / 2 + val y0 = y1 - rasterHeight / 2 + val x = x2 - rasterWidth / 2 + val y = y2 - rasterHeight / 2 ctx.beginPath(); - ctx.moveTo(x1, y1) - ctx.lineTo(x2, y2) + ctx.moveTo(x0, y0) + ctx.lineTo(x, y) ctx.stroke() } @@ -256,6 +274,14 @@ class ImmediateImpl(rasterHeight: Int, rasterWidth: Int, ctx: CanvasRenderingCon ctx.strokeText(text, x0, y) } + def square(x: Double, y: Double, size: Double, segments: Array[Double] = Array.empty[Double]): Unit = { + val x0 = x - rasterWidth / 2 + val y0 = y - rasterHeight / 2 + ctx.beginPath() + ctx.rect(x0, y0, size, size) + ctx.stroke() + } + def transform(a: Double, b: Double, c: Double, d: Double, e: Double, f: Double): Unit = { ctx.transform(a, b, c, d, e, f) } diff --git a/examples/js/src/main/scala/doodle/examples/canvas/CanvasImmediateMode.scala b/examples/js/src/main/scala/doodle/examples/canvas/CanvasImmediateMode.scala index f40c8254..e125d1bf 100644 --- a/examples/js/src/main/scala/doodle/examples/canvas/CanvasImmediateMode.scala +++ b/examples/js/src/main/scala/doodle/examples/canvas/CanvasImmediateMode.scala @@ -22,6 +22,7 @@ import doodle.canvas.algebra.Immediate import doodle.core.* import doodle.syntax.all.* import org.scalajs.dom.CanvasRenderingContext2D +import scala.annotation.tailrec import scala.scalajs.js.annotation.* @@ -34,22 +35,204 @@ object Experiment { .fillColor(Color.blue) .strokeColor(Color.black).debug - def drawFunction = - (ctx: Immediate) => { - ctx.rectangle(25, 0, 150, 200) - ctx.fill(Color.brown) - ctx.stroke(Color.black) - ctx.rectangle(75,0,40,50) - ctx.fill(Color.black) - ctx.ellipse(100, 175, 40, 20) - ctx.fill(Color.blue) - ctx.stroke(Color.black) - //ctx.text("Doodle !!", 0, 0) - ctx.star(100,100,25) - ctx.fill(Color.yellow) + def drawHotel = + (ctx: Immediate) => { + + ctx.rectangle(25, 0, 150, 200) + ctx.fill(Color.rgb(142, 113, 88)) + ctx.stroke(Color.black) + + ctx.rectangle(45, 0, 20, 40) + ctx.fill(Color.rgb(72, 36, 20)) + @tailrec + def drawWindows(index: Int): Unit = { + if (index < 12) { + val row = index / 4 + val col = index % 4 + val x = 30 + col * 40 + val y = 160 - row * 40 + + val color = (index, col) match { + case (0 | 1 | 4 | 5 | 8, _) => Color.rgb(255, 174, 0) + case (2 | 3 | 6 | 9, _) => Color.black + case _ => Color.rgb(255, 174, 0) + } + + ctx.square(x, y, 20) + ctx.fill(color) + + drawWindows(index + 1) + } } - - val joint = roof.above(raster(200, 200)(drawFunction)) + + drawWindows(0) + + ctx.stroke(Color.black) + ctx.rectangle(175, 0, 50, 200) + ctx.fill(Color.rgb(105, 66, 43)) + @tailrec + def drawLines(y: Int): Unit = { + if (y >= 0) { + ctx.lineTo(175, y, 225, y) + drawLines(y - 25) + } + } + + drawLines(165) + + ctx.ellipse(130, 35, 75, 50, Array(5, 5)) + ctx.fill(Color.rgb(234, 215, 163)) + + ctx.text("HOTEL", 130, 32, font = "20px Fraktur") + } + +def drawApartments = + (ctx: Immediate) => { + @tailrec + def drawBuildings(index: Int): Unit = { + if (index < 5) { + val x = index * 50 + val height = index match { + case 0 => 200 + case 1 | 3 => 150 + case 2 => 175 + case 4 => 200 + } + + ctx.rectangle(x, 0, 50, height) + ctx.fill(Color.rgb(178, 165, 148)) + ctx.stroke(Color.black) + + @tailrec + def drawWindows(row: Int, col: Int): Unit = { + if (row < 4 && col < 2) { + val windowX = x + col * 20 + 5 + val windowY = height - (row + 1) * 40 + 10 + if(windowY > 60) { + ctx.rectangle(windowX, windowY, 15, 25) + ctx.fill(Color.rgb(220, 220, 220)) + ctx.stroke(Color.black) + } + if (col < 1) drawWindows(row, col + 1) + else drawWindows(row + 1, 0) + } + } + + drawWindows(0, 0) + drawBuildings(index + 1) + } + } + + @tailrec + def drawShops(index: Int): Unit = { + if (index < 5) { + val x = index * 50 + val color = index match { + case 0 => Color.red + case 1 => Color.rgb(39, 89, 70) + case 2 => Color.rgb(59, 31, 92) + case 3 => Color.rgb(133, 36, 81) + case 4 => Color.rgb(59, 31, 92) + } + + ctx.rectangle(x, 40, 50, 10) + ctx.fill(color) + ctx.stroke(Color.black) + + ctx.rectangle(x, 0, 50, 40) + ctx.fill(Color.rgb(204, 239, 234)) + ctx.stroke(Color.black) + + drawShops(index + 1) + } + } + + drawBuildings(0) + drawShops(0) + + ctx.clipRect(150, 150, 50, 50) + ctx.clipRect(50, 150, 50, 50) + ctx.clip() + + ctx.circle(100, 150, 50) + ctx.fill(Color.rgb(178, 165, 148)) + ctx.stroke(Color.black) + ctx.circle(150, 150, 50) + ctx.fill(Color.rgb(178, 165, 148)) + ctx.stroke(Color.black) + + ctx.endClip() + + ctx.lineTo(100, 150, 100, 200) + ctx.lineTo(100, 150, 50, 150) + ctx.lineTo(150, 150, 150, 200) + ctx.lineTo(150, 150, 200, 150) + } + + def drawCitySpace = + (ctx: Immediate) => + { + ctx.rectangle(0, 0, 130, 200) + ctx.fill(Color.rgb(185, 185, 185)) + ctx.stroke(Color.black) + ctx.rectangle(10, 130, 50, 60) + ctx.fill(Color.rgb(162, 0, 0)) + ctx.stroke(Color.black) + ctx.rectangle(70, 130, 50, 60) + ctx.fill(Color.black); + ctx.text("CITY SPACE", 65, 100, font = "20px Fraktur") + + ctx.rectangle(10, 60, 110, 30) + ctx.fill(Color.rgb(204, 239, 234)) + ctx.stroke(Color.black) + + ctx.rectangle(10, 20, 30, 30) + ctx.fill(Color.rgb(204, 239, 234)) + ctx.stroke(Color.black) + ctx.rectangle(50, 0, 30, 50) + ctx.fill(Color.rgb(204, 239, 234)) + ctx.stroke(Color.black) + ctx.rectangle(90, 20, 30, 30) + ctx.fill(Color.rgb(204, 239, 234)) + ctx.stroke(Color.black) + } + + def drawRoad = + (ctx: Immediate) => + { + ctx.rectangle(0, 0, 800, 50) + ctx.fill(Color.rgb(135,135,144)) + ctx.stroke(Color.black) + ctx.dashLine(0, 25, 800, 25, Array(5, 5)) + } + + val citySpace = raster(150, 200)(drawCitySpace) + val hotel = raster(240, 200)(drawHotel) + val apartments = raster(250, 200)(drawApartments) + val road = raster(800, 50)(drawRoad) + + val tree = + Picture + .triangle(100, 100) + .fillColor(Color.green) + .strokeColor(Color.black) + .above( + Picture + .rectangle(20, 50) + .fillColor(Color.brown) + .strokeColor(Color.black) + .beside( + Picture + .rectangle(20, 50) + .fillColor(Color.brown) + .strokeColor(Color.black) + ) + ) + .at(0,-120) + .scale(0.5, 0.5) + + + val joint = (citySpace.beside(apartments.beside(hotel.beside(tree).beside(tree)))).above(road) @JSExport def draw(mount: String) = From ec3054a8db530d3498c8b2b7b1d164488203e4ce Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Sun, 18 Aug 2024 17:26:26 +0530 Subject: [PATCH 38/43] Formatted the code --- .../doodle/canvas/algebra/CanvasAlgebra.scala | 3 +- .../doodle/canvas/algebra/CanvasDrawing.scala | 6 +- .../doodle/canvas/algebra/Immediate.scala | 335 ++++++++++++++--- .../scala/doodle/canvas/algebra/Raster.scala | 1 - .../scala/doodle/canvas/algebra/Text.scala | 6 - .../main/scala/doodle/algebra/Raster.scala | 2 - .../algebra/generic/GenericRaster.scala | 4 +- .../scala/doodle/syntax/RasterSyntax.scala | 2 +- .../examples/canvas/CanvasImmediateMode.scala | 345 +++++++++--------- 9 files changed, 458 insertions(+), 246 deletions(-) diff --git a/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala b/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala index 0b2875f9..b994f625 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/CanvasAlgebra.scala @@ -20,7 +20,7 @@ import cats.Apply import cats.Eval import cats.Functor import cats.Monad -import doodle.algebra.generic._ +import doodle.algebra.generic.* import doodle.core.BoundingBox import org.scalajs.dom.CanvasRenderingContext2D @@ -77,4 +77,3 @@ final case class CanvasAlgebra( } } } - diff --git a/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala b/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala index 1359f18b..1e874e4b 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala @@ -32,7 +32,7 @@ import doodle.core.OpenPath import doodle.core.PathElement.BezierCurveTo import doodle.core.PathElement.LineTo import doodle.core.PathElement.MoveTo -import doodle.core.Transform +import doodle.core.Transform import doodle.core.font.Font import doodle.core.font.FontFamily import doodle.core.font.FontSize @@ -131,7 +131,7 @@ object CanvasDrawing { } def raster(width: Int, height: Int)( - f: Immediate => Unit + f: Immediate => Unit ): CanvasDrawing[Unit] = { CanvasDrawing { ctx => val path = new Path2D() @@ -144,7 +144,7 @@ object CanvasDrawing { CanvasDrawing { ctx => ctx.fillText(text, x, y) } - + def setFill(fill: Option[Fill]): CanvasDrawing[Unit] = fill.map(setFill).getOrElse(unit) diff --git a/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala b/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala index a35cad70..1b3df122 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala @@ -25,57 +25,196 @@ import scala.annotation.tailrec import scala.scalajs.js.JSConverters.* - trait Immediate { - def arc(x: Int, y: Int, radius: Int, startAngle: Double, endAngle: Double, counterclockwise: Boolean = false, closedPath: Boolean = false, segments: Array[Double] = Array.empty[Double]): Unit - def arcTo(x1: Int, y1: Int, x2: Int, y2: Int, radius: Int, counterclockwise: Boolean = false, closedPath: Boolean = false, segments: Array[Double] = Array.empty[Double]): Unit - def bezierCurveTo(cp1x: Int, cp1y: Int, cp2x: Int, cp2y: Int, x: Int, y: Int, segments: Array[Double] = Array.empty[Double]): Unit + def arc( + x: Int, + y: Int, + radius: Int, + startAngle: Double, + endAngle: Double, + counterclockwise: Boolean = false, + closedPath: Boolean = false, + segments: Array[Double] = Array.empty[Double] + ): Unit + def arcTo( + x1: Int, + y1: Int, + x2: Int, + y2: Int, + radius: Int, + counterclockwise: Boolean = false, + closedPath: Boolean = false, + segments: Array[Double] = Array.empty[Double] + ): Unit + def bezierCurveTo( + cp1x: Int, + cp1y: Int, + cp2x: Int, + cp2y: Int, + x: Int, + y: Int, + segments: Array[Double] = Array.empty[Double] + ): Unit def beginPath(): Unit def clearRect(x: Int, y: Int, width: Int, height: Int): Unit def clip(): Unit - def clipArc(x: Int, y: Int, radius: Int, startAngle: Double = 0, endAngle: Double = 2 * Math.PI, counterclockwise: Boolean = false): Unit + def clipArc( + x: Int, + y: Int, + radius: Int, + startAngle: Double = 0, + endAngle: Double = 2 * Math.PI, + counterclockwise: Boolean = false + ): Unit def clipRect(x: Int, y: Int, width: Int, height: Int): Unit def closePath(): Unit - def circle(x: Double, y: Double, radius: Double, segments: Array[Double] = Array.empty[Double]): Unit - def dashLine(x1: Double, y1: Double, x2: Double, y2: Double, segments: Array[Double] = Array.empty[Double]): Unit - def ellipse(x: Double, y: Double, width: Double, height: Double, segments: Array[Double] = Array.empty[Double]): Unit - def ellipseWithRotation(x: Double, y: Double, radiusX: Double, radiusY: Double, rotation: Double, startAngle: Double, endAngle: Double, counterclockwise: Boolean = false, segments: Array[Double] = Array.empty[Double]): Unit + def circle( + x: Double, + y: Double, + radius: Double, + segments: Array[Double] = Array.empty[Double] + ): Unit + def dashLine( + x1: Double, + y1: Double, + x2: Double, + y2: Double, + segments: Array[Double] = Array.empty[Double] + ): Unit + def ellipse( + x: Double, + y: Double, + width: Double, + height: Double, + segments: Array[Double] = Array.empty[Double] + ): Unit + def ellipseWithRotation( + x: Double, + y: Double, + radiusX: Double, + radiusY: Double, + rotation: Double, + startAngle: Double, + endAngle: Double, + counterclockwise: Boolean = false, + segments: Array[Double] = Array.empty[Double] + ): Unit def endClip(): Unit def fill(color: Color): Unit def line(x: Double, y: Double, closedPath: Boolean = false): Unit def lineTo(x1: Double, y1: Double, x2: Double, y2: Double): Unit - def pentagon(x: Double, y: Double, radius: Double, segments: Array[Double] = Array.empty[Double]): Unit + def pentagon( + x: Double, + y: Double, + radius: Double, + segments: Array[Double] = Array.empty[Double] + ): Unit def quadraticCurveTo(cpx: Double, cpy: Double, x: Double, y: Double): Unit - def rectangle(x: Double, y: Double, width: Double, height: Double, segments: Array[Double] = Array.empty[Double]): Unit + def rectangle( + x: Double, + y: Double, + width: Double, + height: Double, + segments: Array[Double] = Array.empty[Double] + ): Unit def rotate(angle: Double): Unit - def roundedRectangle(x: Int, y: Int, width: Int, height: Int, radius: Int, segments: Array[Double] = Array.empty[Double]): Unit - def star(x: Double, y: Double, radius: Double, segments: Array[Double] = Array.empty[Double]): Unit + def roundedRectangle( + x: Int, + y: Int, + width: Int, + height: Int, + radius: Int, + segments: Array[Double] = Array.empty[Double] + ): Unit + def star( + x: Double, + y: Double, + radius: Double, + segments: Array[Double] = Array.empty[Double] + ): Unit def stroke(color: Color): Unit def strokeText(text: String, x: Double, y: Double): Unit - def square(x: Double, y: Double, size: Double, segments: Array[Double] = Array.empty[Double]): Unit - def text(text: String, x: Double, y: Double, color: Color = Color.black, font: String = "25px serif"): Unit - def transform(a: Double, b: Double, c: Double, d: Double, e: Double, f: Double): Unit + def square( + x: Double, + y: Double, + size: Double, + segments: Array[Double] = Array.empty[Double] + ): Unit + def text( + text: String, + x: Double, + y: Double, + color: Color = Color.black, + font: String = "25px serif" + ): Unit + def transform( + a: Double, + b: Double, + c: Double, + d: Double, + e: Double, + f: Double + ): Unit def translate(x: Double, y: Double): Unit - def triangle(x1: Double, y1: Double, x2: Double, y2: Double, x3: Double, y3: Double, segments: Array[Double] = Array.empty[Double]): Unit + def triangle( + x1: Double, + y1: Double, + x2: Double, + y2: Double, + x3: Double, + y3: Double, + segments: Array[Double] = Array.empty[Double] + ): Unit } -class ImmediateImpl(rasterWidth: Int, rasterHeight: Int, ctx: CanvasRenderingContext2D, region: Path2D) extends Immediate { - def arc(x: Int, y: Int, radius: Int, startAngle: Double, endAngle: Double, counterclockwise: Boolean = false, closedPath: Boolean = false, segments: Array[Double] = Array.empty[Double]): Unit = { +class ImmediateImpl( + rasterWidth: Int, + rasterHeight: Int, + ctx: CanvasRenderingContext2D, + region: Path2D +) extends Immediate { + def arc( + x: Int, + y: Int, + radius: Int, + startAngle: Double, + endAngle: Double, + counterclockwise: Boolean = false, + closedPath: Boolean = false, + segments: Array[Double] = Array.empty[Double] + ): Unit = { val x0 = x - rasterWidth / 2 val y0 = y - rasterHeight / 2 ctx.beginPath() ctx.arc(x0, y0, radius, startAngle, endAngle, counterclockwise) - if(closedPath) ctx.closePath(); + if closedPath then ctx.closePath(); ctx.stroke() } - def arcTo(x1: Int, y1: Int, x2: Int, y2: Int, radius: Int, counterclockwise: Boolean = false, closedPath: Boolean = false, segments: Array[Double] = Array.empty[Double]): Unit = { + def arcTo( + x1: Int, + y1: Int, + x2: Int, + y2: Int, + radius: Int, + counterclockwise: Boolean = false, + closedPath: Boolean = false, + segments: Array[Double] = Array.empty[Double] + ): Unit = { ctx.beginPath() ctx.arcTo(x1, y1, x2, y2, radius) ctx.stroke() } - def bezierCurveTo(cp1x: Int, cp1y: Int, cp2x: Int, cp2y: Int, x: Int, y: Int, segments: Array[Double] = Array.empty[Double]): Unit = { + def bezierCurveTo( + cp1x: Int, + cp1y: Int, + cp2x: Int, + cp2y: Int, + x: Int, + y: Int, + segments: Array[Double] = Array.empty[Double] + ): Unit = { ctx.beginPath() ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) ctx.stroke() @@ -96,7 +235,14 @@ class ImmediateImpl(rasterWidth: Int, rasterHeight: Int, ctx: CanvasRenderingCon ctx.clip(region) } - def clipArc(x: Int, y: Int, radius: Int, startAngle: Double = 0, endAngle: Double = 2 * Math.PI, counterclockwise: Boolean = false): Unit = { + def clipArc( + x: Int, + y: Int, + radius: Int, + startAngle: Double = 0, + endAngle: Double = 2 * Math.PI, + counterclockwise: Boolean = false + ): Unit = { val x0 = x - rasterWidth / 2 val y0 = y - rasterHeight / 2 region.arc(x0, y0, radius, startAngle, endAngle, counterclockwise) @@ -112,7 +258,12 @@ class ImmediateImpl(rasterWidth: Int, rasterHeight: Int, ctx: CanvasRenderingCon ctx.closePath() } - def circle(x: Double, y: Double, radius: Double, segments: Array[Double] = Array.empty[Double]): Unit = { + def circle( + x: Double, + y: Double, + radius: Double, + segments: Array[Double] = Array.empty[Double] + ): Unit = { val x0 = x - rasterWidth / 2.0 val y0 = y - rasterHeight / 2.0 ctx.beginPath() @@ -121,7 +272,13 @@ class ImmediateImpl(rasterWidth: Int, rasterHeight: Int, ctx: CanvasRenderingCon ctx.stroke() } - def dashLine(x1: Double, y1: Double, x2: Double, y2: Double, segments: Array[Double] = Array.empty[Double]): Unit = { + def dashLine( + x1: Double, + y1: Double, + x2: Double, + y2: Double, + segments: Array[Double] = Array.empty[Double] + ): Unit = { val x0 = x1 - rasterWidth / 2 val y0 = y1 - rasterHeight / 2 val x = x2 - rasterWidth / 2 @@ -133,7 +290,13 @@ class ImmediateImpl(rasterWidth: Int, rasterHeight: Int, ctx: CanvasRenderingCon ctx.stroke() } - def ellipse(x: Double, y: Double, width: Double, height: Double, segments: Array[Double] = Array.empty[Double]): Unit = { + def ellipse( + x: Double, + y: Double, + width: Double, + height: Double, + segments: Array[Double] = Array.empty[Double] + ): Unit = { val x0 = x - rasterWidth / 2 val y0 = y - rasterHeight / 2 ctx.beginPath() @@ -141,11 +304,30 @@ class ImmediateImpl(rasterWidth: Int, rasterHeight: Int, ctx: CanvasRenderingCon ctx.fill() } - def ellipseWithRotation(x: Double, y: Double, radiusX: Double, radiusY: Double, rotation: Double, startAngle: Double, endAngle: Double, counterclockwise: Boolean = false, segments: Array[Double] = Array.empty[Double]): Unit = { + def ellipseWithRotation( + x: Double, + y: Double, + radiusX: Double, + radiusY: Double, + rotation: Double, + startAngle: Double, + endAngle: Double, + counterclockwise: Boolean = false, + segments: Array[Double] = Array.empty[Double] + ): Unit = { val x0 = x - rasterWidth / 2 val y0 = y - rasterHeight / 2 ctx.beginPath() - ctx.ellipse(x0, y0, radiusX, radiusY, rotation, startAngle, endAngle, counterclockwise) + ctx.ellipse( + x0, + y0, + radiusX, + radiusY, + rotation, + startAngle, + endAngle, + counterclockwise + ) ctx.fill() } @@ -158,7 +340,13 @@ class ImmediateImpl(rasterWidth: Int, rasterHeight: Int, ctx: CanvasRenderingCon ctx.fill() } - def text(text: String, x: Double, y: Double, color: Color = Color.black, font: String = "25px serif"): Unit = { + def text( + text: String, + x: Double, + y: Double, + color: Color = Color.black, + font: String = "25px serif" + ): Unit = { ctx.save() val x0 = x - rasterWidth / 2 val y0 = y - rasterHeight / 2 @@ -174,8 +362,8 @@ class ImmediateImpl(rasterWidth: Int, rasterHeight: Int, ctx: CanvasRenderingCon } def line(x: Double, y: Double, closedPath: Boolean = false): Unit = { - ctx.lineTo(x - rasterWidth / 2,y - rasterHeight / 2) - if(closedPath) ctx.closePath() + ctx.lineTo(x - rasterWidth / 2, y - rasterHeight / 2) + if closedPath then ctx.closePath() ctx.stroke() } @@ -190,13 +378,18 @@ class ImmediateImpl(rasterWidth: Int, rasterHeight: Int, ctx: CanvasRenderingCon ctx.stroke() } - def pentagon(x: Double, y: Double, radius: Double, segments: Array[Double] = Array.empty[Double]): Unit = { + def pentagon( + x: Double, + y: Double, + radius: Double, + segments: Array[Double] = Array.empty[Double] + ): Unit = { val x0 = x - radius / 2 val y0 = y - radius / 2 - + @tailrec def drawSide(i: Int): Unit = { - if (i <= 5) { + if i <= 5 then { val angle = 2 * Math.PI * i / 5 val x1 = x0 + radius * Math.cos(angle) val y1 = y0 + radius * Math.sin(angle) @@ -215,7 +408,13 @@ class ImmediateImpl(rasterWidth: Int, rasterHeight: Int, ctx: CanvasRenderingCon ctx.stroke() } - def rectangle(x: Double, y: Double, width: Double, height: Double, segments: Array[Double] = Array.empty[Double]): Unit = { + def rectangle( + x: Double, + y: Double, + width: Double, + height: Double, + segments: Array[Double] = Array.empty[Double] + ): Unit = { val x0 = x - rasterWidth / 2 val y0 = y - rasterHeight / 2 ctx.beginPath() @@ -226,7 +425,14 @@ class ImmediateImpl(rasterWidth: Int, rasterHeight: Int, ctx: CanvasRenderingCon ctx.rotate(angle) } - def roundedRectangle(x: Int, y: Int, width: Int, height: Int, radius: Int, segments: Array[Double] = Array.empty[Double]): Unit = { + def roundedRectangle( + x: Int, + y: Int, + width: Int, + height: Int, + radius: Int, + segments: Array[Double] = Array.empty[Double] + ): Unit = { val x0 = x - rasterWidth / 2 val y0 = y - rasterHeight / 2 ctx.beginPath() @@ -235,33 +441,38 @@ class ImmediateImpl(rasterWidth: Int, rasterHeight: Int, ctx: CanvasRenderingCon ctx.arcTo(x0 + width, y0 + height, x0, y0 + height, radius) ctx.arcTo(x0, y0 + height, x0, y0, radius) ctx.arcTo(x0, y0, x0 + width, y0, radius) - ctx.fill() + ctx.fill() } - def star(x1: Double, y1: Double, radius: Double, segments: Array[Double] = Array.empty[Double]): Unit = { + def star( + x1: Double, + y1: Double, + radius: Double, + segments: Array[Double] = Array.empty[Double] + ): Unit = { val outerRadius = radius val innerRadius = radius / 2 val angle = Math.PI / 5 - val x = x1 - rasterWidth/2 - val y = y1 - rasterHeight/2 + val x = x1 - rasterWidth / 2 + val y = y1 - rasterHeight / 2 ctx.beginPath() ctx.moveTo( - x + Math.cos(0) * outerRadius, + x + Math.cos(0) * outerRadius, y + Math.sin(0) * outerRadius ) - for (i <- 1 to 10) { + for i <- 1 to 10 do { val isOuter = i % 2 == 0 - val r = if (isOuter) outerRadius else innerRadius + val r = if isOuter then outerRadius else innerRadius val a = i * angle ctx.lineTo( - x + Math.cos(a) * r, + x + Math.cos(a) * r, y + Math.sin(a) * r ) } ctx.closePath() - ctx.fill() + ctx.fill() } def stroke(color: Color): Unit = { @@ -274,7 +485,12 @@ class ImmediateImpl(rasterWidth: Int, rasterHeight: Int, ctx: CanvasRenderingCon ctx.strokeText(text, x0, y) } - def square(x: Double, y: Double, size: Double, segments: Array[Double] = Array.empty[Double]): Unit = { + def square( + x: Double, + y: Double, + size: Double, + segments: Array[Double] = Array.empty[Double] + ): Unit = { val x0 = x - rasterWidth / 2 val y0 = y - rasterHeight / 2 ctx.beginPath() @@ -282,7 +498,14 @@ class ImmediateImpl(rasterWidth: Int, rasterHeight: Int, ctx: CanvasRenderingCon ctx.stroke() } - def transform(a: Double, b: Double, c: Double, d: Double, e: Double, f: Double): Unit = { + def transform( + a: Double, + b: Double, + c: Double, + d: Double, + e: Double, + f: Double + ): Unit = { ctx.transform(a, b, c, d, e, f) } @@ -290,7 +513,15 @@ class ImmediateImpl(rasterWidth: Int, rasterHeight: Int, ctx: CanvasRenderingCon ctx.translate(x, y) } - def triangle(x1: Double, y1: Double, x2: Double, y2: Double, x3: Double, y3: Double, segments: Array[Double] = Array.empty[Double]): Unit = { + def triangle( + x1: Double, + y1: Double, + x2: Double, + y2: Double, + x3: Double, + y3: Double, + segments: Array[Double] = Array.empty[Double] + ): Unit = { ctx.beginPath() ctx.moveTo(x1, y1) ctx.lineTo(x2, y2) @@ -299,11 +530,3 @@ class ImmediateImpl(rasterWidth: Int, rasterHeight: Int, ctx: CanvasRenderingCon ctx.fill() } } - - - - - - - - diff --git a/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala b/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala index e7839f51..7e0bfe8a 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala @@ -39,4 +39,3 @@ trait Raster extends GenericRaster[CanvasDrawing, Immediate] { } } } - diff --git a/canvas/src/main/scala/doodle/canvas/algebra/Text.scala b/canvas/src/main/scala/doodle/canvas/algebra/Text.scala index 2938d7ae..dd3bcf56 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/Text.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/Text.scala @@ -38,12 +38,6 @@ trait Text extends GenericText[CanvasDrawing] { text: String, bounds: Bounds ): CanvasDrawing[Unit] = ??? - // CanvasDrawing.setTransform(tx) >> - // CanvasDrawing.withFill(fill) { - // CanvasDrawing.withStroke(stroke) { - // CanvasDrawing.text(text, bounds.width, bounds.actualBoundingBoxLeft) - // } - // } def textBoundingBox(text: String, font: Font): (BoundingBox, Bounds) = { ??? diff --git a/core/shared/src/main/scala/doodle/algebra/Raster.scala b/core/shared/src/main/scala/doodle/algebra/Raster.scala index f07ab24c..8d41a707 100644 --- a/core/shared/src/main/scala/doodle/algebra/Raster.scala +++ b/core/shared/src/main/scala/doodle/algebra/Raster.scala @@ -30,5 +30,3 @@ trait RasterConstructor[A] { algebra.raster(width, height)(f) } } - - diff --git a/core/shared/src/main/scala/doodle/algebra/generic/GenericRaster.scala b/core/shared/src/main/scala/doodle/algebra/generic/GenericRaster.scala index 50ced06b..8e217ad0 100644 --- a/core/shared/src/main/scala/doodle/algebra/generic/GenericRaster.scala +++ b/core/shared/src/main/scala/doodle/algebra/generic/GenericRaster.scala @@ -41,9 +41,7 @@ trait GenericRaster[G[_], A] extends Raster[A] { val bb = BoundingBox.centered(width, height) ( bb, - State.inspect(tx => - RasterApi.raster(tx, width, height)(f) - ) + State.inspect(tx => RasterApi.raster(tx, width, height)(f)) ) } } diff --git a/core/shared/src/main/scala/doodle/syntax/RasterSyntax.scala b/core/shared/src/main/scala/doodle/syntax/RasterSyntax.scala index 3145288c..ba84b210 100644 --- a/core/shared/src/main/scala/doodle/syntax/RasterSyntax.scala +++ b/core/shared/src/main/scala/doodle/syntax/RasterSyntax.scala @@ -32,4 +32,4 @@ trait RasterSyntax { def apply(implicit algebra: Alg & Raster[R]): algebra.Drawing[Unit] = algebra.raster(width, height)(f) } -} \ No newline at end of file +} diff --git a/examples/js/src/main/scala/doodle/examples/canvas/CanvasImmediateMode.scala b/examples/js/src/main/scala/doodle/examples/canvas/CanvasImmediateMode.scala index e125d1bf..0ca631b8 100644 --- a/examples/js/src/main/scala/doodle/examples/canvas/CanvasImmediateMode.scala +++ b/examples/js/src/main/scala/doodle/examples/canvas/CanvasImmediateMode.scala @@ -29,212 +29,213 @@ import scala.scalajs.js.annotation.* @JSExportTopLevel("CanvasImmediateMode") object Experiment { - def roof = + def roof = Picture - .triangle(200,100) + .triangle(200, 100) .fillColor(Color.blue) - .strokeColor(Color.black).debug - - def drawHotel = - (ctx: Immediate) => { - - ctx.rectangle(25, 0, 150, 200) - ctx.fill(Color.rgb(142, 113, 88)) - ctx.stroke(Color.black) - - ctx.rectangle(45, 0, 20, 40) - ctx.fill(Color.rgb(72, 36, 20)) - @tailrec - def drawWindows(index: Int): Unit = { - if (index < 12) { - val row = index / 4 - val col = index % 4 - val x = 30 + col * 40 - val y = 160 - row * 40 - - val color = (index, col) match { - case (0 | 1 | 4 | 5 | 8, _) => Color.rgb(255, 174, 0) - case (2 | 3 | 6 | 9, _) => Color.black - case _ => Color.rgb(255, 174, 0) - } + .strokeColor(Color.black) + .debug + + def drawHotel = + (ctx: Immediate) => { + + ctx.rectangle(25, 0, 150, 200) + ctx.fill(Color.rgb(142, 113, 88)) + ctx.stroke(Color.black) + + ctx.rectangle(45, 0, 20, 40) + ctx.fill(Color.rgb(72, 36, 20)) + @tailrec + def drawWindows(index: Int): Unit = { + if index < 12 then { + val row = index / 4 + val col = index % 4 + val x = 30 + col * 40 + val y = 160 - row * 40 + + val color = (index, col) match { + case (0 | 1 | 4 | 5 | 8, _) => Color.rgb(255, 174, 0) + case (2 | 3 | 6 | 9, _) => Color.black + case _ => Color.rgb(255, 174, 0) + } - ctx.square(x, y, 20) - ctx.fill(color) + ctx.square(x, y, 20) + ctx.fill(color) - drawWindows(index + 1) + drawWindows(index + 1) + } } - } - drawWindows(0) + drawWindows(0) - ctx.stroke(Color.black) - ctx.rectangle(175, 0, 50, 200) - ctx.fill(Color.rgb(105, 66, 43)) - @tailrec - def drawLines(y: Int): Unit = { - if (y >= 0) { - ctx.lineTo(175, y, 225, y) - drawLines(y - 25) + ctx.stroke(Color.black) + ctx.rectangle(175, 0, 50, 200) + ctx.fill(Color.rgb(105, 66, 43)) + @tailrec + def drawLines(y: Int): Unit = { + if y >= 0 then { + ctx.lineTo(175, y, 225, y) + drawLines(y - 25) + } } - } - drawLines(165) - - ctx.ellipse(130, 35, 75, 50, Array(5, 5)) - ctx.fill(Color.rgb(234, 215, 163)) - - ctx.text("HOTEL", 130, 32, font = "20px Fraktur") - } - -def drawApartments = - (ctx: Immediate) => { - @tailrec - def drawBuildings(index: Int): Unit = { - if (index < 5) { - val x = index * 50 - val height = index match { - case 0 => 200 - case 1 | 3 => 150 - case 2 => 175 - case 4 => 200 - } + drawLines(165) - ctx.rectangle(x, 0, 50, height) - ctx.fill(Color.rgb(178, 165, 148)) - ctx.stroke(Color.black) + ctx.ellipse(130, 35, 75, 50, Array(5, 5)) + ctx.fill(Color.rgb(234, 215, 163)) + + ctx.text("HOTEL", 130, 32, font = "20px Fraktur") + } + def drawApartments = + (ctx: Immediate) => + { @tailrec - def drawWindows(row: Int, col: Int): Unit = { - if (row < 4 && col < 2) { - val windowX = x + col * 20 + 5 - val windowY = height - (row + 1) * 40 + 10 - if(windowY > 60) { - ctx.rectangle(windowX, windowY, 15, 25) - ctx.fill(Color.rgb(220, 220, 220)) - ctx.stroke(Color.black) - } - if (col < 1) drawWindows(row, col + 1) - else drawWindows(row + 1, 0) + def drawBuildings(index: Int): Unit = { + if index < 5 then { + val x = index * 50 + val height = index match { + case 0 => 200 + case 1 | 3 => 150 + case 2 => 175 + case 4 => 200 + } + + ctx.rectangle(x, 0, 50, height) + ctx.fill(Color.rgb(178, 165, 148)) + ctx.stroke(Color.black) + + @tailrec + def drawWindows(row: Int, col: Int): Unit = { + if row < 4 && col < 2 then { + val windowX = x + col * 20 + 5 + val windowY = height - (row + 1) * 40 + 10 + if windowY > 60 then { + ctx.rectangle(windowX, windowY, 15, 25) + ctx.fill(Color.rgb(220, 220, 220)) + ctx.stroke(Color.black) + } + if col < 1 then drawWindows(row, col + 1) + else drawWindows(row + 1, 0) + } + } + + drawWindows(0, 0) + drawBuildings(index + 1) } } - drawWindows(0, 0) - drawBuildings(index + 1) - } - } - - @tailrec - def drawShops(index: Int): Unit = { - if (index < 5) { - val x = index * 50 - val color = index match { - case 0 => Color.red - case 1 => Color.rgb(39, 89, 70) - case 2 => Color.rgb(59, 31, 92) - case 3 => Color.rgb(133, 36, 81) - case 4 => Color.rgb(59, 31, 92) + @tailrec + def drawShops(index: Int): Unit = { + if index < 5 then { + val x = index * 50 + val color = index match { + case 0 => Color.red + case 1 => Color.rgb(39, 89, 70) + case 2 => Color.rgb(59, 31, 92) + case 3 => Color.rgb(133, 36, 81) + case 4 => Color.rgb(59, 31, 92) + } + + ctx.rectangle(x, 40, 50, 10) + ctx.fill(color) + ctx.stroke(Color.black) + + ctx.rectangle(x, 0, 50, 40) + ctx.fill(Color.rgb(204, 239, 234)) + ctx.stroke(Color.black) + + drawShops(index + 1) + } } - ctx.rectangle(x, 40, 50, 10) - ctx.fill(color) - ctx.stroke(Color.black) - - ctx.rectangle(x, 0, 50, 40) - ctx.fill(Color.rgb(204, 239, 234)) - ctx.stroke(Color.black) - - drawShops(index + 1) - } - } - - drawBuildings(0) - drawShops(0) - - ctx.clipRect(150, 150, 50, 50) - ctx.clipRect(50, 150, 50, 50) - ctx.clip() - - ctx.circle(100, 150, 50) - ctx.fill(Color.rgb(178, 165, 148)) - ctx.stroke(Color.black) - ctx.circle(150, 150, 50) - ctx.fill(Color.rgb(178, 165, 148)) - ctx.stroke(Color.black) - - ctx.endClip() + drawBuildings(0) + drawShops(0) - ctx.lineTo(100, 150, 100, 200) - ctx.lineTo(100, 150, 50, 150) - ctx.lineTo(150, 150, 150, 200) - ctx.lineTo(150, 150, 200, 150) - } + ctx.clipRect(150, 150, 50, 50) + ctx.clipRect(50, 150, 50, 50) + ctx.clip() - def drawCitySpace = - (ctx: Immediate) => - { - ctx.rectangle(0, 0, 130, 200) - ctx.fill(Color.rgb(185, 185, 185)) + ctx.circle(100, 150, 50) + ctx.fill(Color.rgb(178, 165, 148)) ctx.stroke(Color.black) - ctx.rectangle(10, 130, 50, 60) - ctx.fill(Color.rgb(162, 0, 0)) + ctx.circle(150, 150, 50) + ctx.fill(Color.rgb(178, 165, 148)) ctx.stroke(Color.black) - ctx.rectangle(70, 130, 50, 60) - ctx.fill(Color.black); - ctx.text("CITY SPACE", 65, 100, font = "20px Fraktur") - ctx.rectangle(10, 60, 110, 30) - ctx.fill(Color.rgb(204, 239, 234)) - ctx.stroke(Color.black) + ctx.endClip() - ctx.rectangle(10, 20, 30, 30) - ctx.fill(Color.rgb(204, 239, 234)) - ctx.stroke(Color.black) - ctx.rectangle(50, 0, 30, 50) - ctx.fill(Color.rgb(204, 239, 234)) - ctx.stroke(Color.black) - ctx.rectangle(90, 20, 30, 30) - ctx.fill(Color.rgb(204, 239, 234)) - ctx.stroke(Color.black) + ctx.lineTo(100, 150, 100, 200) + ctx.lineTo(100, 150, 50, 150) + ctx.lineTo(150, 150, 150, 200) + ctx.lineTo(150, 150, 200, 150) } - def drawRoad = - (ctx: Immediate) => - { - ctx.rectangle(0, 0, 800, 50) - ctx.fill(Color.rgb(135,135,144)) - ctx.stroke(Color.black) - ctx.dashLine(0, 25, 800, 25, Array(5, 5)) - } + def drawCitySpace = + (ctx: Immediate) => { + ctx.rectangle(0, 0, 130, 200) + ctx.fill(Color.rgb(185, 185, 185)) + ctx.stroke(Color.black) + ctx.rectangle(10, 130, 50, 60) + ctx.fill(Color.rgb(162, 0, 0)) + ctx.stroke(Color.black) + ctx.rectangle(70, 130, 50, 60) + ctx.fill(Color.black); + ctx.text("CITY SPACE", 65, 100, font = "20px Fraktur") + + ctx.rectangle(10, 60, 110, 30) + ctx.fill(Color.rgb(204, 239, 234)) + ctx.stroke(Color.black) + + ctx.rectangle(10, 20, 30, 30) + ctx.fill(Color.rgb(204, 239, 234)) + ctx.stroke(Color.black) + ctx.rectangle(50, 0, 30, 50) + ctx.fill(Color.rgb(204, 239, 234)) + ctx.stroke(Color.black) + ctx.rectangle(90, 20, 30, 30) + ctx.fill(Color.rgb(204, 239, 234)) + ctx.stroke(Color.black) + } - val citySpace = raster(150, 200)(drawCitySpace) - val hotel = raster(240, 200)(drawHotel) - val apartments = raster(250, 200)(drawApartments) - val road = raster(800, 50)(drawRoad) + def drawRoad = + (ctx: Immediate) => { + ctx.rectangle(0, 0, 800, 50) + ctx.fill(Color.rgb(135, 135, 144)) + ctx.stroke(Color.black) + ctx.dashLine(0, 25, 800, 25, Array(5, 5)) + } - val tree = - Picture - .triangle(100, 100) - .fillColor(Color.green) - .strokeColor(Color.black) - .above( + val citySpace = raster(150, 200)(drawCitySpace) + val hotel = raster(240, 200)(drawHotel) + val apartments = raster(250, 200)(drawApartments) + val road = raster(800, 50)(drawRoad) + + val tree = Picture - .rectangle(20, 50) - .fillColor(Color.brown) + .triangle(100, 100) + .fillColor(Color.green) .strokeColor(Color.black) - .beside( + .above( Picture .rectangle(20, 50) .fillColor(Color.brown) .strokeColor(Color.black) + .beside( + Picture + .rectangle(20, 50) + .fillColor(Color.brown) + .strokeColor(Color.black) + ) ) - ) - .at(0,-120) - .scale(0.5, 0.5) - + .at(0, -120) + .scale(0.5, 0.5) - val joint = (citySpace.beside(apartments.beside(hotel.beside(tree).beside(tree)))).above(road) + val joint = (citySpace + .beside(apartments.beside(hotel.beside(tree).beside(tree)))) + .above(road) - @JSExport - def draw(mount: String) = - joint.drawWithFrame(Frame(mount)) + @JSExport + def draw(mount: String) = + joint.drawWithFrame(Frame(mount)) } From 69b6150914ef7d4de6e1e90ccd6295b9d4ca8683 Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Sun, 18 Aug 2024 17:52:06 +0530 Subject: [PATCH 39/43] GitWorkflowGenerate --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1ad7e909..0169ea18 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -71,11 +71,11 @@ jobs: - name: Make target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') - run: mkdir -p reactor/js/target image/js/target unidocs/target js/target reactor/jvm/target core/js/target examples/js/target turtle/js/target core/jvm/target jvm/target interact/js/target interact/jvm/target java2d/target examples/jvm/target target turtle/jvm/target image/jvm/target svg/jvm/target svg/js/target golden/target canvas/target project/target + run: mkdir -p reactor/js/target image/js/target unidocs/target js/target reactor/jvm/target core/js/target examples/js/target turtle/js/target core/jvm/target jvm/target interact/js/target interact/jvm/target java2d/target examples/jvm/target turtle/jvm/target image/jvm/target svg/jvm/target svg/js/target golden/target target canvas/target project/target - name: Compress target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') - run: tar cf targets.tar reactor/js/target image/js/target unidocs/target js/target reactor/jvm/target core/js/target examples/js/target turtle/js/target core/jvm/target jvm/target interact/js/target interact/jvm/target java2d/target examples/jvm/target target turtle/jvm/target image/jvm/target svg/jvm/target svg/js/target golden/target canvas/target project/target + run: tar cf targets.tar reactor/js/target image/js/target unidocs/target js/target reactor/jvm/target core/js/target examples/js/target turtle/js/target core/jvm/target jvm/target interact/js/target interact/jvm/target java2d/target examples/jvm/target turtle/jvm/target image/jvm/target svg/jvm/target svg/js/target golden/target target canvas/target project/target - name: Upload target directories if: github.event_name != 'pull_request' && (startsWith(github.ref, 'refs/tags/v') || github.ref == 'refs/heads/main') From 30f91b414ba879c12b9324eb3d1add7734751c78 Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Sun, 18 Aug 2024 18:04:57 +0530 Subject: [PATCH 40/43] Removed warnings --- .../doodle/canvas/algebra/CanvasDrawing.scala | 5 - .../doodle/canvas/algebra/Immediate.scala | 1 - .../scala/doodle/canvas/algebra/Raster.scala | 1 - .../examples/canvas/CanvasImmediateMode.scala | 243 +++++++++--------- 4 files changed, 121 insertions(+), 129 deletions(-) diff --git a/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala b/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala index 1e874e4b..5a756253 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/CanvasDrawing.scala @@ -16,7 +16,6 @@ package doodle.canvas.algebra -import doodle.algebra.Algebra import doodle.algebra.generic.* import cats.Apply @@ -41,10 +40,6 @@ import doodle.core.font.FontWeight import org.scalajs.dom.CanvasRenderingContext2D import org.scalajs.dom.Path2D -import doodle.algebra.Algebra -import doodle.algebra.Raster -import doodle.algebra.Picture - import scala.scalajs.js import scala.scalajs.js.JSConverters.* diff --git a/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala b/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala index 1b3df122..d4a8ef0b 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala @@ -18,7 +18,6 @@ package doodle.canvas.algebra import doodle.core.Color import doodle.core.* -import doodle.syntax.all.* import org.scalajs.dom.CanvasRenderingContext2D import org.scalajs.dom.Path2D import scala.annotation.tailrec diff --git a/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala b/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala index 7e0bfe8a..da5e8013 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/Raster.scala @@ -18,7 +18,6 @@ package doodle.canvas.algebra import doodle.algebra.Algebra import doodle.algebra.generic.* -import org.scalajs.dom.CanvasRenderingContext2D import doodle.core.Transform as Tx trait Raster extends GenericRaster[CanvasDrawing, Immediate] { diff --git a/examples/js/src/main/scala/doodle/examples/canvas/CanvasImmediateMode.scala b/examples/js/src/main/scala/doodle/examples/canvas/CanvasImmediateMode.scala index 0ca631b8..b68381b2 100644 --- a/examples/js/src/main/scala/doodle/examples/canvas/CanvasImmediateMode.scala +++ b/examples/js/src/main/scala/doodle/examples/canvas/CanvasImmediateMode.scala @@ -21,7 +21,6 @@ import doodle.canvas.{*, given} import doodle.canvas.algebra.Immediate import doodle.core.* import doodle.syntax.all.* -import org.scalajs.dom.CanvasRenderingContext2D import scala.annotation.tailrec import scala.scalajs.js.annotation.* @@ -87,155 +86,155 @@ object Experiment { ctx.text("HOTEL", 130, 32, font = "20px Fraktur") } - def drawApartments = - (ctx: Immediate) => - { - @tailrec - def drawBuildings(index: Int): Unit = { - if index < 5 then { - val x = index * 50 - val height = index match { - case 0 => 200 - case 1 | 3 => 150 - case 2 => 175 - case 4 => 200 - } + val hotel = raster(240, 200)(drawHotel) + + def drawApartment = + (ctx: Immediate) => { + @tailrec + def drawBuildings(index: Int): Unit = { + if index < 5 then { + val x = index * 50 + val height = index match { + case 0 => 200 + case 1 | 3 => 150 + case 2 => 175 + case 4 => 200 + } + + ctx.rectangle(x, 0, 50, height) + ctx.fill(Color.rgb(178, 165, 148)) + ctx.stroke(Color.black) - ctx.rectangle(x, 0, 50, height) - ctx.fill(Color.rgb(178, 165, 148)) - ctx.stroke(Color.black) - - @tailrec - def drawWindows(row: Int, col: Int): Unit = { - if row < 4 && col < 2 then { - val windowX = x + col * 20 + 5 - val windowY = height - (row + 1) * 40 + 10 - if windowY > 60 then { - ctx.rectangle(windowX, windowY, 15, 25) - ctx.fill(Color.rgb(220, 220, 220)) - ctx.stroke(Color.black) - } - if col < 1 then drawWindows(row, col + 1) - else drawWindows(row + 1, 0) + @tailrec + def drawWindows(row: Int, col: Int): Unit = { + if row < 4 && col < 2 then { + val windowX = x + col * 20 + 5 + val windowY = height - (row + 1) * 40 + 10 + if windowY > 60 then { + ctx.rectangle(windowX, windowY, 15, 25) + ctx.fill(Color.rgb(220, 220, 220)) + ctx.stroke(Color.black) } + if col < 1 then drawWindows(row, col + 1) + else drawWindows(row + 1, 0) } - - drawWindows(0, 0) - drawBuildings(index + 1) } + + drawWindows(0, 0) + drawBuildings(index + 1) } + } - @tailrec - def drawShops(index: Int): Unit = { - if index < 5 then { - val x = index * 50 - val color = index match { - case 0 => Color.red - case 1 => Color.rgb(39, 89, 70) - case 2 => Color.rgb(59, 31, 92) - case 3 => Color.rgb(133, 36, 81) - case 4 => Color.rgb(59, 31, 92) - } + @tailrec + def drawShops(index: Int): Unit = { + if index < 5 then { + val x = index * 50 + val color = index match { + case 0 => Color.red + case 1 => Color.rgb(39, 89, 70) + case 2 => Color.rgb(59, 31, 92) + case 3 => Color.rgb(133, 36, 81) + case 4 => Color.rgb(59, 31, 92) + } - ctx.rectangle(x, 40, 50, 10) - ctx.fill(color) - ctx.stroke(Color.black) + ctx.rectangle(x, 40, 50, 10) + ctx.fill(color) + ctx.stroke(Color.black) - ctx.rectangle(x, 0, 50, 40) - ctx.fill(Color.rgb(204, 239, 234)) - ctx.stroke(Color.black) + ctx.rectangle(x, 0, 50, 40) + ctx.fill(Color.rgb(204, 239, 234)) + ctx.stroke(Color.black) - drawShops(index + 1) - } + drawShops(index + 1) } + } - drawBuildings(0) - drawShops(0) + drawBuildings(0) + drawShops(0) - ctx.clipRect(150, 150, 50, 50) - ctx.clipRect(50, 150, 50, 50) - ctx.clip() + ctx.clipRect(150, 150, 50, 50) + ctx.clipRect(50, 150, 50, 50) + ctx.clip() - ctx.circle(100, 150, 50) - ctx.fill(Color.rgb(178, 165, 148)) - ctx.stroke(Color.black) - ctx.circle(150, 150, 50) - ctx.fill(Color.rgb(178, 165, 148)) - ctx.stroke(Color.black) + ctx.circle(100, 150, 50) + ctx.fill(Color.rgb(178, 165, 148)) + ctx.stroke(Color.black) + ctx.circle(150, 150, 50) + ctx.fill(Color.rgb(178, 165, 148)) + ctx.stroke(Color.black) - ctx.endClip() + ctx.endClip() - ctx.lineTo(100, 150, 100, 200) - ctx.lineTo(100, 150, 50, 150) - ctx.lineTo(150, 150, 150, 200) - ctx.lineTo(150, 150, 200, 150) - } + ctx.lineTo(100, 150, 100, 200) + ctx.lineTo(100, 150, 50, 150) + ctx.lineTo(150, 150, 150, 200) + ctx.lineTo(150, 150, 200, 150) + } - def drawCitySpace = - (ctx: Immediate) => { - ctx.rectangle(0, 0, 130, 200) - ctx.fill(Color.rgb(185, 185, 185)) - ctx.stroke(Color.black) - ctx.rectangle(10, 130, 50, 60) - ctx.fill(Color.rgb(162, 0, 0)) - ctx.stroke(Color.black) - ctx.rectangle(70, 130, 50, 60) - ctx.fill(Color.black); - ctx.text("CITY SPACE", 65, 100, font = "20px Fraktur") + def drawCitySpace = + (ctx: Immediate) => { + ctx.rectangle(0, 0, 130, 200) + ctx.fill(Color.rgb(185, 185, 185)) + ctx.stroke(Color.black) + ctx.rectangle(10, 130, 50, 60) + ctx.fill(Color.rgb(162, 0, 0)) + ctx.stroke(Color.black) + ctx.rectangle(70, 130, 50, 60) + ctx.fill(Color.black); + ctx.text("CITY SPACE", 65, 100, font = "20px Fraktur") - ctx.rectangle(10, 60, 110, 30) - ctx.fill(Color.rgb(204, 239, 234)) - ctx.stroke(Color.black) + ctx.rectangle(10, 60, 110, 30) + ctx.fill(Color.rgb(204, 239, 234)) + ctx.stroke(Color.black) - ctx.rectangle(10, 20, 30, 30) - ctx.fill(Color.rgb(204, 239, 234)) - ctx.stroke(Color.black) - ctx.rectangle(50, 0, 30, 50) - ctx.fill(Color.rgb(204, 239, 234)) - ctx.stroke(Color.black) - ctx.rectangle(90, 20, 30, 30) - ctx.fill(Color.rgb(204, 239, 234)) - ctx.stroke(Color.black) - } + ctx.rectangle(10, 20, 30, 30) + ctx.fill(Color.rgb(204, 239, 234)) + ctx.stroke(Color.black) + ctx.rectangle(50, 0, 30, 50) + ctx.fill(Color.rgb(204, 239, 234)) + ctx.stroke(Color.black) + ctx.rectangle(90, 20, 30, 30) + ctx.fill(Color.rgb(204, 239, 234)) + ctx.stroke(Color.black) + } - def drawRoad = - (ctx: Immediate) => { - ctx.rectangle(0, 0, 800, 50) - ctx.fill(Color.rgb(135, 135, 144)) - ctx.stroke(Color.black) - ctx.dashLine(0, 25, 800, 25, Array(5, 5)) - } + def drawRoad = + (ctx: Immediate) => { + ctx.rectangle(0, 0, 800, 50) + ctx.fill(Color.rgb(135, 135, 144)) + ctx.stroke(Color.black) + ctx.dashLine(0, 25, 800, 25, Array(5, 5)) + } - val citySpace = raster(150, 200)(drawCitySpace) - val hotel = raster(240, 200)(drawHotel) - val apartments = raster(250, 200)(drawApartments) - val road = raster(800, 50)(drawRoad) + val citySpace = raster(150, 200)(drawCitySpace) + val apartments = raster(250, 200)(drawApartment) + val road = raster(800, 50)(drawRoad) - val tree = + val tree = + Picture + .triangle(100, 100) + .fillColor(Color.green) + .strokeColor(Color.black) + .above( Picture - .triangle(100, 100) - .fillColor(Color.green) + .rectangle(20, 50) + .fillColor(Color.brown) .strokeColor(Color.black) - .above( + .beside( Picture .rectangle(20, 50) .fillColor(Color.brown) .strokeColor(Color.black) - .beside( - Picture - .rectangle(20, 50) - .fillColor(Color.brown) - .strokeColor(Color.black) - ) ) - .at(0, -120) - .scale(0.5, 0.5) + ) + .at(0, -120) + .scale(0.5, 0.5) - val joint = (citySpace - .beside(apartments.beside(hotel.beside(tree).beside(tree)))) - .above(road) + val joint = (citySpace + .beside(apartments.beside(hotel.beside(tree).beside(tree)))) + .above(road) - @JSExport - def draw(mount: String) = - joint.drawWithFrame(Frame(mount)) + @JSExport + def draw(mount: String) = + joint.drawWithFrame(Frame(mount)) } From ced2f8ef778d94bfb86751bbba7f55a644b29913 Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Sun, 18 Aug 2024 18:13:52 +0530 Subject: [PATCH 41/43] Removed cmd /c from sbt file --- build.sbt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.sbt b/build.sbt index aab773ce..4f521a7d 100644 --- a/build.sbt +++ b/build.sbt @@ -152,9 +152,9 @@ lazy val docs = val dest1 = mdocOut.value val dest2 = (laikaSite / target).value val cmd1 = - s"cmd /c npx tailwindcss -i ${src.toString}/creative-scala.css -o ${dest1.toString}/creative-scala.css" + s"npx tailwindcss -i ${src.toString}/creative-scala.css -o ${dest1.toString}/creative-scala.css" val cmd2 = - s"cmd /c npx tailwindcss -i ${src.toString}/creative-scala.css -o ${dest2.toString}/creative-scala.css" + s"npx tailwindcss -i ${src.toString}/creative-scala.css -o ${dest2.toString}/creative-scala.css" cmd1 ! cmd2 ! From 24bc8c578464d09fd0723f7e56a9722f4529f124 Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Tue, 20 Aug 2024 15:40:01 +0530 Subject: [PATCH 42/43] Few changes and example setup --- .../doodle/canvas/algebra/Immediate.scala | 72 +++++++++---------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala b/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala index d4a8ef0b..e49eceda 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala @@ -28,7 +28,7 @@ trait Immediate { def arc( x: Int, y: Int, - radius: Int, + diameter: Int, startAngle: Double, endAngle: Double, counterclockwise: Boolean = false, @@ -40,7 +40,7 @@ trait Immediate { y1: Int, x2: Int, y2: Int, - radius: Int, + diameter: Int, counterclockwise: Boolean = false, closedPath: Boolean = false, segments: Array[Double] = Array.empty[Double] @@ -60,7 +60,7 @@ trait Immediate { def clipArc( x: Int, y: Int, - radius: Int, + diameter: Int, startAngle: Double = 0, endAngle: Double = 2 * Math.PI, counterclockwise: Boolean = false @@ -70,7 +70,7 @@ trait Immediate { def circle( x: Double, y: Double, - radius: Double, + diameter: Double, segments: Array[Double] = Array.empty[Double] ): Unit def dashLine( @@ -90,8 +90,8 @@ trait Immediate { def ellipseWithRotation( x: Double, y: Double, - radiusX: Double, - radiusY: Double, + diameterX: Double, + diameterY: Double, rotation: Double, startAngle: Double, endAngle: Double, @@ -105,7 +105,7 @@ trait Immediate { def pentagon( x: Double, y: Double, - radius: Double, + diameter: Double, segments: Array[Double] = Array.empty[Double] ): Unit def quadraticCurveTo(cpx: Double, cpy: Double, x: Double, y: Double): Unit @@ -122,13 +122,13 @@ trait Immediate { y: Int, width: Int, height: Int, - radius: Int, + diameter: Int, segments: Array[Double] = Array.empty[Double] ): Unit def star( x: Double, y: Double, - radius: Double, + diameter: Double, segments: Array[Double] = Array.empty[Double] ): Unit def stroke(color: Color): Unit @@ -175,7 +175,7 @@ class ImmediateImpl( def arc( x: Int, y: Int, - radius: Int, + diameter: Int, startAngle: Double, endAngle: Double, counterclockwise: Boolean = false, @@ -185,7 +185,7 @@ class ImmediateImpl( val x0 = x - rasterWidth / 2 val y0 = y - rasterHeight / 2 ctx.beginPath() - ctx.arc(x0, y0, radius, startAngle, endAngle, counterclockwise) + ctx.arc(x0, y0, diameter / 2, startAngle, endAngle, counterclockwise) if closedPath then ctx.closePath(); ctx.stroke() } @@ -195,13 +195,13 @@ class ImmediateImpl( y1: Int, x2: Int, y2: Int, - radius: Int, + diameter: Int, counterclockwise: Boolean = false, closedPath: Boolean = false, segments: Array[Double] = Array.empty[Double] ): Unit = { ctx.beginPath() - ctx.arcTo(x1, y1, x2, y2, radius) + ctx.arcTo(x1, y1, x2, y2, diameter / 2) ctx.stroke() } @@ -237,14 +237,14 @@ class ImmediateImpl( def clipArc( x: Int, y: Int, - radius: Int, + diameter: Int, startAngle: Double = 0, endAngle: Double = 2 * Math.PI, counterclockwise: Boolean = false ): Unit = { val x0 = x - rasterWidth / 2 val y0 = y - rasterHeight / 2 - region.arc(x0, y0, radius, startAngle, endAngle, counterclockwise) + region.arc(x0, y0, diameter / 2, startAngle, endAngle, counterclockwise) } def clipRect(x: Int, y: Int, width: Int, height: Int): Unit = { @@ -260,14 +260,14 @@ class ImmediateImpl( def circle( x: Double, y: Double, - radius: Double, + diameter: Double, segments: Array[Double] = Array.empty[Double] ): Unit = { val x0 = x - rasterWidth / 2.0 val y0 = y - rasterHeight / 2.0 ctx.beginPath() ctx.setLineDash(segments.toJSArray) - ctx.arc(x0, y0, radius, 0, 2 * Math.PI) + ctx.arc(x0, y0, diameter / 2, 0, 2 * Math.PI) ctx.stroke() } @@ -306,8 +306,8 @@ class ImmediateImpl( def ellipseWithRotation( x: Double, y: Double, - radiusX: Double, - radiusY: Double, + diameterX: Double, + diameterY: Double, rotation: Double, startAngle: Double, endAngle: Double, @@ -320,8 +320,8 @@ class ImmediateImpl( ctx.ellipse( x0, y0, - radiusX, - radiusY, + diameterX, + diameterY, rotation, startAngle, endAngle, @@ -380,24 +380,24 @@ class ImmediateImpl( def pentagon( x: Double, y: Double, - radius: Double, + diameter: Double, segments: Array[Double] = Array.empty[Double] ): Unit = { - val x0 = x - radius / 2 - val y0 = y - radius / 2 + val x0 = x - rasterWidth / 2 + val y0 = y - rasterHeight / 2 @tailrec def drawSide(i: Int): Unit = { if i <= 5 then { val angle = 2 * Math.PI * i / 5 - val x1 = x0 + radius * Math.cos(angle) - val y1 = y0 + radius * Math.sin(angle) + val x1 = x0 + (diameter / 2) * Math.cos(angle) + val y1 = y0 + (diameter / 2) * Math.sin(angle) ctx.lineTo(x1, y1) drawSide(i + 1) } } ctx.beginPath() - ctx.moveTo(x0 + radius, y0) + ctx.moveTo(x0 + (diameter / 2), y0) drawSide(1) } @@ -429,28 +429,28 @@ class ImmediateImpl( y: Int, width: Int, height: Int, - radius: Int, + diameter: Int, segments: Array[Double] = Array.empty[Double] ): Unit = { val x0 = x - rasterWidth / 2 val y0 = y - rasterHeight / 2 ctx.beginPath() - ctx.moveTo(x0 + radius, y0) - ctx.arcTo(x0 + width, y0, x0 + width, y0 + height, radius) - ctx.arcTo(x0 + width, y0 + height, x0, y0 + height, radius) - ctx.arcTo(x0, y0 + height, x0, y0, radius) - ctx.arcTo(x0, y0, x0 + width, y0, radius) + ctx.moveTo(x0 + (diameter / 2), y0) + ctx.arcTo(x0 + width, y0, x0 + width, y0 + height, diameter / 2) + ctx.arcTo(x0 + width, y0 + height, x0, y0 + height, diameter / 2) + ctx.arcTo(x0, y0 + height, x0, y0, diameter / 2) + ctx.arcTo(x0, y0, x0 + width, y0, diameter / 2) ctx.fill() } def star( x1: Double, y1: Double, - radius: Double, + diameter: Double, segments: Array[Double] = Array.empty[Double] ): Unit = { - val outerRadius = radius - val innerRadius = radius / 2 + val outerRadius = diameter / 2 + val innerRadius = diameter / 4 val angle = Math.PI / 5 val x = x1 - rasterWidth / 2 val y = y1 - rasterHeight / 2 From 37f261898ddf57944a6f8c97476494c7e940795e Mon Sep 17 00:00:00 2001 From: Siddharth Banga <91724657+VOSID8@users.noreply.github.com> Date: Fri, 23 Aug 2024 02:20:48 +0530 Subject: [PATCH 43/43] Fixed methods" ; --- .../scala/doodle/canvas/algebra/Immediate.scala | 15 ++++----------- .../examples/canvas/CanvasImmediateMode.scala | 7 ------- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala b/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala index e49eceda..73c4413e 100644 --- a/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala +++ b/canvas/src/main/scala/doodle/canvas/algebra/Immediate.scala @@ -54,7 +54,6 @@ trait Immediate { y: Int, segments: Array[Double] = Array.empty[Double] ): Unit - def beginPath(): Unit def clearRect(x: Int, y: Int, width: Int, height: Int): Unit def clip(): Unit def clipArc( @@ -66,7 +65,6 @@ trait Immediate { counterclockwise: Boolean = false ): Unit def clipRect(x: Int, y: Int, width: Int, height: Int): Unit - def closePath(): Unit def circle( x: Double, y: Double, @@ -185,6 +183,7 @@ class ImmediateImpl( val x0 = x - rasterWidth / 2 val y0 = y - rasterHeight / 2 ctx.beginPath() + ctx.setLineDash(segments.toJSArray) ctx.arc(x0, y0, diameter / 2, startAngle, endAngle, counterclockwise) if closedPath then ctx.closePath(); ctx.stroke() @@ -219,10 +218,6 @@ class ImmediateImpl( ctx.stroke() } - def beginPath(): Unit = { - ctx.beginPath(); - } - def clearRect(x: Int, y: Int, width: Int, height: Int): Unit = { val x0 = x / 2 val y0 = y / 2 @@ -253,10 +248,6 @@ class ImmediateImpl( region.rect(x0, y0, width, height) } - def closePath(): Unit = { - ctx.closePath() - } - def circle( x: Double, y: Double, @@ -299,6 +290,7 @@ class ImmediateImpl( val x0 = x - rasterWidth / 2 val y0 = y - rasterHeight / 2 ctx.beginPath() + ctx.setLineDash(segments.toJSArray) ctx.ellipse(x0, y0, width / 2, height / 2, 0, 0, 2 * Math.PI) ctx.fill() } @@ -317,6 +309,7 @@ class ImmediateImpl( val x0 = x - rasterWidth / 2 val y0 = y - rasterHeight / 2 ctx.beginPath() + ctx.setLineDash(segments.toJSArray) ctx.ellipse( x0, y0, @@ -493,8 +486,8 @@ class ImmediateImpl( val x0 = x - rasterWidth / 2 val y0 = y - rasterHeight / 2 ctx.beginPath() + ctx.setLineDash(segments.toJSArray) ctx.rect(x0, y0, size, size) - ctx.stroke() } def transform( diff --git a/examples/js/src/main/scala/doodle/examples/canvas/CanvasImmediateMode.scala b/examples/js/src/main/scala/doodle/examples/canvas/CanvasImmediateMode.scala index b68381b2..bd3311a8 100644 --- a/examples/js/src/main/scala/doodle/examples/canvas/CanvasImmediateMode.scala +++ b/examples/js/src/main/scala/doodle/examples/canvas/CanvasImmediateMode.scala @@ -28,13 +28,6 @@ import scala.scalajs.js.annotation.* @JSExportTopLevel("CanvasImmediateMode") object Experiment { - def roof = - Picture - .triangle(200, 100) - .fillColor(Color.blue) - .strokeColor(Color.black) - .debug - def drawHotel = (ctx: Immediate) => {