diff --git a/.gitignore b/.gitignore index 4fd3781..f84b0e5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ .idea -*.tff +*.ttf *.iml /cm-unicode-0.7.0 /out \ No newline at end of file diff --git a/src/com/kieferlam/maths/Main.kt b/src/com/kieferlam/maths/Main.kt new file mode 100644 index 0000000..469bcd2 --- /dev/null +++ b/src/com/kieferlam/maths/Main.kt @@ -0,0 +1,8 @@ +package com.kieferlam.maths + +import com.kieferlam.maths.fx.Launcher +import javafx.application.Application + +fun main(args: Array) { + Application.launch(Launcher::class.java) +} \ No newline at end of file diff --git a/src/com/kieferlam/maths/fx/Launcher.kt b/src/com/kieferlam/maths/fx/Launcher.kt new file mode 100644 index 0000000..91ed541 --- /dev/null +++ b/src/com/kieferlam/maths/fx/Launcher.kt @@ -0,0 +1,20 @@ +package com.kieferlam.maths.fx + +import com.kieferlam.maths.fx.style.MenubarStyle +import com.kieferlam.maths.fx.view.RootView +import javafx.stage.Stage +import tornadofx.* + +class Launcher : App(RootView::class){ + + init{ + reloadStylesheetsOnFocus() + } + + override fun start(stage: Stage) { + stage.width = 1280.0 + stage.height = 720.0 + stage.title = "Maths" + super.start(stage) + } +} \ No newline at end of file diff --git a/src/com/kieferlam/maths/fx/style/MenubarStyle.kt b/src/com/kieferlam/maths/fx/style/MenubarStyle.kt new file mode 100644 index 0000000..cc896bb --- /dev/null +++ b/src/com/kieferlam/maths/fx/style/MenubarStyle.kt @@ -0,0 +1,17 @@ +package com.kieferlam.maths.fx.style + +import tornadofx.* + +class MenubarStyle : Stylesheet(){ + init{ + menuBar{ + padding = box(0.0.px) + } + menu{ + padding = box(5.0.px, 8.0.px) + } + menuItem{ + padding = box(100.0.px) + } + } +} \ No newline at end of file diff --git a/src/com/kieferlam/maths/fx/view/RootView.kt b/src/com/kieferlam/maths/fx/view/RootView.kt new file mode 100644 index 0000000..970a9d0 --- /dev/null +++ b/src/com/kieferlam/maths/fx/view/RootView.kt @@ -0,0 +1,17 @@ +package com.kieferlam.maths.fx.view + +import com.kieferlam.maths.fx.view.maths.MathsInputPane +import com.kieferlam.maths.fx.view.menu.GlobalMenuBar +import javafx.scene.Parent +import javafx.scene.layout.BorderPane +import tornadofx.* + +class RootView : View(){ + override val root = BorderPane() + + init{ + root.top = GlobalMenuBar() + root.center = MathsInputPane() + } + +} \ No newline at end of file diff --git a/src/com/kieferlam/maths/fx/view/maths/MathsInputPane.kt b/src/com/kieferlam/maths/fx/view/maths/MathsInputPane.kt new file mode 100644 index 0000000..3322d35 --- /dev/null +++ b/src/com/kieferlam/maths/fx/view/maths/MathsInputPane.kt @@ -0,0 +1,117 @@ +package com.kieferlam.maths.fx.view.maths + +import com.kieferlam.maths.fx.view.render.MathsRenderer +import com.kieferlam.maths.fx.view.render.measureString +import com.kieferlam.maths.fx.view.render.operator.OperatorRenderer +import com.kieferlam.postfix.exception.InvalidSyntaxException +import com.kieferlam.postfix.syntax.ExpressionEntity +import com.kieferlam.postfix.syntax.Postfix +import javafx.application.Platform +import javafx.event.EventHandler +import javafx.scene.canvas.Canvas +import javafx.scene.image.Image +import javafx.scene.layout.Pane +import javafx.scene.paint.Color +import tornadofx.add +import java.util.* + +class MathsInputPane : Pane() { + + private val canvas = Canvas() + private val context by lazy { canvas.graphicsContext2D } + + private var inputString = "" + private var postfix = ArrayList() + private var resultString = "NaN" + private var mathsRender = Stack() + + private var isDrawing = false + private val drawThread = Thread({ + isDrawing = true + while (isDrawing) { + if (!this.scene.window.isShowing) isDrawing = false + draw() + Thread.sleep(1000L) + } + }) + + init { + canvas.widthProperty().bind(widthProperty()) + canvas.heightProperty().bind(heightProperty()) + canvas.widthProperty().addListener { _, _, _ -> + draw() + } + canvas.heightProperty().addListener { _, _, _ -> + draw() + } + + add(canvas) + onMousePressed = EventHandler { + this.requestFocus() + } + + onKeyTyped = EventHandler { + when (it.character) { + "\b" -> { + if (inputString.isNotEmpty()) inputString = inputString.substring(0, inputString.length - 1) + } + "\u000D" -> { + } + else -> inputString += it.character + } + + try { + postfix = Postfix.from(inputString) + resultString = Postfix.evaluate(postfix).value.toDouble().toString() + Platform.runLater { + mathsRender = MathsRenderer.render(postfix) + } + } catch (syntax: InvalidSyntaxException) { + "Syntax Error" + } catch (e: Exception) { + e.message!! + } + + draw() + } + + Platform.runLater { + drawThread.start() + } + } + + fun draw() { + with(context) { + clearRect(0.0, 0.0, width, height) + + fill = Color.BLACK + font = OperatorRenderer.FONT + + val displayString = inputString + " = " + resultString + val textSize = measureString(displayString, font) + val xScale = Math.min(width / textSize.width, 1.0) + val yScale = Math.min(height / (textSize.height + (mathsRender.maxBy { it.height }?.height ?: 0.0)), 1.0) + val scale = Math.min(xScale, yScale) + + save() + translate(width * 0.5, height * 0.5) + scale(scale * 0.8, scale * 0.8) + + fillText(displayString, -textSize.width * 0.5, -textSize.height) + + fill = Color.LIGHTGRAY + strokeLine(-canvas.width, -100.0, canvas.width, -100.0) + fill = Color.BLACK + + var x = 0.0 + mathsRender.forEach { + drawImage(it, x - 500, -(it.height * 0.5) + 200.0) + x += it.width + } + + restore() + + } + } + +} \ No newline at end of file diff --git a/src/com/kieferlam/maths/fx/view/menu/FileMenu.kt b/src/com/kieferlam/maths/fx/view/menu/FileMenu.kt new file mode 100644 index 0000000..f8c073e --- /dev/null +++ b/src/com/kieferlam/maths/fx/view/menu/FileMenu.kt @@ -0,0 +1,29 @@ +package com.kieferlam.maths.fx.view.menu + +import javafx.scene.control.Menu +import javafx.scene.control.MenuItem + +class FileMenu : Menu("File") { + init { + items.addAll( + New(), + Open() + ) + } +} + +private class New : MenuItem("New") { + init{ + setOnAction { + + } + } +} + +private class Open : MenuItem("Open") { + init{ + setOnAction { + + } + } +} \ No newline at end of file diff --git a/src/com/kieferlam/maths/fx/view/menu/GlobalMenuBar.kt b/src/com/kieferlam/maths/fx/view/menu/GlobalMenuBar.kt new file mode 100644 index 0000000..291474a --- /dev/null +++ b/src/com/kieferlam/maths/fx/view/menu/GlobalMenuBar.kt @@ -0,0 +1,17 @@ +package com.kieferlam.maths.fx.view.menu + +import com.kieferlam.maths.fx.style.MenubarStyle +import javafx.scene.control.Menu +import javafx.scene.control.MenuBar +import javafx.scene.control.MenuItem +import tornadofx.* + +class GlobalMenuBar : MenuBar(){ + init{ + menus.addAll( + FileMenu() + ) + } + + override fun getUserAgentStylesheet(): String = MenubarStyle().base64URL.toExternalForm() +} \ No newline at end of file diff --git a/src/com/kieferlam/maths/fx/view/render/MathsRenderException.kt b/src/com/kieferlam/maths/fx/view/render/MathsRenderException.kt new file mode 100644 index 0000000..f60ee91 --- /dev/null +++ b/src/com/kieferlam/maths/fx/view/render/MathsRenderException.kt @@ -0,0 +1,3 @@ +package com.kieferlam.maths.fx.view.render + +class MathsRenderException(msg: String = "") : Exception("Unable to render maths expression: $msg") \ No newline at end of file diff --git a/src/com/kieferlam/maths/fx/view/render/MathsRenderUtils.kt b/src/com/kieferlam/maths/fx/view/render/MathsRenderUtils.kt new file mode 100644 index 0000000..d0a5a0c --- /dev/null +++ b/src/com/kieferlam/maths/fx/view/render/MathsRenderUtils.kt @@ -0,0 +1,16 @@ +package com.kieferlam.maths.fx.view.render + +import javafx.scene.Group +import javafx.scene.Scene +import javafx.scene.text.Font +import javafx.scene.text.Text +import tornadofx.* + +fun measureString(string: String, font: Font): Size { + var text = Text(string) + text.font = font + Scene(Group(text)) + return Size(text.layoutBounds.width, text.layoutBounds.height) +} + +data class Size(val width: Double, val height: Double) \ No newline at end of file diff --git a/src/com/kieferlam/maths/fx/view/render/MathsRenderer.kt b/src/com/kieferlam/maths/fx/view/render/MathsRenderer.kt new file mode 100644 index 0000000..2bd23b1 --- /dev/null +++ b/src/com/kieferlam/maths/fx/view/render/MathsRenderer.kt @@ -0,0 +1,36 @@ +package com.kieferlam.maths.fx.view.render + +import com.kieferlam.maths.fx.view.render.operator.OperandRenderer +import com.kieferlam.maths.fx.view.render.operator.OperatorRenderer +import com.kieferlam.postfix.syntax.ExpressionEntity +import com.kieferlam.postfix.syntax.Operand +import com.kieferlam.postfix.syntax.Operator +import com.kieferlam.postfix.syntax.operator.function.FunctionOperator +import javafx.scene.SnapshotParameters +import javafx.scene.canvas.Canvas +import javafx.scene.image.Image +import javafx.scene.image.WritableImage +import java.util.* +import kotlin.collections.ArrayList + +object MathsRenderer { + + fun render(expression: ArrayList): Stack { + var stack = Stack() + for (e in expression) { + if (e.isOperator) { + val numOfOperands = (e as Operator).numberOfParameters + if (stack.size < numOfOperands) throw MathsRenderException("Not enough operands (operator requires $numOfOperands operands).") + val operandImgs = ArrayList() + for (i in 0 until numOfOperands) { + operandImgs.add(stack.pop()) + } + stack.push(OperatorRenderer.getRenderer(e).render(e, *operandImgs.toTypedArray().reversedArray())) + } else { + stack.push(OperandRenderer.render(e as Operand)) + } + } + return stack + } + +} \ No newline at end of file diff --git a/src/com/kieferlam/maths/fx/view/render/operator/DefaultFunctionRenderer.kt b/src/com/kieferlam/maths/fx/view/render/operator/DefaultFunctionRenderer.kt new file mode 100644 index 0000000..42c2bb8 --- /dev/null +++ b/src/com/kieferlam/maths/fx/view/render/operator/DefaultFunctionRenderer.kt @@ -0,0 +1,53 @@ +package com.kieferlam.maths.fx.view.render.operator + +import com.kieferlam.maths.fx.view.render.MathsRenderException +import com.kieferlam.maths.fx.view.render.Size +import com.kieferlam.maths.fx.view.render.measureString +import com.kieferlam.postfix.syntax.Operator +import com.kieferlam.postfix.syntax.operator.function.FunctionOperator +import javafx.geometry.VPos +import javafx.scene.SnapshotParameters +import javafx.scene.canvas.Canvas +import javafx.scene.image.Image +import javafx.scene.paint.Color + +class DefaultFunctionRenderer : OperatorRenderer(FunctionOperator.ABSOLUTE) { + + override fun render(operator: Operator?, vararg operands: Image): Image { + if (operator == null) throw MathsRenderException("Operator is null.") + if(operands.size < operator.numberOfParameters) throw MathsRenderException("Not enough parameters.") + val canvas = Canvas() + val g = canvas.graphicsContext2D + val snapshot = SnapshotParameters() + snapshot.fill = Color.TRANSPARENT +// snapshot.fill = Color.rgb((Math.random() * 255).toInt(), (Math.random() * 255).toInt(), (Math.random() * 255).toInt()) + + val operatorSize = measureString(operator.symbol + "()", FONT) + + val operandSize = Size(operands.sumBy { it.width.toInt() }.toDouble() , operands.maxBy { it.height.toInt() }!!.height) + + canvas.width = operatorSize.width + operandSize.width + canvas.height = operandSize.height + + var operandIndex = 0 + var drawX = 0.0 + + with(g) { + font = OperatorRenderer.FONT + fill = Color.BLACK + textBaseline = VPos.TOP + if(operator.numberOfParameters > 1){ + drawImage(operands[operandIndex], drawX, (canvas.height - operands[operandIndex].height)*0.5) + drawX += operands[operandIndex].width + operandIndex++ + } + fillText(operator.symbol, drawX, ((canvas.height - operatorSize.height) * 0.5) - 15.0) + drawX += operatorSize.width + drawImage(operands[operandIndex], drawX, (canvas.height - operands[operandIndex].height)*0.5) + operandIndex++ + } + + return canvas.snapshot(snapshot, null) + } + +} \ No newline at end of file diff --git a/src/com/kieferlam/maths/fx/view/render/operator/DefaultRenderer.kt b/src/com/kieferlam/maths/fx/view/render/operator/DefaultRenderer.kt new file mode 100644 index 0000000..26dfe32 --- /dev/null +++ b/src/com/kieferlam/maths/fx/view/render/operator/DefaultRenderer.kt @@ -0,0 +1,53 @@ +package com.kieferlam.maths.fx.view.render.operator + +import com.kieferlam.maths.fx.view.render.MathsRenderException +import com.kieferlam.maths.fx.view.render.Size +import com.kieferlam.maths.fx.view.render.measureString +import com.kieferlam.postfix.syntax.Operand +import com.kieferlam.postfix.syntax.Operator +import javafx.geometry.VPos +import javafx.scene.SnapshotParameters +import javafx.scene.canvas.Canvas +import javafx.scene.image.Image +import javafx.scene.paint.Color + +class DefaultRenderer : OperatorRenderer(Operator.PLUS) { + + override fun render(operator: Operator?, vararg operands: Image): Image { + if (operator == null) throw MathsRenderException("Operator is null.") + if(operands.size < operator.numberOfParameters) throw MathsRenderException("Not enough parameters.") + val canvas = Canvas() + val g = canvas.graphicsContext2D + val snapshot = SnapshotParameters() + snapshot.fill = Color.TRANSPARENT +// snapshot.fill = Color.rgb((Math.random() * 255).toInt(), (Math.random() * 255).toInt(), (Math.random() * 255).toInt()) + + val operatorSize = measureString(operator.symbol, OperatorRenderer.FONT) + + val operandSize = Size(operands.sumBy { it.width.toInt() }.toDouble(), operands.maxBy { it.height.toInt() }!!.height) + + canvas.width = operatorSize.width + operandSize.width + canvas.height = operandSize.height + + var operandIndex = 0 + var drawX = 0.0 + + with(g) { + font = OperatorRenderer.FONT + fill = Color.BLACK + textBaseline = VPos.TOP + if(operator.numberOfParameters > 1){ + drawImage(operands[operandIndex], drawX, (canvas.height - operands[operandIndex].height)*0.5) + drawX += operands[operandIndex].width + operandIndex++ + } + fillText(operator.symbol, drawX, ((canvas.height - operatorSize.height) * 0.5) - 15.0) + drawX += operatorSize.width + drawImage(operands[operandIndex], drawX, (canvas.height - operands[operandIndex].height)*0.5) + operandIndex++ + } + + return canvas.snapshot(snapshot, null) + } + +} \ No newline at end of file diff --git a/src/com/kieferlam/maths/fx/view/render/operator/FractionRenderer.kt b/src/com/kieferlam/maths/fx/view/render/operator/FractionRenderer.kt new file mode 100644 index 0000000..bfe3d2f --- /dev/null +++ b/src/com/kieferlam/maths/fx/view/render/operator/FractionRenderer.kt @@ -0,0 +1,63 @@ +package com.kieferlam.maths.fx.view.render.operator + +import com.kieferlam.maths.fx.view.render.MathsRenderException +import com.kieferlam.maths.fx.view.render.Size +import com.kieferlam.maths.fx.view.render.measureString +import com.kieferlam.postfix.syntax.Operator +import com.kieferlam.postfix.syntax.operator.DivideOperator +import javafx.geometry.VPos +import javafx.scene.SnapshotParameters +import javafx.scene.canvas.Canvas +import javafx.scene.image.Image +import javafx.scene.paint.Color +import javafx.scene.shape.StrokeLineCap + +class FractionRenderer : OperatorRenderer(Operator.DIVIDE){ + + companion object { + val LINE_WIDTH = 5.0 + } + + override fun render(operator: Operator?, vararg operands: Image): Image { + if(operands.size < Operator.DIVIDE.numberOfParameters) throw MathsRenderException("Not enough parameters.") + val canvas = Canvas() + val g = canvas.graphicsContext2D + val snapshot = SnapshotParameters() + snapshot.fill = Color.TRANSPARENT +// snapshot.fill = Color.rgb((Math.random() * 255).toInt(), (Math.random() * 255).toInt(), (Math.random() * 255).toInt()) + + val operandSize = Size(operands.maxBy { it.width.toInt() }!!.width, operands.sumByDouble { it.height }) + + canvas.width = operandSize.width + canvas.height = operandSize.height + + with(g) { + font = OperatorRenderer.FONT + fill = Color.BLACK + textBaseline = VPos.TOP + + var y = 0.0 + + //Top operand + drawImage(operands[0], (canvas.width - operands[0].width)*0.5, y) + y += operands[0].height + 4.0 + + //Fraction Line + lineWidth = LINE_WIDTH + lineCap = StrokeLineCap.ROUND + beginPath() + moveTo(0.0, y) + lineTo(canvas.width, y) + stroke() + + y += 4.0 + + //Bottom operand + drawImage(operands[1], (canvas.width - operands[1].width)*0.5, y) + + } + + return canvas.snapshot(snapshot, null) + } + +} \ No newline at end of file diff --git a/src/com/kieferlam/maths/fx/view/render/operator/MinusRenderer.kt b/src/com/kieferlam/maths/fx/view/render/operator/MinusRenderer.kt new file mode 100644 index 0000000..bf185f1 --- /dev/null +++ b/src/com/kieferlam/maths/fx/view/render/operator/MinusRenderer.kt @@ -0,0 +1,47 @@ +package com.kieferlam.maths.fx.view.render.operator + +import com.kieferlam.maths.fx.view.render.MathsRenderException +import com.kieferlam.maths.fx.view.render.Size +import com.kieferlam.maths.fx.view.render.measureString +import com.kieferlam.postfix.syntax.Operator +import javafx.geometry.VPos +import javafx.scene.SnapshotParameters +import javafx.scene.canvas.Canvas +import javafx.scene.image.Image +import javafx.scene.paint.Color +import javafx.scene.shape.StrokeLineCap + +class MinusRenderer : OperatorRenderer(Operator.MINUS) { + + override fun render(operator: Operator?, vararg operands: Image): Image { + if(operands.size < Operator.MINUS.numberOfParameters) throw MathsRenderException("Not enough parameters.") + val canvas = Canvas() + val g = canvas.graphicsContext2D + val snapshot = SnapshotParameters() + snapshot.fill = Color.TRANSPARENT +// snapshot.fill = Color.rgb((Math.random() * 255).toInt(), (Math.random() * 255).toInt(), (Math.random() * 255).toInt()) + + val operatorSize = measureString("\u2212", FONT) + + val operandSize = Size(operands.sumBy { it.width.toInt() }.toDouble(), operands.maxBy { it.height.toInt() }!!.height) + + canvas.width = operatorSize.width + operandSize.width + canvas.height = operandSize.height + + var drawX = 0.0 + + with(g) { + font = OperatorRenderer.FONT + fill = Color.BLACK + textBaseline = VPos.TOP + drawImage(operands[0], drawX, (canvas.height - operands[0].height)*0.5) + drawX += operands[0].width + fillText("\u2212", drawX, ((canvas.height - operatorSize.height) * 0.5) - 15.0) + drawX += operatorSize.width + drawImage(operands[1], drawX, (canvas.height - operands[1].height)*0.5) + } + + return canvas.snapshot(snapshot, null) + } + +} \ No newline at end of file diff --git a/src/com/kieferlam/maths/fx/view/render/operator/MultiplyRenderer.kt b/src/com/kieferlam/maths/fx/view/render/operator/MultiplyRenderer.kt new file mode 100644 index 0000000..d1f052a --- /dev/null +++ b/src/com/kieferlam/maths/fx/view/render/operator/MultiplyRenderer.kt @@ -0,0 +1,46 @@ +package com.kieferlam.maths.fx.view.render.operator + +import com.kieferlam.maths.fx.view.render.MathsRenderException +import com.kieferlam.maths.fx.view.render.Size +import com.kieferlam.maths.fx.view.render.measureString +import com.kieferlam.postfix.syntax.Operator +import javafx.geometry.VPos +import javafx.scene.SnapshotParameters +import javafx.scene.canvas.Canvas +import javafx.scene.image.Image +import javafx.scene.paint.Color + +class MultiplyRenderer : OperatorRenderer(Operator.TIMES) { + + override fun render(operator: Operator?, vararg operands: Image): Image { + if(operands.size < Operator.TIMES.numberOfParameters) throw MathsRenderException("Not enough parameters.") + val canvas = Canvas() + val g = canvas.graphicsContext2D + val snapshot = SnapshotParameters() + snapshot.fill = Color.TRANSPARENT +// snapshot.fill = Color.rgb((Math.random() * 255).toInt(), (Math.random() * 255).toInt(), (Math.random() * 255).toInt()) + + val operatorSize = measureString("\u00D7", FONT) + + val operandSize = Size(operands.sumBy { it.width.toInt() }.toDouble(), operands.maxBy { it.height.toInt() }!!.height) + + canvas.width = operatorSize.width + operandSize.width + canvas.height = operandSize.height + + var drawX = 0.0 + + with(g) { + font = OperatorRenderer.FONT + fill = Color.BLACK + textBaseline = VPos.TOP + drawImage(operands[0], drawX, (canvas.height - operands[0].height)*0.5) + drawX += operands[0].width + fillText("\u00D7", drawX, (canvas.height - operatorSize.height) * 0.5) + drawX += operatorSize.width + drawImage(operands[1], drawX, (canvas.height - operands[1].height)*0.5) + } + + return canvas.snapshot(snapshot, null) + } + +} \ No newline at end of file diff --git a/src/com/kieferlam/maths/fx/view/render/operator/OperandRenderer.kt b/src/com/kieferlam/maths/fx/view/render/operator/OperandRenderer.kt new file mode 100644 index 0000000..ce75efd --- /dev/null +++ b/src/com/kieferlam/maths/fx/view/render/operator/OperandRenderer.kt @@ -0,0 +1,40 @@ +package com.kieferlam.maths.fx.view.render.operator + +import com.kieferlam.maths.fx.view.maths.MathsInputPane +import com.kieferlam.maths.fx.view.render.measureString +import com.kieferlam.postfix.syntax.Operand +import javafx.geometry.VPos +import javafx.scene.SnapshotParameters +import javafx.scene.canvas.Canvas +import javafx.scene.image.Image +import javafx.scene.image.WritableImage +import javafx.scene.paint.Color + +object OperandRenderer { + + fun render(op: Operand): Image { + val canvas = Canvas() + val g = canvas.graphicsContext2D + g.font = OperatorRenderer.FONT + val snapshot = SnapshotParameters() + snapshot.fill = Color.TRANSPARENT + + var text = op.value.toDouble().toString() + if (op.value.toDouble() == op.value.toInt().toDouble()) { + text = op.value.toInt().toString() + } + val size = measureString(text, OperatorRenderer.FONT) + + canvas.width = size.width + canvas.height = size.height + + with(g) { + fill = Color.BLACK + textBaseline = VPos.TOP + fillText(text, 0.0, 0.0) + } + + return canvas.snapshot(snapshot, null) + } + +} \ No newline at end of file diff --git a/src/com/kieferlam/maths/fx/view/render/operator/OperatorRenderer.kt b/src/com/kieferlam/maths/fx/view/render/operator/OperatorRenderer.kt new file mode 100644 index 0000000..43c8422 --- /dev/null +++ b/src/com/kieferlam/maths/fx/view/render/operator/OperatorRenderer.kt @@ -0,0 +1,41 @@ +package com.kieferlam.maths.fx.view.render.operator + +import com.kieferlam.postfix.syntax.Operand +import com.kieferlam.postfix.syntax.Operator +import com.kieferlam.postfix.syntax.operator.function.FunctionOperator +import javafx.scene.SnapshotParameters +import javafx.scene.image.Image +import javafx.scene.text.Font +import java.io.File +import java.io.FileInputStream + +abstract class OperatorRenderer(private val operator: Operator) { + + companion object { + val RENDER_WIDTH = 1280.0 + val RENDER_HEIGHT = 720.0 + val FONT: Font by lazy { Font.loadFont(FileInputStream(File("./cmunrm.ttf")), 192.0) } + + private val RENDERERS = arrayOf( + DefaultRenderer(), + MinusRenderer(), + MultiplyRenderer(), + FractionRenderer(), + PowerRenderer(), + SquareRootRenderer() + ) + + fun getRenderer(op: Operator): OperatorRenderer { + return (1 until RENDERERS.size) + .firstOrNull { RENDERERS[it].isOperatorRenderer(op) } + ?.let { RENDERERS[it] } + ?: RENDERERS[0] + } + + } + + fun isOperatorRenderer(op: Operator): Boolean = op == operator + + abstract fun render(operator: Operator? = null, vararg operands: Image): Image + +} \ No newline at end of file diff --git a/src/com/kieferlam/maths/fx/view/render/operator/PowerRenderer.kt b/src/com/kieferlam/maths/fx/view/render/operator/PowerRenderer.kt new file mode 100644 index 0000000..9dac910 --- /dev/null +++ b/src/com/kieferlam/maths/fx/view/render/operator/PowerRenderer.kt @@ -0,0 +1,47 @@ +package com.kieferlam.maths.fx.view.render.operator + +import com.kieferlam.maths.fx.view.render.MathsRenderException +import com.kieferlam.maths.fx.view.render.Size +import com.kieferlam.postfix.syntax.Operator +import javafx.geometry.VPos +import javafx.scene.SnapshotParameters +import javafx.scene.canvas.Canvas +import javafx.scene.image.Image +import javafx.scene.paint.Color +import javafx.scene.shape.StrokeLineCap +import javafx.scene.text.Font +import java.io.File +import java.io.FileInputStream + +class PowerRenderer : OperatorRenderer(Operator.POWER) { + + override fun render(operator: Operator?, vararg operands: Image): Image { + if (operands.size < Operator.DIVIDE.numberOfParameters) throw MathsRenderException("Not enough parameters.") + val canvas = Canvas() + val g = canvas.graphicsContext2D + val snapshot = SnapshotParameters() + snapshot.fill = Color.TRANSPARENT +// snapshot.fill = Color.rgb((Math.random() * 255).toInt(), (Math.random() * 255).toInt(), (Math.random() * 255).toInt()) + + val operandSize = Size(operands[0].width + (operands[1].width * 0.5), operands[0].height + (operands[1].height * 0.25)) + + canvas.width = operandSize.width + canvas.height = operandSize.height + + with(g) { + font = OperatorRenderer.FONT + fill = Color.BLACK + textBaseline = VPos.TOP + + //Left operand + drawImage(operands[0], 0.0, operands[1].height*0.25) + + //Super operand + drawImage(operands[1], operands[0].width, 0.0, operands[1].width*0.5, operands[1].height*0.5) + + } + + return canvas.snapshot(snapshot, null) + } + +} diff --git a/src/com/kieferlam/maths/fx/view/render/operator/SquareRootRenderer.kt b/src/com/kieferlam/maths/fx/view/render/operator/SquareRootRenderer.kt new file mode 100644 index 0000000..83c322b --- /dev/null +++ b/src/com/kieferlam/maths/fx/view/render/operator/SquareRootRenderer.kt @@ -0,0 +1,56 @@ +package com.kieferlam.maths.fx.view.render.operator + +import com.kieferlam.maths.fx.view.render.MathsRenderException +import com.kieferlam.maths.fx.view.render.Size +import com.kieferlam.postfix.syntax.Operator +import com.kieferlam.postfix.syntax.operator.function.FunctionOperator +import javafx.geometry.VPos +import javafx.scene.SnapshotParameters +import javafx.scene.canvas.Canvas +import javafx.scene.image.Image +import javafx.scene.paint.Color +import javafx.scene.shape.StrokeLineCap +import javafx.scene.shape.StrokeLineJoin + +class SquareRootRenderer : OperatorRenderer(FunctionOperator.SQRT){ + + companion object { + val LINE_WIDTH = 5.0 + } + + override fun render(operator: Operator?, vararg operands: Image): Image { + if(operands.size < FunctionOperator.SQRT.numberOfParameters) throw MathsRenderException("Not enough parameters.") + val canvas = Canvas() + val g = canvas.graphicsContext2D + val snapshot = SnapshotParameters() + snapshot.fill = Color.TRANSPARENT +// snapshot.fill = Color.rgb((Math.random() * 255).toInt(), (Math.random() * 255).toInt(), (Math.random() * 255).toInt()) + + val operandSize = Size(operands[0].width + 50.0, operands[0].height + 20.0) + + canvas.width = operandSize.width + canvas.height = operandSize.height + + with(g) { + font = OperatorRenderer.FONT + fill = Color.BLACK + textBaseline = VPos.TOP + lineWidth = LINE_WIDTH + lineCap = StrokeLineCap.ROUND + lineJoin = StrokeLineJoin.MITER + + beginPath() + moveTo(0.0, canvas.height - 50.0) + lineTo(14.0, canvas.height - 60.0) + lineTo(30.0, canvas.height - 4.0) + lineTo(50.0, 4.0) + lineTo(canvas.width, 4.0) + stroke() + + drawImage(operands[0], 50.0, 8.0) + } + + return canvas.snapshot(snapshot, null) + } + +} \ No newline at end of file diff --git a/src/com/kieferlam/maths/testing/MiscellaneousTests.kt b/src/com/kieferlam/maths/testing/MiscellaneousTests.kt new file mode 100644 index 0000000..deef380 --- /dev/null +++ b/src/com/kieferlam/maths/testing/MiscellaneousTests.kt @@ -0,0 +1,16 @@ +package com.kieferlam.maths.testing + +import com.kieferlam.postfix.syntax.Operator +import com.kieferlam.postfix.syntax.operator.PlusOperator +import org.junit.Test +import kotlin.test.assertEquals + +class MiscTests { + + @Test + fun classTest() { + assertEquals(Operator.javaClass, Operator.javaClass) + assertEquals(Operator.PLUS.javaClass, PlusOperator::class.java) + } + +} \ No newline at end of file