Skip to content

Commit

Permalink
Sync with Kojo 2.9.28; bump up Scala version to 2.13.14.
Browse files Browse the repository at this point in the history
  • Loading branch information
litan committed Jun 23, 2024
1 parent b966a09 commit fa48850
Show file tree
Hide file tree
Showing 20 changed files with 201 additions and 48 deletions.
4 changes: 2 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name := "kojo-lib"

organization := "net.kogics"

version := "0.2.0"
version := "0.3.0"

scalaVersion := "2.13.8"
scalaVersion := "2.13.14"

scalacOptions := Seq("-feature", "-deprecation")
run / javaOptions ++= Seq("-Xmx1024m", "-Xss1m")
Expand Down
2 changes: 1 addition & 1 deletion src/main/scala/net/kogics/kojo/animation/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,8 @@ package object animation {
(System.currentTimeMillis - startMillis).toDouble
val ns = nextState(currState, elapsedTimeMillis)
val pic2 = picMaker(ns)
currPic.erase()
pic2.draw()
currPic.erase()

currState = ns
currPic = pic2
Expand Down
3 changes: 3 additions & 0 deletions src/main/scala/net/kogics/kojo/core/Picture.scala
Original file line number Diff line number Diff line change
Expand Up @@ -217,5 +217,8 @@ trait Picture extends InputAware {
def withPosition(x: Double, y: Double): Picture
def withZIndex(idx: Int): Picture
def withClipping(clipShape: Shape): Picture
def withClipping(clipPic: Picture): Picture
def withMask(maskPic: Picture): Picture
def withPenCapJoin(capJoin: (Int, Int)): Picture
def withNoPen(): Picture
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import net.kogics.kojo.core.Point
import net.kogics.kojo.core.SCanvas
import net.kogics.kojo.util.Utils

package object gaming {
package object fpgaming {
trait GameMsgSink[Msg] {
def triggerIncrementalUpdate(msg: Msg): Unit

Expand Down Expand Up @@ -99,15 +99,16 @@ package object gaming {
init: => Model,
update: (Model, Msg) => Model,
view: Model => Picture,
subscriptions: Model => Seq[Sub[Msg]]
subscriptions: Model => Seq[Sub[Msg]],
refreshRate: Long
)(implicit canvas: SCanvas)
extends GameMsgSink[Msg] {
private var currModel: Model = _
private var currSubs: Seq[Sub[Msg]] = _
private var currView: Picture = _
private var firstTime = true

private var gameTimer = canvas.timer(20) {
private var gameTimer = canvas.timer(refreshRate) {
if (firstTime) {
firstTime = false
currModel = init
Expand Down
8 changes: 4 additions & 4 deletions src/main/scala/net/kogics/kojo/kmath/rational.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ import org.apache.commons.math3.fraction.BigFraction
import org.apache.commons.math3.fraction.BigFractionFormat

trait Rationals {
implicit def itor(n: Int) = Rational(new BigFraction(n))
implicit def ltor(n: Long) = Rational(new BigFraction(n))
implicit def dtor(n: Double) = {
implicit def itor(n: Int): Rational = Rational(new BigFraction(n))
implicit def ltor(n: Long): Rational = Rational(new BigFraction(n))
implicit def dtor(n: Double): Rational = {
val ret = Rational(new BigFraction(n, 1e-9, Int.MaxValue))
if (n != 0 && ret == 0) Rational(new BigFraction(n)) else ret
}
implicit def ftor(n: Float) = Rational(new BigFraction(n))
implicit def ftor(n: Float): Rational = Rational(new BigFraction(n))

implicit class RationalMaker(val sc: StringContext) {
val bff = new BigFractionFormat
Expand Down
36 changes: 20 additions & 16 deletions src/main/scala/net/kogics/kojo/lite/Builtins.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,10 @@ import java.awt.Paint
import java.awt.Toolkit
import java.net.URL
import javax.swing.JComponent

import scala.language.implicitConversions

import com.jhlabs.image.AbstractBufferedImageOp
import com.jhlabs.image.LightFilter.Light
import net.kogics.kojo.core.Rich2DPath
import net.kogics.kojo.core.VertexShape
import net.kogics.kojo.core.Voice
import net.kogics.kojo.core.{Rich2DPath, SCanvas, VertexShape, Voice}
import net.kogics.kojo.kmath.KEasing
import net.kogics.kojo.music.RealtimeNotePlayer
import net.kogics.kojo.turtle.TurtleWorldAPI
Expand Down Expand Up @@ -212,7 +208,7 @@ class Builtins(
def withFillColor(pic: Picture, color: Color) = pic.withFillColor(color)
def withPenColor(pic: Picture, color: Color) = pic.withPenColor(color)

implicit val _picCanvas = tCanvas
implicit val _picCanvas: SCanvas = tCanvas
def pict(painter: Painter) = picture.Pic(painter)
def PictureT(painter: Painter) = picture.Pic(painter)
def Picture(fn: => Unit) = picture.Pic0 { t =>
Expand Down Expand Up @@ -760,7 +756,7 @@ class Builtins(
def rangeTo(start: Double, end: Double, step: Double) = Range.BigDecimal.inclusive(start, end, step)
def rangeTill(start: Double, end: Double, step: Double) = Range.BigDecimal(start, end, step)

implicit def bd2double(bd: BigDecimal) = bd.doubleValue
implicit def bd2double(bd: BigDecimal): Double = bd.doubleValue

type CanvasDraw = net.kogics.kojo.lite.CanvasDraw
import scala.language.reflectiveCalls
Expand Down Expand Up @@ -836,6 +832,8 @@ class Builtins(
LoadProgress.hideLoading()
}

def resolvedPath(fname: String): String = net.kogics.kojo.util.Utils.absolutePath(fname)

def animateWithRedraw[S](initState: S, nextState: S => S, stateView: S => Picture): Unit = {
import edu.umd.cs.piccolo.activities.PActivity

Expand All @@ -846,8 +844,8 @@ class Builtins(
case (state, pic) =>
val newState = nextState(state)
val pic2 = stateView(state)
pic.erase()
pic2.draw()
pic.erase()
if (newState == state) {
tCanvas.stopAnimationActivity(anim)
}
Expand Down Expand Up @@ -876,15 +874,21 @@ class Builtins(
animateWithSetupCanvasDraw { canvas => }(drawFrame)
}

type Sub[M] = gaming.Sub[M]
type CmdQ[M] = gaming.CmdQ[M]
val Subscriptions = gaming.Subscriptions
lazy val CollisionDetector = new gaming.CollisionDetector()
@volatile private var currGame: Option[gaming.Game[_, _]] = None
type Sub[M] = fpgaming.Sub[M]
type CmdQ[M] = fpgaming.CmdQ[M]
val Subscriptions = fpgaming.Subscriptions
lazy val CollisionDetector = new fpgaming.CollisionDetector()
@volatile private var currGame: Option[fpgaming.Game[_, _]] = None

def runGame[S, M](init: S, update: (S, M) => S, view: S => Picture, subscriptions: S => Seq[Sub[M]]): Unit = {
currGame = Some(new gaming.Game(init, update, view, subscriptions))
def runGame[S, M](
init: S,
update: (S, M) => S,
view: S => Picture,
subscriptions: S => Seq[Sub[M]],
refreshRate: Long = 20
): Unit = {
currGame = Some(new fpgaming.Game(init, update, view, subscriptions, refreshRate))
}

def runCommandQuery[M](cmdQ: CmdQ[M]): Unit = currGame.get.asInstanceOf[gaming.Game[_, M]].runCommandQuery(cmdQ)
def runCommandQuery[M](cmdQ: CmdQ[M]): Unit = currGame.get.asInstanceOf[fpgaming.Game[_, M]].runCommandQuery(cmdQ)
}
38 changes: 38 additions & 0 deletions src/main/scala/net/kogics/kojo/lite/CanvasDraw.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ class CanvasDraw(g2d: java.awt.Graphics2D, width: Double, height: Double, val b:
var fillColor: Color = null
var strokeColor: Color = cm.red
var strokeThickness = 2.0
var textFont: Font = b.Font("Sans Serif", 15)
var matrices = List.empty[AffineTransform]
var penCap = ROUND
var penJoin = MITER
Expand Down Expand Up @@ -88,6 +89,7 @@ class CanvasDraw(g2d: java.awt.Graphics2D, width: Double, height: Double, val b:
stroke = makeStroke(strokeThickness, penCap, penJoin)
}

// keep this public so people can draw shapes beyond those supported by CanvasDraw
def drawShape(s: java.awt.Shape): Unit = {
if (fillColor != null) {
g2d.setPaint(fillColor)
Expand All @@ -100,6 +102,40 @@ class CanvasDraw(g2d: java.awt.Graphics2D, width: Double, height: Double, val b:
}
}

private def drawText(s: String, x: Double, y: Double): Unit = {
if (strokeColor != null) {
g2d.setPaint(strokeColor)
g2d.setStroke(stroke)
}
g2d.setFont(textFont)
pushMatrix()
scale(1, -1)
// val te = b.textExtent(s, textFont.getSize, textFont.getName)
// translate(0, te.height)
g2d.drawString(s, x.toFloat, -y.toFloat)
popMatrix()
}

private def drawImage(image: Image, x: Int, y: Int): Unit = {
pushMatrix()
scale(1, -1)
translate(0, -image.getHeight(null))
g2d.drawImage(image, x, -y, null)
popMatrix()
}

def textFont(f: Font): Unit = {
textFont = f
}

def text(s: String, x: Double, y: Double): Unit = {
drawText(s, x, y)
}

def image(img: Image, x: Int, y: Int): Unit = {
drawImage(img, x, y)
}

def ellipse(cx: Double, cy: Double, w: Double, h: Double): Unit = {
tempEllipse.setFrame(cx - w / 2, cy - h / 2, w, h)
drawShape(tempEllipse)
Expand Down Expand Up @@ -139,6 +175,8 @@ class CanvasDraw(g2d: java.awt.Graphics2D, width: Double, height: Double, val b:
g2d.rotate(angle)
}

def rotateDegrees(angle: Double): Unit = rotate(angle.toRadians)

def scale(f: Double): Unit = {
g2d.scale(f, f)
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/scala/net/kogics/kojo/lite/Versions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ package net.kogics.kojo.lite

object Versions {
val KojoLibMajorVersion = "Pre 1.0"
val KojoLibVersion = "0.2.0"
val KojoLibVersion = "0.3.0"
val KojoLibRevision = "r1"
val KojoLibBuildDate = "25 February 2023"
val KojoLibBuildDate = "23 June 2024"
val JavaVersion = {
val jrv = System.getProperty("java.runtime.version")
val arch = System.getProperty("os.arch")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ class SpriteCanvas(val kojoCtx: core.KojoCtx) extends PSwingCanvas with SCanvas
@volatile var turtle = newTurtle()
val pictures = origLayer
// getCamera.addLayer(getCamera.getLayerCount - 1, pictures)
implicit val picCanvas = this
implicit val picCanvas: SpriteCanvas = this

def turtle0 = turtle

Expand Down
70 changes: 69 additions & 1 deletion src/main/scala/net/kogics/kojo/picture/package.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,22 @@ package net.kogics.kojo

import java.awt.event.KeyEvent
import java.awt.geom.GeneralPath
import java.awt.image.BufferedImage
import java.awt.image.BufferedImageOp
import java.awt.image.DataBufferInt
import java.awt.Color
import java.awt.Font
import java.awt.Graphics2D
import java.awt.Image
import java.awt.Paint
import java.awt.Shape
import java.net.URL
import java.util.Random
import javax.swing.JComponent

import com.jhlabs.image.LightFilter
import com.jhlabs.image.LightFilter.Light
import com.jhlabs.image.PointFilter
import com.vividsolutions.jts.geom.Coordinate
import com.vividsolutions.jts.geom.GeometryFactory
import com.vividsolutions.jts.geom.PrecisionModel
Expand Down Expand Up @@ -184,7 +188,7 @@ package object picture {
new Java2DPic(w * scaleOutFactor, h * scaleOutFactor, fn) {
override def draw(): Unit = {
super.draw()
scale(1 / scaleOutFactor)
this.scale(1 / scaleOutFactor)
canvas.animate {
update()
if (stopCheck) {
Expand Down Expand Up @@ -480,4 +484,68 @@ package object picture {
case ep: EffectablePicture => ep
case _ => new EffectableImagePic(freshPic(p))(p.canvas)
}

def toShape(p: Picture): Shape = {
p.draw()
val coords = p.picGeom.getCoordinates
p.erase()
if (coords.length > 0) {
val path = new GeneralPath()
val fc = coords.head
path.moveTo(fc.x, fc.y)
coords.tail.foreach(c => path.lineTo(c.x, c.y))
path.closePath()
path
}
else {
throw new RuntimeException("Unable to convert picture to shape")
}
}

class MaskOp(maskPic: Picture) extends PointFilter {
// var maskImg: BufferedImage = _
var maskPixels: Array[Int] = _
var maskWidth: Int = 0
var maskHeight: Int = 0

def initMaskImg(): Unit = {
maskPic.draw()
val maskImg = maskPic.toImage
maskPic.erase()
maskWidth = maskImg.getWidth; maskHeight = maskImg.getHeight
// maskPixels = new Array[Int](maskWidth * maskHeight)
// maskImg.getRaster.getDataElements(0, 0, maskWidth, maskHeight, maskPixels)
maskPixels = maskImg.getRaster.getDataBuffer.asInstanceOf[DataBufferInt].getData
}

def checkSizes(src: BufferedImage): Unit = {
require(
src.getWidth <= maskWidth && src.getHeight <= maskHeight,
"The mask cannot be smaller than the masked pic"
)
}

override def filter(src: BufferedImage, dest: BufferedImage): BufferedImage = {
initMaskImg()
checkSizes(src)
super.filter(src, dest)
}

def filterRGB(x: Int, y: Int, pixel: Int): Int = {
val alpha = (pixel >> 24) & 0xff
val red = (pixel >> 16) & 0xff
val green = (pixel >> 8) & 0xff
val blue = pixel & 0xff

val maskPixel = maskPixels(x + y * maskWidth) // maskImg.getRGB(x, y)
val mred = (maskPixel >> 16) & 0xff
val mgreen = (maskPixel >> 8) & 0xff
val mblue = maskPixel & 0xff
val mgray = (mred + mgreen + mblue) / 3

val outAlpha = math.min(alpha, mgray)
val outPixel = outAlpha << 24 | red << 16 | green << 8 | blue
outPixel
}
}
}
15 changes: 13 additions & 2 deletions src/main/scala/net/kogics/kojo/picture/picimage.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import java.awt.GradientPaint
import java.awt.RenderingHints

import scala.collection.mutable.ArrayBuffer
import scala.util.control.NonFatal

import com.jhlabs.image.GaussianFilter
import com.jhlabs.image.LightFilter
Expand Down Expand Up @@ -193,7 +194,17 @@ class EffectableImagePic(pic: Picture)(implicit val canvas: SCanvas)

def pimage(img: BufferedImage) = {
val inode: PImage = new PImage(img) {
lazy val picWithEffects: BufferedImage = effects.foldLeft(img) { (imgt, op) => op.filter(imgt) }
lazy val picWithEffects: BufferedImage =
try {
effects.foldLeft(img) { (imgt, op) => op.filter(imgt) }
}
catch {
case NonFatal(e) =>
println(s"Problem - ${e.getMessage}")
erase()
img
}

override def paint(paintContext: PPaintContext): Unit = {
val finalImg = picWithEffects
val g3 = paintContext.getGraphics()
Expand All @@ -207,8 +218,8 @@ class EffectableImagePic(pic: Picture)(implicit val canvas: SCanvas)
}

override def realDraw() = {
pic.draw()
Utils.runInSwingThread {
pic.draw()
picLayer.removeChild(pic.tnode)
tnode.addChild(pimage(pic.toImage))
tnode.translate(pic.bounds.x, pic.bounds.y)
Expand Down
3 changes: 3 additions & 0 deletions src/main/scala/net/kogics/kojo/picture/pics.scala
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,10 @@ trait CorePicOps2 extends GeomPolygon { self: Picture =>
def withPosition(x: Double, y: Double): Picture = PostDrawTransform { pic => pic.setPosition(x, y) }(this)
def withZIndex(zIndex: Int): Picture = PostDrawTransform { pic => pic.setZIndex(zIndex) }(this)
def withClipping(clipShape: Shape): Picture = new ClipPic(this, clipShape)(canvas)
def withClipping(clipPic: Picture): Picture = new ClipPicWithPic(this, clipPic)(canvas)
def withMask(maskPic: Picture): Picture = this.withEffect(new MaskOp(maskPic))
def withPenCapJoin(capJoin: (Int, Int)): Picture = PostDrawTransform { pic => pic.setPenCapJoin(capJoin) }(this)
def withNoPen(): Picture = PostDrawTransform { pic => pic.setNoPen() }(this)
}

trait RedrawStopper extends Picture {
Expand Down
Loading

0 comments on commit fa48850

Please sign in to comment.