diff --git a/dotscape b/dotscape index f9cdd57..206c8ae 100755 Binary files a/dotscape and b/dotscape differ diff --git a/functional-core/app/app-update.sml b/functional-core/app/app-update.sml index ea30ec4..1e44e1c 100644 --- a/functional-core/app/app-update.sml +++ b/functional-core/app/app-update.sml @@ -15,57 +15,69 @@ struct fun mouseMoveOrRelease (model: app_type) = let - val (drawVec, _, _) = ClickPoints.getClickPosition (1.0, 0.0, 0.0, model) - val drawVec = TriangleStage.toVector (model, drawVec) + val drawVec = + case ClickPoints.getClickPositionFromMouse model of + SOME (xpos, ypos) => + ClickPoints.getDrawVec (xpos, ypos, 1.0, 0.0, 0.0, model) + | NONE => Vector.fromList [] val drawMsg = DRAW_DOT drawVec in (model, DRAW drawMsg) end fun mouseLeftClick (model: app_type) = - let - val (dotVec, hpos, vpos) = - ClickPoints.getClickPosition (0.0, 0.0, 1.0, model) - val newUndoTuple = (hpos, vpos) - in - if Vector.length dotVec > 0 then - case #triangleStage model of - NO_TRIANGLE => - let - val drawVec = TriangleStage.toVector (model, dotVec) - val drawMsg = DRAW_DOT drawVec + case ClickPoints.getClickPositionFromMouse model of + SOME (xpos, ypos) => + let + val dotVec = ClickPoints.getDrawVec (xpos, ypos, 0.0, 0.0, 1.0, model) + val {windowWidth, windowHeight, ...} = model + val halfWidth = Real32.fromInt (windowWidth div 2) + val halfHeight = Real32.fromInt (windowHeight div 2) + val hpos = + Ndc.centreAlignX (xpos, windowWidth, windowHeight, halfWidth) + val vpos = + Ndc.centreAlignY (ypos, windowWidth, windowHeight, halfHeight) + val newUndoTuple = (hpos, vpos) + in + (case #triangleStage model of + NO_TRIANGLE => + let + val drawVec = TriangleStage.toVector (model, dotVec) + val drawMsg = DRAW_DOT drawVec - val newTriangleStage = FIRST {x1 = hpos, y1 = vpos} - val model = - AppWith.addTriangleStage (model, newTriangleStage, newUndoTuple) - in - (model, DRAW drawMsg) - end - | FIRST {x1, y1} => - let - val drawVec = TriangleStage.firstToVector (x1, y1, dotVec, model) - val drawMsg = DRAW_DOT drawVec + val newTriangleStage = FIRST {x1 = hpos, y1 = vpos} + val model = + AppWith.addTriangleStage + (model, newTriangleStage, newUndoTuple) + in + (model, DRAW drawMsg) + end + | FIRST {x1, y1} => + let + val drawVec = + TriangleStage.firstToVector (x1, y1, dotVec, model) + val drawMsg = DRAW_DOT drawVec - val newTriangleStage = SECOND - {x1 = x1, y1 = y1, x2 = hpos, y2 = vpos} - val model = - AppWith.addTriangleStage (model, newTriangleStage, newUndoTuple) - in - (model, DRAW drawMsg) - end - | SECOND {x1, y1, x2, y2} => - let - val model = AppWith.addTriangle - (model, x1, y1, x2, y2, hpos, vpos, newUndoTuple) + val newTriangleStage = SECOND + {x1 = x1, y1 = y1, x2 = hpos, y2 = vpos} + val model = + AppWith.addTriangleStage + (model, newTriangleStage, newUndoTuple) + in + (model, DRAW drawMsg) + end + | SECOND {x1, y1, x2, y2} => + let + val model = AppWith.addTriangle + (model, x1, y1, x2, y2, hpos, vpos, newUndoTuple) - val drawVec = Triangles.toVector model - val drawMsg = DRAW_TRIANGLES_AND_RESET_DOTS drawVec - in - (model, DRAW drawMsg) - end - else - (model, NO_MAILBOX) - end + val drawVec = Triangles.toVector model + val drawMsg = DRAW_TRIANGLES_AND_RESET_DOTS drawVec + in + (model, DRAW drawMsg) + end) + end + | NONE => (model, NO_MAILBOX) fun resizeWindow (model, width, height) = let @@ -182,9 +194,7 @@ struct in (model, DRAW drawMsg) end) - | [] => - (* Nothing to redo. *) - (model, NO_MAILBOX) + | [] => (* Nothing to redo. *) (model, NO_MAILBOX) fun toggleGraph (model: app_type) = if #showGraph model then diff --git a/functional-core/app/click-points.sml b/functional-core/app/click-points.sml index c02cc19..4eb7f86 100644 --- a/functional-core/app/click-points.sml +++ b/functional-core/app/click-points.sml @@ -1,9 +1,16 @@ signature CLICK_POINTS = sig val generate: int * int -> Real32.real vector - val getClickPosition: - Real32.real * Real32.real * Real32.real * AppType.app_type - -> Real32.real vector * Real32.real * Real32.real + val getClickPositionFromMouse: AppType.app_type + -> (Real32.real * Real32.real) option + val getDrawVec: + Real32.real + * Real32.real + * Real32.real + * Real32.real + * Real32.real + * AppType.app_type + -> Real32.real vector end structure ClickPoints :> CLICK_POINTS = @@ -25,188 +32,43 @@ struct * *) val range = 15.0 - fun getVerticalClickPos - ( yClickPoints - , idx - , horizontalPos - , mouseX - , mouseY - , r - , g - , b - , windowWidth - , windowHeight - ) = - if idx = Vector.length yClickPoints then - (#[], 0.0, 0.0) + fun getClickPos (clickPoints, mousePos, idx) = + if idx = Vector.length clickPoints then + NONE else let - val curVerticalPos = Vector.sub (yClickPoints, idx) + val curPos = Vector.sub (clickPoints, idx) in - if - mouseY < curVerticalPos - range orelse mouseY > curVerticalPos + range - then - getVerticalClickPos - ( yClickPoints - , idx + 1 - , horizontalPos - , mouseX - , mouseY - , r - , g - , b - , windowWidth - , windowHeight - ) + if mousePos < curPos - range orelse mousePos > curPos + range then + getClickPos (clickPoints, mousePos, idx + 1) else - let - (* calculate normalised device coordinates *) - val halfWidth = Real32.fromInt (windowWidth div 2) - val halfHeight = Real32.fromInt (windowHeight div 2) - val hpos = horizontalPos - halfWidth - val vpos = ~(curVerticalPos - halfHeight) - - (* coordinates to form small box around clicked area *) - val left = (hpos - 5.0) / halfWidth - val right = (hpos + 5.0) / halfWidth - val bottom = (vpos - 5.0) / halfHeight - val top = (vpos + 5.0) / halfHeight - - (* normalised device coordinates of drawVec should be relative - * to actual windowWidth and windowHeight, - * even if not a square, to display cursor position... *) - val drawVec = Ndc.ltrbToVertex (left, top, right, bottom, r, g, b) - in - (* - * ...however, normalised device coordinate of hpos and vpos - * should be relative to the vertical centre - * (if height is greater than width) - * or horizontal centre - * (if width is greater than height). - * - * So, for example, a 900x1000 resolution - * will have clickable points from 50...950, - * in increments of 50. - * Because we always want to show canvas as a square - * with an aspect ratio of 1:1. - * For displaying the click position on the screen, drawVec - * which uses actual windowWidth and windowHeight, is fine. - * However, we want to attach the meaning "start" to 50 - * and "end" to 950, - * so the hpos and vpos stored in the app's triangle list - * subtracts the offset 50 if needed, - * allowing us to treat the coordinates as a 900x900 square. - * - * We may not actually want to render a square to the screen - * if the screen's aspect ratio is not 1:1, - * but it's the responsibility of the rendering code - * which turns triangles into OpenGL vectors - * to do that. - * *) - if windowWidth = windowHeight then - let - val hpos = hpos / halfWidth - val vpos = vpos / halfHeight - in - (drawVec, hpos, vpos) - end - else if windowWidth > windowHeight then - let - val difference = windowWidth - windowHeight - val offset = Real32.fromInt (difference div 2) - val hpos = hpos / (halfWidth - offset) - val vpos = vpos / halfHeight - in - (drawVec, hpos, vpos) - end - else - (* windowHeight > windowWidth *) - let - val difference = windowHeight - windowWidth - val offset = Real32.fromInt (difference div 2) - val hpos = hpos / halfWidth - val vpos = vpos / (halfHeight - offset) - in - (drawVec, hpos, vpos) - end - end + SOME (Vector.sub (clickPoints, idx)) end - fun getHorizontalClickPos - ( xClickPoints - , yClickPoints - , idx - , mouseX - , mouseY - , r - , g - , b - , windowWidth - , windowHeight - ) = - if idx = Vector.length xClickPoints then - (#[], 0.0, 0.0) - else - let - val curPos = Vector.sub (xClickPoints, idx) - in - if mouseX < curPos - range orelse mouseX > curPos + range then - getHorizontalClickPos - ( xClickPoints - , yClickPoints - , idx + 1 - , mouseX - , mouseY - , r - , g - , b - , windowWidth - , windowHeight - ) - else - getVerticalClickPos - ( yClickPoints - , 0 - , curPos - , mouseX - , mouseY - , r - , g - , b - , windowWidth - , windowHeight - ) - end + fun getClickPositionFromMouse (app: AppType.app_type) = + case getClickPos (#xClickPoints app, #mouseX app, 0) of + SOME xPos => + (case getClickPos (#yClickPoints app, #mouseY app, 0) of + SOME yPos => SOME (xPos, yPos) + | NONE => NONE) + | NONE => NONE - (* - * This function returns a vector containing the position data of the - * clicked square. - * If a square wasn't found at the clicked position, - * an empty vector is returned. - *) - fun getClickPosition (r, g, b, app: AppType.app_type) = + fun getDrawVec (xpos, ypos, r, g, b, app: AppType.app_type) = let - val - { xClickPoints - , yClickPoints - , mouseX - , mouseY - , windowWidth - , windowHeight - , ... - } = app + val {windowWidth, windowHeight, ...} = app + + (* calculate normalised device coordinates *) + val halfWidth = Real32.fromInt (windowWidth div 2) + val halfHeight = Real32.fromInt (windowHeight div 2) + val hpos = xpos - halfWidth + val vpos = ~(ypos - halfHeight) + + (* coordinates to form small box around clicked area *) + val left = (hpos - 5.0) / halfWidth + val right = (hpos + 5.0) / halfWidth + val bottom = (vpos - 5.0) / halfHeight + val top = (vpos + 5.0) / halfHeight in - getHorizontalClickPos - ( xClickPoints - , yClickPoints - , 0 - , mouseX - , mouseY - , r - , g - , b - , windowWidth - , windowHeight - ) + Ndc.ltrbToVertex (left, top, right, bottom, r, g, b) end end