3.0-M2.di.source-code.p5.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of p5_2.13.0-M2 Show documentation
Show all versions of p5_2.13.0-M2 Show documentation
draw drx types in the processing.org environment
/*
Copyright 2010 Aaron J. Radke
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 cc.drx.p5
//import java.util.Date
//import scala.collection.JavaConverters._
import scala.language.implicitConversions
import scala.collection.concurrent.TrieMap
//import scala.util.{Either,Left,Right}
import processing.core._
import processing.core.PConstants._
import processing.core.PApplet._
import cc.drx._
import java.awt.event._
//import java.awt.event.WindowStateListener
//import java.awt.event.WindowEvent
import java.awt.Frame
/*
TODO: describe p5(data driven drawing) and the similarities but difference between p5(data driven documents)
d3: scales , data merge animations
underscoreio/doodle: shape composition: beside, above, below
node2: data driven, cross platform, realtime, data adjust
processing: low barrier to entry (single file app, no boilerplate code), draw loop, mouse interaction
contextFree, structureSynth: simple method modifications rotate translate color : rx t c
*/
case class RenderMode(className:String) extends AnyVal
object RenderMode{
//def launch = TODO make a launcher??
def find(name:String):Option[RenderMode] = list.find(_.className endsWith name)
lazy val list:List[RenderMode] = List(JAVA2D, FX2D, P2D, P3D, PDF)
lazy val JAVA2D = RenderMode(PConstants.JAVA2D) //canvas
lazy val FX2D = RenderMode(PConstants.FX2D) //javaFX
lazy val P2D = RenderMode(PConstants.P2D) //2d opengl
lazy val P3D = RenderMode(PConstants.P3D) //3d opengl
lazy val PDF = RenderMode(PConstants.PDF) //requires itext??
}
trait P5App{
private def p5className:String = this.getClass.getName.dropRight(1)
def launch:Unit = P5.launch(Array(p5className))
def launch(args:Array[String]):Unit = P5.launch(p5className +: args)
def main(args:Array[String]) = launch(args)
}
object P5{
def launch[A <: PApplet](klass:Class[A]):Unit = PApplet.main(Array(klass.getName)) //TODO add a feature so the classOf is not required
def launch(args:Array[String]):Unit = PApplet.main(args)
@deprecated("use RenderMode instead","v0.2.14")
val renderClasses:Set[String] = Set(JAVA2D, P2D, P3D, FX2D)
@deprecated("use RenderMode instead","v0.2.14")
def renderClass(k:Symbol):String = renderClasses.find(_ contains k.name.toUpperCase) getOrElse JAVA2D
}
@deprecated("use some version of mouseDrag and scale","v0.1.15")
class CanvasZoom{
var poffset = Vec(0,0)
var offset = Vec(0,0)
var scale = 1.0
def zoom(fraction:Double,center:Vec=Vec(0,0)) = {
val tmp = scale - scale*fraction
val new_scale = if(tmp <= 0.0) 0.01 else tmp
val pc = (center - offset)/scale
offset = center - (pc*new_scale)
scale = new_scale
}
def initMove = poffset = offset
def move(relative:Vec) = offset = poffset + relative
def draw(drawFunc:PGraphics=>Unit)(implicit g:PGraphics):Unit = {
g.pushMatrix
g.scale(scale)
g.translate(offset.x/scale, offset.y/scale)
drawFunc(g)
g.popMatrix
}
def draw(drawFunc: =>Unit)(implicit ctx:PApplet):Unit = {
ctx.pushMatrix
ctx.scale(scale)
ctx.translate(offset.x/scale, offset.y/scale)
drawFunc
ctx.popMatrix
}
}
trait FullScreen extends P5{
override def settings = {
fullScreen(renderMode.className)
}
}
class P5 extends PApplet{
import P5._
import Color._
//--wip external launcher frame's
/**Interfacing with java (this will make a jframe) */
lazy val getFrame:javax.swing.JFrame = {
import javax.swing.JFrame
val frame = new JFrame(this.getClass.getSimpleName)
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//TODO get size from the jframe instead
// val h = frame.getSize.getHeight
// val w = frame.getSize.getWidgth
//--add the native surface to the frame
frame.add(getCanvasAWT)
//--adjust frame size
frame.setSize(sketchSize.x.toInt, sketchSize.y.toInt)
frame.setResizable(true)
frame.setVisible(true) //TODO add boolean argument to prevent thread startup
//--TODO add resize listeners...
frame
}
private lazy val getStartedSurface:processing.core.PSurface = {
val ps:processing.core.PSurface = if(surface == null) this.initSurface() else surface //TODO make sure this null check is really the right way to test this, it is required if something else like the applet already made an initSurface
ps.setResizable(true)
ps.setSize(sketchSize.x.toInt, sketchSize.y.toInt)
ps.startThread()//the animation thread //TODO add boolean argument to prevent thread startup
ps //return the that has already been started surface
}
/**Interfacing with javafx (this will also spin up the animation threads)
* https://docs.oracle.com/javase/8/javafx/api/javafx/scene/canvas/Canvas.html
* https://github.com/processing/processing/blob/master/core/src/processing/javafx/PSurfaceFX.java
* */
private def requireJAVA2D = require(renderMode == RenderMode.JAVA2D,s"returning a canvas requires the `override renderMode == JAVA2D`, it is currently set to `$renderMode`")
private def requireFX = require(renderMode == RenderMode.FX2D,s"returning a canvas requires the `override renderMode == FX2D`, it is currently set to `$renderMode`")
lazy val getCanvasFX:javafx.scene.canvas.Canvas = {
requireFX;
getStartedSurface.getNative.asInstanceOf[javafx.scene.canvas.Canvas]
}
/**this stage will make sure the fx app will close on window events*/
private lazy val getStageFXWithoutShow:javafx.stage.Stage = {
requireFX
val jbSurface = JailBreak(getStartedSurface, classOf[processing.javafx.PSurfaceFX])
val stage = jbSurface[javafx.stage.Stage]("stage") //get the private stage value from the psurface object
getCanvasFX.requestFocus() //this is the necessary one to make the keyEvents start working in the embeded FX app, there are lots of places this works, but it seems all stages should have and if it works outside of a runLater then it shoudl be put there as it does
//--stop the fx app on close
stage.setOnCloseRequest(new javafx.event.EventHandler[javafx.stage.WindowEvent]{
override def handle(e:javafx.stage.WindowEvent) = {
javafx.application.Platform.exit()
System.exit(0)
}
})
stage
}
/**this stage will also show the stage in a Platform runLater thread, if you dont' want to auto show it with runLater use getSTageFXWithoutShow*/
lazy val getStageFX:javafx.stage.Stage = {
requireFX
val stage = getStageFXWithoutShow
//--show the stage //TODO maybe don't do this yet to let other apps make modifications first or add a boolean
//TODO the mouse, wheel events work, but for some reason the keyboard events are not working ... the PApplet.launch seems to work though with JAVAFX so somethign with this getStageFX method
javafx.application.Platform.runLater(new Runnable(){def run = {
stage.show() //Note this crashes with a "Not on FX application thread" unless it lives inside the runLater runable
}})
stage //return the fx stage
}
@deprecated("use the more specific getCanvasAWT instead", "v0.2.14")
lazy val getCanvas:java.awt.Canvas = getCanvasAWT
/**Interfacing with java (this will also spin up the animation threads) */
lazy val getCanvasAWT:java.awt.Canvas = {
//requireJAVA2D //TODO require the check for JAVA2D if no other renders also use the java.awt.Canvas
val canvas = getStartedSurface.getNative.asInstanceOf[processing.awt.PSurfaceAWT$SmoothCanvas] //java2d AWT?
canvas.addComponentListener(
new java.awt.event.ComponentAdapter{//ComponentListener{
override def componentResized(e:java.awt.event.ComponentEvent):Unit = {
// println(s"event: $e")
for(c <- Option(e.getComponent)) {
val h = c.getSize.getHeight.toInt
val w = c.getSize.getWidth.toInt
// println(s"Resized native canvas to: $w x $h")
surface.setSize(w,h)
redraw()
}
}
}
)
canvas
}
def sketchSize = Vec(1280,720)
def renderMode:RenderMode = RenderMode.JAVA2D //TODO replace all renderClass settings with the renderMode
/**note this is important that this renderclass is a def so that a val is not loaded as null in the JailBreak override of PApplet.renderer */
@deprecated("use RenderMode instead","v0.2.14")
def renderClass:String = JAVA2D //FX2D // P2D // P3D // OPENGL // JAVA2D // FX2D
//--WARNING the following is an initialization hack to change the default renderer
//this is used in an embedded app since non-launched frames may not use the PApplet.size(_,_,render) setting to set the renderer
private val jailBreak = JailBreak(this,classOf[processing.core.PApplet])
jailBreak("renderer") = renderMode.className
// println("proccessing app found render:" + jailBreak[String]("renderer") + " from renderClass:"+renderClass)
override def settings = {
size(sketchSize.x.toInt, sketchSize.y.toInt, renderMode.className)
//fullScreen(renderMode.className)
smooth(4)
pixelDensity(displayDensity)
}
override def setup() = {
surface.setResizable(true)
frameRate(60)//limit the framerate, no need to over do things
Draw.defaults(g) //background(White)
hint(ENABLE_KEY_REPEAT)
Option(java.awt.SplashScreen.getSplashScreen) foreach {_.close}
}
implicit lazy val keyboard = renderMode match {
case RenderMode.FX2D => Keyboard.Java2d //TODO make sure this works right
case RenderMode.JAVA2D => Keyboard.Java2d
case _ => Keyboard.Jogl
}
// @deprecated("use keyboard.count(up,down) instead", "v0.2.13") //TODO
def keyCount(s:String) = keyboard.count(s(0).toString, s(1).toString)
// @deprecated("use keyboard.count(up) instead", "v0.2.13") //TODO
def keyCount(c:Char) = keyboard.count(c.toString)
// @deprecated("use keyboard("alt-c") instead", "v0.2.13") //TODO
// def isCoded(c:Char):Boolean = keyboard.keyState.codes contains Keyboard.KeyCode(c.toInt)
private var _wheelCount:Int = 0
def wheelCount:Int = _wheelCount
override def mouseWheel(e:processing.event.MouseEvent) = _wheelCount += e.getCount()
/**this should be called at the end of an overridden keyReleasedd*/
override def keyReleased() = {
if(keyboard("f12") || keyboard("print")) save(s"export/screen-${Date.now.krypton take 6}.png") // _savePng = true
keyboard -= Keyboard.KeyCode(if (key==CODED) -keyCode else keyCode)
if(keyboard("shift-f12") || keyboard("shift-print")) OS.browse(f"export") // open the export directory
//if(key == CODED && keyCode == 154 /*PrtScn*/)
//if(key == 'q') {stop; dispose; System.exit(0)}
//if(coded(ALT) && key == 'q'){ stop; dispose; System.exit(0)}
}
def logKeyEvent(root:String="KeyEvent") = println(s"$root key:'$key' key.toInt:${key.toInt} keyCode:$keyCode coded:${key == CODED}")
override def keyPressed() = {
keyboard += Keyboard.KeyCode(if (key==CODED) -keyCode else keyCode)
if(key == ESC) key = 0 //hack to stop processing from quiting the application on ESC
}
/**clear UI state like the keyHeld state, keyCount, WheelCount*/
def clearState() = {
keyboard.clear()
_wheelCount = 0 //TODO roll in wheel count ot the Keyboard state
mouseDrag = None
}
override def clear() = {
super.clear()
clearState()
}
/**defines the line that the current mouseDrag has drawn, returns None if no mouse drag*/
var mouseDrag:Option[Line] = None //TODO make the var private
/**defines the press of an event, this value is setup at the begining of a draw loop and cleared at the end*/
var mousePress:Option[Vec] = None //TODO make the var private
override def mouseDragged = {
mouseDrag = mouseDrag match {
case Some(Line(start,_)) => Some(Line(start,mouse))
case None => Some(Line(mouse,mouse))
}
}
override def mouseReleased = {
super.mouseReleased
mouseDrag = None
mousePress = None
}
override def mousePressed = {
super.mousePressed() //the () are required to differentiate between the callback and the variable name
mousePress = Some(mouse)
}
//---mouse functions as vectors
def mouse = Vec(mouseX,mouseY)
def pmouse = Vec(pmouseX,pmouseY)
def size = Vec(width,height)
def screen = Rect(Vec(0,0),size)
//def center = size/2
//def gmouse = frame.loc + mouse
@deprecated("just use surface.setTitle since procesing doesn't always use frames or overload the resource /icon/icon-{sz}.png files", "0.1.15")
def setAppIcon(title:String,img:PImage,iconSize:Int=64) = {
//val pg = draw(iconSize,iconSize){_.image(img,0,0,iconSize,iconSize)}
//frame.setIconImage(pg.image)
surface.setTitle(title)
}
//---new draw factories
@deprecated("Use implicit RichPGraphics.draw{drawFunc} instead","v0.2.0")
def draw(drawFunc: => Unit)(implicit g:PGraphics):PGraphics= {
draw{g => drawFunc}
}
@deprecated("This makes it far to easy to make expensive graphics buffers use createGraphics(w,h) instead","v0.2.0")
def draw(drawFunc:PGraphics => Unit)(implicit g:PGraphics):PGraphics= {
g.beginDraw
g.pushMatrix
drawFunc(g)
g.popMatrix
g.endDraw
g
}
override def createGraphics(w:Int,h:Int):PGraphics = Draw.defaults(super.createGraphics(w,h))
def launch:Unit = PApplet.main(Array(this.getClass.getName))
}