structure NormalMode = struct open AppType open DrawMessage open FileMessage open InputMessage open UpdateMessage fun getDotVecFromIndices (model: app_type, hIdx, vIdx) = let val {windowWidth, windowHeight, xClickPoints, yClickPoints, ...} = model val xpos = Vector.sub (xClickPoints, hIdx) val ypos = Vector.sub (yClickPoints, vIdx) val endXpos = if hIdx + 1 = Vector.length xClickPoints then xpos else Vector.sub (xClickPoints, hIdx + 1) val endYpos = if vIdx + 1 = Vector.length yClickPoints then ypos else Vector.sub (yClickPoints, vIdx + 1) val tl = ClickPoints.getDrawDotRgb (xpos, ypos, 0.0, 0.0, 1.0, windowWidth, windowHeight) val tr = ClickPoints.getDrawDotRgb (endXpos, ypos, 0.0, 0.0, 1.0, windowWidth, windowHeight) val bl = ClickPoints.getDrawDotRgb (xpos, endYpos, 0.0, 0.0, 1.0, windowWidth, windowHeight) val br = ClickPoints.getDrawDotRgb (endXpos, endYpos, 0.0, 0.0, 1.0, windowWidth, windowHeight) in Vector.concat [tl, tr, bl, br] end fun mouseMoveOrRelease (model: app_type) = let val drawVec = case ClickPoints.getClickPositionFromMouse model of SOME (hIdx, vIdx) => getDotVecFromIndices (model, hIdx, vIdx) | NONE => Vector.fromList [] val drawMsg = DRAW_DOT drawVec val drawMsg = [DRAW drawMsg] in (model, drawMsg) end fun getDrawDotMsgWhenArrowIsAtBoundary model = let val {arrowX, arrowY, ...} = model val dotVec = getDotVecFromIndices (model, arrowX, arrowY) val drawMsg = DRAW_DOT dotVec val drawMsg = [DRAW drawMsg] in (model, drawMsg) end fun moveArrowUp (model: app_type) = let val {arrowX, arrowY, ...} = model in if arrowY > 0 then let val newArrowY = arrowY - 1 val model = AppWith.arrowY (model, newArrowY) val dotVec = getDotVecFromIndices (model, arrowX, newArrowY) val drawMsg = DRAW_DOT dotVec val drawMsg = [DRAW drawMsg] in (model, drawMsg) end else getDrawDotMsgWhenArrowIsAtBoundary model end fun moveArrowLeft (model: app_type) = let val {arrowX, arrowY, ...} = model in if arrowX > 0 then let val newArrowX = arrowX - 1 val model = AppWith.arrowX (model, newArrowX) val dotVec = getDotVecFromIndices (model, newArrowX, arrowY) val drawMsg = DRAW_DOT dotVec val drawMsg = [DRAW drawMsg] in (model, drawMsg) end else getDrawDotMsgWhenArrowIsAtBoundary model end fun moveArrowRight (model: app_type) = let val {arrowX, arrowY, xClickPoints, ...} = model in if arrowX < Vector.length xClickPoints - 2 then let val newArrowX = arrowX + 1 val model = AppWith.arrowX (model, newArrowX) val dotVec = getDotVecFromIndices (model, newArrowX, arrowY) val drawMsg = DRAW_DOT dotVec val drawMsg = [DRAW drawMsg] in (model, drawMsg) end else getDrawDotMsgWhenArrowIsAtBoundary model end fun moveArrowDown (model: app_type) = let val {arrowX, arrowY, yClickPoints, ...} = model in if arrowY < Vector.length yClickPoints - 2 then let val newArrowY = arrowY + 1 val model = AppWith.arrowY (model, newArrowY) val dotVec = getDotVecFromIndices (model, arrowX, newArrowY) val drawMsg = DRAW_DOT dotVec val drawMsg = [DRAW drawMsg] in (model, drawMsg) end else getDrawDotMsgWhenArrowIsAtBoundary model end fun realToInt x = Real32.toInt IEEEReal.TO_NEAREST x fun getDrawMessage (model: app_type, initialMsg) = let val { canvasWidth , canvasHeight , layerTree , windowWidth , windowHeight , xClickPoints , yClickPoints , arrowX , arrowY , ... } = model val maxSide = Int.max (canvasWidth, canvasHeight) val squares = LayerTree.flatten (maxSide, layerTree) val dotVec = getDotVecFromIndices (model, arrowX, arrowY) val squares = CollisionTree.toTriangles ( windowWidth , windowHeight , squares , maxSide , canvasWidth , canvasHeight , xClickPoints , yClickPoints ) val drawMsg = DRAW_SQUARES_AND_DOTS {squares = squares, dots = dotVec} val drawMsg = DRAW (drawMsg) :: initialMsg in (model, drawMsg) end fun changePixel (model: app_type, hIdx, vIdx, pixel) = let val {canvasWidth, canvasHeight, layer, layerTree, ...} = model val maxSide = Int.max (canvasWidth, canvasHeight) val layerTree = LayerTree.addPixel (layer, hIdx, vIdx, maxSide, pixel, layerTree) val model = AppWith.layerTree (model, layerTree, hIdx, vIdx) in getDrawMessage (model, []) end fun addPixel (model: app_type, hIdx, vIdx) = let val {r, g, b, a, ...} = model val pixel = {r = r, g = g, b = b, a = a} in changePixel (model, hIdx, vIdx, pixel) end fun deletePixel (model, hIdx, vIdx) = changePixel (model, hIdx, vIdx, Grid.emptyPixel) fun mouseLeftClick model = case ClickPoints.getClickPositionFromMouse model of SOME (hIdx, vIdx) => addPixel (model, hIdx, vIdx) | NONE => (model, []) fun enterOrSpaceCoordinates model = let val {arrowX, arrowY, ...} = model in addPixel (model, arrowX, arrowY) end fun backspace model = let val {arrowX, arrowY, ...} = model in deletePixel (model, arrowX, arrowY) end fun resizeWindow (model, width, height) = let val model = AppWith.windowResize (model, width, height) val {arrowX, arrowY, ...} = model val dots = getDotVecFromIndices (model, arrowX, arrowY) in CommonUpdate.resizeWindow (model, width, height, dots) end fun undoAction model = (model, []) fun redoAction model = (model, []) fun toggleGraph (model: app_type) = if #showGraph model then let val model = AppWith.graphVisibility (model, false) val drawMsg = DRAW_GRAPH (Vector.fromList []) val drawMsg = [DRAW drawMsg] in (model, drawMsg) end else let val model = AppWith.graphVisibility (model, true) val graphLines = GraphLines.generate model val drawMsg = DRAW_GRAPH graphLines val drawMsg = [DRAW drawMsg] in (model, drawMsg) end fun updateNum (model: app_type, newNum) = (AppWith.modalNum (model, newNum), []) fun clearNum model = updateNum (model, 0) fun updateRed model = (AppWith.r model, []) fun updateGreen model = (AppWith.g model, []) fun updateBlue model = (AppWith.b model, []) fun updateAlpha model = (AppWith.a model, []) fun changeLayer model = (AppWith.layer model, []) fun selectCursorColour (model: app_type) = let val {layer, layerTree, arrowX, arrowY, ...} = model in case LayerTree.get (layer, layerTree) of SOME grid => let val yAxis = Vector.sub (grid, arrowX) val {r, g, b, a} = Vector.sub (yAxis, arrowY) val model = AppWith.cursorColour (model, r, g, b, a) in (model, []) end | NONE => (model, []) end fun updateCanvas (model, canvasWidth, canvasHeight) = let val { arrowX , arrowY , windowWidth , windowHeight , xClickPoints , yClickPoints , showGraph , layerTree , ... } = model val dotVec = getDotVecFromIndices (model, arrowX, arrowY) val graphLines = if showGraph then GraphLines.generate model else Vector.fromList [] val maxSide = Int.max (canvasWidth, canvasHeight) val squares = LayerTree.flatten (maxSide, layerTree) val squares = CollisionTree.toTriangles ( windowWidth , windowHeight , squares , maxSide , canvasWidth , canvasHeight , xClickPoints , yClickPoints ) val msg = RESIZE_SQUARES_DOTS_AND_GRAPH {squares = squares, dots = dotVec, graphLines = graphLines} in (model, [DRAW msg]) end fun updateCanvasWidth model = let val {modalNum, layerTree, canvasHeight, ...} = model val newCanvasWidth = modalNum val maxSide = Int.max (newCanvasWidth, canvasHeight) val layerTree = LayerTree.changeGridSize (maxSide, layerTree) val model = AppWith.canvasWidth (model, newCanvasWidth, layerTree) val {canvasWidth, canvasHeight, ...} = model in updateCanvas (model, canvasWidth, canvasHeight) end fun updateCanvasHeight model = let val {modalNum, layerTree, canvasWidth, ...} = model val newCanvasHeight = modalNum val maxSide = Int.max (newCanvasHeight, canvasWidth) val layerTree = LayerTree.changeGridSize (maxSide, layerTree) val model = AppWith.canvasHeight (model, newCanvasHeight, layerTree) val {canvasWidth, canvasHeight, ...} = model in updateCanvas (model, canvasWidth, canvasHeight) end fun useLayers (model, layerTree, canvasWidth, canvasHeight) = let val model = AppWith.parsedLayerTree (model, layerTree, canvasWidth, canvasHeight) val graphLines = if #showGraph model then GraphLines.generate model else Vector.fromList [] val initialMsg = DRAW_GRAPH graphLines val initialMsg = [DRAW initialMsg] in getDrawMessage (model, initialMsg) end fun enterMoveMode model = let val model = AppWith.mode (model, AppType.MOVE_MODE) in (model, []) end fun flipHorizontally (model: app_type) = let val {layerTree, arrowX, arrowY, ...} = model val layerTree = LayerTree.flipHorizontally layerTree val model = AppWith.layerTree (model, layerTree, arrowX, arrowY) in getDrawMessage (model, []) end fun update (model: app_type, inputMsg) = case inputMsg of MOUSE_MOVE {x = mouseX, y = mouseY} => let val model = AppWith.mousePosition (model, mouseX, mouseY) in mouseMoveOrRelease model end | MOUSE_LEFT_RELEASE => mouseMoveOrRelease model | MOUSE_LEFT_CLICK => mouseLeftClick model | NUM num => updateNum (model, num) | KEY_ESC => clearNum model | KEY_R => updateRed model | KEY_G => updateGreen model | KEY_B => updateBlue model | KEY_A => updateAlpha model | KEY_L => changeLayer model | KEY_C => selectCursorColour model | KEY_W => updateCanvasWidth model | KEY_H => updateCanvasHeight model | KEY_M => enterMoveMode model | KEY_F => flipHorizontally model | RESIZE_WINDOW {width, height} => resizeWindow (model, width, height) | UNDO_ACTION => undoAction model | REDO_ACTION => redoAction model | KEY_T => toggleGraph model | KEY_CTRL_S => CommonUpdate.getSaveSquaresMsg model | KEY_CTRL_L => CommonUpdate.getLoadSquaresMsg model | KEY_CTRL_E => CommonUpdate.getExportSquaresMsg model | KEY_CTRL_C => CommonUpdate.getCollisionMsg model | USE_LAYERS {tree, canvasWidth, canvasHeight} => useLayers (model, tree, canvasWidth, canvasHeight) | SQUARES_LOAD_ERROR => CommonUpdate.squaresLoadError model | ARROW_UP => moveArrowUp model | ARROW_LEFT => moveArrowLeft model | ARROW_RIGHT => moveArrowRight model | ARROW_DOWN => moveArrowDown model | KEY_BACKSPACE => backspace model | KEY_ENTER => enterOrSpaceCoordinates model | KEY_SPACE => enterOrSpaceCoordinates model end