diff --git a/build.sbt b/build.sbt index 709192c..9a65cd7 100644 --- a/build.sbt +++ b/build.sbt @@ -21,6 +21,7 @@ skip in packageJSDependencies := false libraryDependencies += "org.scalactic" %% "scalactic" % "3.0.5" libraryDependencies += "org.scalatest" %% "scalatest" % "3.0.5" % "test" +/* // sbt ghpagesPushSite enablePlugins(GhpagesPlugin) enablePlugins(SiteScaladocPlugin) @@ -46,3 +47,4 @@ mappings in makeSite ++= Seq( file(s"${Paths.www}/js/lib/webvr-polyfill.min.js") -> "js/lib/webvr-polyfill.min.js", file("target/scala-2.12/vrdatavisualization-opt.js") -> "js/vrdatavisualization-opt.js" ) +*/ diff --git a/project/build.properties b/project/build.properties new file mode 100644 index 0000000..0531343 --- /dev/null +++ b/project/build.properties @@ -0,0 +1 @@ +sbt.version=1.1.2 diff --git a/project/build.sbt b/project/build.sbt index 909198e..45c500c 100644 --- a/project/build.sbt +++ b/project/build.sbt @@ -1,3 +1,3 @@ addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "1.0.0") addSbtPlugin("org.scala-js" % "sbt-scalajs" % "0.6.22") -addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "1.3.2") +/*addSbtPlugin("com.typesafe.sbt" % "sbt-site" % "1.3.2")*/ diff --git a/project/ghpages.sbt b/project/ghpages.sbt index 06eabbc..e335f67 100644 --- a/project/ghpages.sbt +++ b/project/ghpages.sbt @@ -1 +1 @@ -addSbtPlugin("com.typesafe.sbt" % "sbt-ghpages" % "0.6.2") \ No newline at end of file +/*addSbtPlugin("com.typesafe.sbt" % "sbt-ghpages" % "0.6.2")*/ \ No newline at end of file diff --git a/src/main/scala/facade/Dat.scala b/src/main/scala/facade/Dat.scala index be43d1a..6247ddd 100644 --- a/src/main/scala/facade/Dat.scala +++ b/src/main/scala/facade/Dat.scala @@ -24,12 +24,15 @@ object Dat { } @js.native trait GUI extends Object3D { - def add(something: js.Object): Unit = js.native - def add(something: js.Object, id: String): Unit = js.native + def add(something: js.Object): GuiComponent = js.native + def add(something: js.Object, id: String): GuiComponent = js.native + def add(something: js.Object, id: String, options: js.Array[String]): GuiComponent = js.native // For drop-downs def add(something: js.Object, id: String, min: Double, max: Double): GuiSlider = js.native def addFolder(folder: GUI): GUI = js.native + def addCheckbox(something: js.Object, id: String): GuiComponent = js.native def addButton(fn: js.Function, id: String = "Button"): Unit = js.native def addButton(id: String, fn: js.Function): Unit = js.native + def addDropdown(something: js.Object, id: String, options: js.Array[String]): GuiComponent = js.native def open(): Unit = js.native def close(): Unit = js.native def removeFolder(gui: GUI): Unit = js.native @@ -43,14 +46,22 @@ object Dat { def step(n: Double): GuiSlider = js.native def updateValueLabel(label: String): Unit = js.native def updateObject(obj: js.Any): Unit = js.native + def updateSlider():Unit = js.native } + @js.native + trait GuiCheckbox extends GuiComponent {} + @js.native trait GuiButton extends GuiComponent {} + @js.native + trait GuiDropdown extends GuiComponent {} + @js.native trait GuiComponent extends Object3D { def name(name: String): GuiSlider = js.native + def onChange(callback: js.Function): GuiComponent = js.native } @js.native diff --git a/src/main/scala/vrdv/input/oculus/OculusController.scala b/src/main/scala/vrdv/input/oculus/OculusController.scala index f9f79cf..13c525a 100644 --- a/src/main/scala/vrdv/input/oculus/OculusController.scala +++ b/src/main/scala/vrdv/input/oculus/OculusController.scala @@ -109,11 +109,12 @@ abstract class OculusController(vrc: VRController) extends input.Device { setEventListener(Input.Primary_ValueChanged, (event: Event) => { Log("Primary Value Changed")//; Log(event) val value = primaryValue(event.target) + if(value > 0.05) { - inputDetails.arrow.visible = true + //inputDetails.arrow.visible = true inputDevice.pressed(true) } else { - inputDetails.arrow.visible = false + //inputDetails.arrow.visible = false inputDevice.pressed(false) } @@ -156,6 +157,22 @@ abstract class OculusController(vrc: VRController) extends input.Device { } }) + setEventListener(Input.ThumbRest_TouchBegan, (event: Event) => { + inputDetails.arrow.visible = true + inputParser.passInput(new Point(Some(vrc), myCID) { + rc = inputDetails + persist = true + }) + }) + + setEventListener(Input.ThumbRest_TouchEnded, (event: Event) => { + inputDetails.arrow.visible = false + inputParser.passInput(new Point(Some(vrc), myCID) { + rc = inputDetails + persist = false + }) + }) + setAxesEventListener(Input.Axes_Changed, (event: AxesChangedEvent) => { Log("Axes Changed!") if(captured.nonEmpty) { @@ -202,14 +219,6 @@ abstract class OculusController(vrc: VRController) extends input.Device { Log("Grip Press Ended") }) - setEventListener(Input.ThumbRest_TouchBegan, (event: Event) => { - Log("Thumbrest Touch Began") - }) - - setEventListener(Input.ThumbRest_TouchEnded, (event: Event) => { - Log("Thumbrest Touch Ended") - }) - */ } // end of initCommonEventListeners diff --git a/src/main/scala/vrdv/input/oculus/OculusControllerLeft.scala b/src/main/scala/vrdv/input/oculus/OculusControllerLeft.scala index f9c7e4e..42c1a57 100644 --- a/src/main/scala/vrdv/input/oculus/OculusControllerLeft.scala +++ b/src/main/scala/vrdv/input/oculus/OculusControllerLeft.scala @@ -2,6 +2,7 @@ package vrdv.input.oculus import facade.Dat import facade.IFThree.VRController +import org.scalajs.dom.raw.Event import util.Log import vrdv.model.PlotterModelManager @@ -35,22 +36,70 @@ class OculusControllerLeft(vrc: VRController, val mc: PlotterModelManager) exten // Setup events unique for this controller + setEventListener(Input.Left.Y_PressEnded, (event: Event) => { + mc.plotter.clearSelections() + }) + + setEventListener(Input.Left.X_PressEnded, (event: Event) => { + mc.plotter.toggleGuiVisibility + }) + /* -- Unused inputs setEventListener(Input.Left.X_PressBegan, (event: Event) => { - Log("X Press Began") + Log.show("X Press Began") }) - setEventListener(Input.Left.X_PressEnded, (event: Event) => { - Log("X Press Ended") + setEventListener(Input.Left.Y_PressBegan, (event: Event) => { + Log.show("Y Press Began") }) - setEventListener(Input.Left.Y_PressBegan, (event: Event) => { - Log("Y Press Began") + setEventListener(Input.Left.Menu_PressBegan, (event: Event) => { + Log.show("Home Button Pressed - event: " + event.`type`) }) - setEventListener(Input.Left.Y_PressEnded, (event: Event) => { - Log("Y Press Ended") + setEventListener(Input.Left.Menu_PressEnded, (event: Event) => { + Log.show("Home Button Pressed - event: " + event.`type`) + }) + + setEventListener(Input.Left.Thumbstick_PressBegan, (event: Event) => { + Log.show("Left Thumbstick Button Pressed - event: " + event.`type`) + }) + + setEventListener(Input.Left.Thumbstick_PressEnded, (event: Event) => { + Log.show("Left Thumbstick Button Pressed - event: " + event.`type`) + }) + + setEventListener(Input.Left.Thumbstick_TouchBegan, (event: Event) => { + Log.show("Left Thumbstick Touch Began - event: " + event.`type`) + }) + + setEventListener(Input.Left.Thumbstick_TouchEnded, (event: Event) => { + Log.show("Left Thumbstick Touch Ended - event: " + event.`type`) + }) + + setEventListener(Input.Left.ThumbRest_TouchBegan, (event: Event) => { + Log.show("Left Thumbrest Touch Began - event: " + event.`type`) + }) + + setEventListener(Input.Left.ThumbRest_TouchEnded, (event: Event) => { + Log.show("Left Thumbrest Touch Ended - event: " + event.`type`) + }) + + setEventListener(Input.Left.X_TouchBegan, (event: Event) => { + Log.show("X Touch Began - event: " + event.`type`) + }) + + setEventListener(Input.Left.X_TouchEnded, (event: Event) => { + Log.show("X Touch Ended - event: " + event.`type`) + }) + + setEventListener(Input.Left.Y_TouchBegan, (event: Event) => { + Log.show("Y Touch Began - event: " + event.`type`) + }) + + setEventListener(Input.Left.Y_TouchEnded, (event: Event) => { + Log.show("Y Touch Ended - event: " + event.`type`) }) */ diff --git a/src/main/scala/vrdv/input/oculus/OculusControllerRight.scala b/src/main/scala/vrdv/input/oculus/OculusControllerRight.scala index 6a91247..1719ad2 100644 --- a/src/main/scala/vrdv/input/oculus/OculusControllerRight.scala +++ b/src/main/scala/vrdv/input/oculus/OculusControllerRight.scala @@ -2,6 +2,7 @@ package vrdv.input.oculus import facade.Dat import facade.IFThree.VRController +import org.scalajs.dom.raw.Event import util.Log import vrdv.model.PlotterModelManager @@ -34,22 +35,62 @@ class OculusControllerRight(vrc: VRController, val mc: PlotterModelManager) exte // Setup events unique for this controller + setEventListener(Input.Right.B_PressEnded, (event: Event) => { + mc.plotter.clearSelections() + }) + + setEventListener(Input.Right.A_PressEnded, (event: Event) => { + mc.plotter.toggleGuiVisibility + }) + /* -- Unused inputs setEventListener(Input.Right.A_PressBegan, (event: Event) => { - Log("A Press Began") + Log.show("A Press Began") }) - setEventListener(Input.Right.A_PressEnded, (event: Event) => { - Log("A Press Ended") + setEventListener(Input.Right.B_PressBegan, (event: Event) => { + Log.show("B Press Began") }) - setEventListener(Input.Right.B_PressBegan, (event: Event) => { - Log("B Press Began") + setEventListener(Input.Right.Thumbstick_PressBegan, (event: Event) => { + Log.show("Right Thumbstick Button Pressed - event: " + event.`type`) }) - setEventListener(Input.Right.B_PressEnded, (event: Event) => { - Log("B Press Ended") + setEventListener(Input.Right.Thumbstick_PressEnded, (event: Event) => { + Log.show("Right Thumbstick Button Pressed - event: " + event.`type`) + }) + + setEventListener(Input.Right.ThumbRest_TouchBegan, (event: Event) => { + Log.show("Right Thumbrest Touch Began - event: " + event.`type`) + }) + + setEventListener(Input.Right.Thumbstick_PressBegan, (event: Event) => { + Log.show("Right Thumbstick Button Pressed - event: " + event.`type`) + }) + + setEventListener(Input.Right.Thumbstick_PressEnded, (event: Event) => { + Log.show("Right Thumbstick Button Pressed - event: " + event.`type`) + }) + + setEventListener(Input.Right.ThumbRest_TouchEnded, (event: Event) => { + Log.show("Right Thumbrest Touch Ended - event: " + event.`type`) + }) + + setEventListener(Input.Right.A_TouchBegan, (event: Event) => { + Log.show("A Touch Began - event: " + event.`type`) + }) + + setEventListener(Input.Right.A_TouchEnded, (event: Event) => { + Log.show("A Touch Ended - event: " + event.`type`) + }) + + setEventListener(Input.Right.B_TouchBegan, (event: Event) => { + Log.show("B Touch Began - event: " + event.`type`) + }) + + setEventListener(Input.Right.B_TouchEnded, (event: Event) => { + Log.show("B Touch Ended - event: " + event.`type`) }) */ diff --git a/src/main/scala/vrdv/input/oculus/package.scala b/src/main/scala/vrdv/input/oculus/package.scala index 3a15545..1866753 100644 --- a/src/main/scala/vrdv/input/oculus/package.scala +++ b/src/main/scala/vrdv/input/oculus/package.scala @@ -30,6 +30,18 @@ package object oculus { val X_PressEnded: String = "X press ended" val Y_PressBegan: String = "Y press began" val Y_PressEnded: String = "Y press ended" + val X_TouchBegan: String = "X touch began" + val X_TouchEnded: String = "X touch ended" + val Y_TouchBegan: String = "Y touch began" + val Y_TouchEnded: String = "Y touch ended" + val Thumbstick_PressBegan: String = "thumbstick press began" + val Thumbstick_PressEnded: String = "thumbstick press ended" + val Thumbstick_TouchBegan: String = "thumbstick touch began" + val Thumbstick_TouchEnded: String = "thumbstick touch ended" + //val ThumbRest_TouchBegan: String = "thumbrest touch began" + //val ThumbRest_TouchEnded: String = "thumbrest touch ended" + //val Menu_PressBegan: String = "menu press began" + //val Menu_PressEnded: String = "menu press ended" } object Right { @@ -38,6 +50,16 @@ package object oculus { val A_PressEnded: String = "A press ended" val B_PressBegan: String = "B press began" val B_PressEnded: String = "B press ended" + val A_TouchBegan: String = "A touch began" + val A_TouchEnded: String = "A touch ended" + val B_TouchBegan: String = "B touch began" + val B_TouchEnded: String = "B touch ended" + val Thumbstick_PressBegan: String = "thumbstick press began" + val Thumbstick_PressEnded: String = "thumbstick press ended" + val Thumbstick_TouchBegan: String = "thumbstick touch began" + val Thumbstick_TouchEnded: String = "thumbstick touch ended" + //val ThumbRest_TouchBegan: String = "thumbrest touch began" + //val ThumbRest_TouchEnded: String = "thumbrest touch ended" } // These touch events for Oculus Controls are not registering. (Firefox 61.0.1) @@ -50,4 +72,7 @@ package object oculus { def primaryValue(target: Any): Double = target .asInstanceOf[js.Dynamic].getButton(1).value.asInstanceOf[Double] + def isTouched(target: Any): Boolean = target + .asInstanceOf[js.Dynamic].getButton(1).isTouched.asInstanceOf[Boolean] + } diff --git a/src/main/scala/vrdv/model/Plotter.scala b/src/main/scala/vrdv/model/Plotter.scala index 65e3979..69cfd98 100644 --- a/src/main/scala/vrdv/model/Plotter.scala +++ b/src/main/scala/vrdv/model/Plotter.scala @@ -2,14 +2,15 @@ package vrdv.model import org.scalajs.threejs.{Camera, Intersection, Scene, Vector3} import resources.{Data, Res} -import util.Log +import util.{Log, ScaleCenterProperties} import vrdv.input.{Action, InputDetails, Interactions} import vrdv.obj3D.displays.{CSC_DefaultConfig, ColumnSelectionConsole} import vrdv.obj3D.plots._ -import vrdv.obj3D.{CustomColors, DatGui, Region} +import vrdv.obj3D._ /** * Created by Dorian Thiessen on 2018-07-29. + * Modified by Wade McDonald on 2018-10-02. */ class Plotter(scene: Scene, camera: Camera) extends ModelComponent[Action] { @@ -17,9 +18,9 @@ class Plotter(scene: Scene, camera: Camera) extends ModelComponent[Action] { private var REGIONS: Array[Region] = Array() private var PLOT: Array[Plot] = Array() private var AXES: Array[CoordinateAxes] = Array() - private var GUI: Array[DatGui] = Array() + private var GUI: Array[DatGuiW] = Array() - def addGUI(gui: DatGui): Unit = GUI = GUI :+ gui + def addGUI(gui: DatGuiW): Unit = GUI = GUI :+ gui def addPlot(plot: Plot): Unit = PLOT = PLOT :+ plot def addAxes(axes: CoordinateAxes): Unit = AXES = AXES :+ axes def addRegion(r: Region): Unit = { @@ -36,6 +37,169 @@ class Plotter(scene: Scene, camera: Camera) extends ModelComponent[Action] { // ------ Plot stuff + /*---- New 2018-11-02 ----*/ + + //TODO Maybe don't filter these and check for isDefined when accessing? + def getPlots: Array[Plot] = regions.map(_.maybeGetPlot()).filter(_.isDefined).map(_.get) + def getAxes: Array[CoordinateAxes] = regions.map(_.maybeGetAxes()).filter(_.isDefined).map(_.get) + + def maybeGetRegionForPlot(plot: Plot): Option[Region] = + Option(regions.filter(r => r.maybeGetPlot().getOrElse(Unit) == plot)(0)) + + def getData: Array[Data] = DATA(0) + //def getPlotIndex(plot: Plot): Int = PLOT.indexOf(plot) + def getPlotIndex(plot: Plot): Int = getPlots.indexOf(plot) + def getPlot(index: Int): Plot = PLOT(index) + def replacePlot(oldPlot: Plot, newPlot: Plot): Unit = { + val idx = getPlotIndex(oldPlot) + Log.show("[Plotter] replacing plot, idx = " + idx) + regions(idx).addPlot(newPlot) + AXES(idx) = regions(idx).maybeGetAxes().get + PLOT(idx) = newPlot + Log.show("[Plotter] index of new plot = " + getPlotIndex(newPlot)) + } + + private val initialMenu = new InitialMenu(this) + private var globalMenu: GlobalMenu = null + + def showGlobalMenu: Unit = { + if(globalMenu == null) { + globalMenu = new GlobalMenu(this) + scene.add(globalMenu.object3D) + } + globalMenu.setVisible(true) + } + + def toggleGuiVisibility: Unit = { + Log.show("Toggle GUI visibility.") + val vis = !GUI(0).object3D.visible + for(gui <- GUI) gui.setVisible(vis) + //initialMenu.setVisible(vis) + } + + def setupData(data: Array[Data], pointColor: Double = CustomColors.BLUE_HUE_SHIFT): Unit = { + if (data.isEmpty) return + DATA = DATA :+ data + + if(data.length >= 3) { + //Instantiating the Initial Menu + scene.add(initialMenu.object3D) + + } else { + Log.show("[Plotter] [setupData] Error: less than 3 columns in dataset.") + } + } + + def initPlot3DWithData: Unit = { + val data = DATA(0) + val pointColor = CustomColors.BLUE_HUE_SHIFT + + if (PLOT.length < 3) { + Log.show(s"Data.length == ${data.length}") + + val scatterPlot: ScatterPlot = ScatterPlot(data, Res.getLastLoadedTextureID, pointColor) + + Log("[Regions] - Adding 3D plot to new region") + val i = regions.length + addRegion(Region(i)) + regions(i).addPlot(scatterPlot) + repositionRegions() + + + //val gui = DatGui(scatterPlot, regions(i).maybeGetAxes().get, this) + val gui = new SettingsGui(scatterPlot, regions(i).maybeGetAxes().get, this) + //TODO Fix This + addGUI(gui) + //TODO add Gui to Region + //regions(i).gui = Some(gui) + //regions(i).add(gui.object3D) + scene.add(gui.object3D) + gui.object3D.position.setX(regions(i).object3D.position.x - 0.5) + gui.object3D.position.setY(regions(i).object3D.position.y + 0.5) + gui.object3D.position.setZ(regions(i).object3D.position.z + 1.0) + + //gui.object3D.rotateY(3.14 / 4) + + + //TODO Fix This + addPlot(scatterPlot) + addAxes(regions(i).maybeGetAxes().get) + //addGUI(gui) + + } + } + + def newPlot3DWithData(xCol: Int, yCol: Int, zCol: Int): Plot = { + val data = DATA(0) + val pointColor = CustomColors.BLUE_HUE_SHIFT + val scatterPlot: ScatterPlot = ScatterPlot(data, Res.getLastLoadedTextureID, pointColor) + scatterPlot.switchAxis(0, data(xCol), true) + scatterPlot.switchAxis(1, data(yCol), true) + scatterPlot.switchAxis(2, data(zCol), true) + + scatterPlot + } + + def newPlot2DWithData(columnNumber: Int): Plot = { + val xs = DATA(0)(columnNumber) + val plot2D = TimeSeriesPlot2D(xs) + plot2D + } + + def newShadowManifoldWithData(columnNumber: Int, tau: Int = 1): Plot = { + val data = DATA(0) + + val newSM: ShadowManifold = ShadowManifold(data(columnNumber), tau) + newSM.fixScale() + newSM.setVisiblePointRange(0, newSM.numPoints - 2 * tau) + newSM.requestFullGeometryUpdate() + + newSM + } + + /* + def requestEmbedding(axisID: AxisID, columnIndex: Int, tau: Int, plotIndex: Int = 0): Unit = { + Log.show("[Plotter.requestEmbedding()]") + + /*var regionCreated: Boolean = false + if (plotIndex >= PLOT.length) { + regionCreated = true + val i = regions.length + addRegion(Region(i)) + repositionRegions() + }*/ + + val maybeNewSM: Option[ShadowManifold] = + PLOT(plotIndex) match { + case sm: ShadowManifold => + Log.show(s"embedding from SM->SM with columnIndex: $columnIndex (id: ${DATA(0)(columnIndex).id})") + ShadowManifold.fromShadowManifold(sm)(tau) + case sp: ScatterPlot => ShadowManifold.fromScatterPlot(sp)(tau, axisID) + } + + if (maybeNewSM.isEmpty) { + Log.show("[Plotter.requestEmbedding()] maybeNewSM.isEmpty == true") + return + } + + val newSM: ShadowManifold = maybeNewSM.get + PLOT(plotIndex) = newSM + newSM.fixScale() + newSM.setVisiblePointRange(0, newSM.numPoints - 2 * tau) + newSM.requestFullGeometryUpdate() + + Log.show(s"[Plotter.requestEmbedding()] (x, y, z) = (${newSM.xVar}, ${newSM.yVar}, ${newSM.zVar})") + + AXES(plotIndex) match { + case a2D: CoordinateAxes2D ⇒ a2D.setAxesTitles(newSM.xVar, newSM.yVar) + case a3D: CoordinateAxes3D ⇒ a3D.setAxesTitles(newSM.xVar, newSM.yVar, newSM.zVar) + } + } // -- eof + */ + + /*---- End New ----*/ + + /* def plot2D3D(data: Array[Data], pointColor: Double = CustomColors.BLUE_HUE_SHIFT): Unit = { if (data.isEmpty) return DATA = DATA :+ data @@ -102,6 +266,7 @@ class Plotter(scene: Scene, camera: Camera) extends ModelComponent[Action] { * @param plot Some instance of a class that implements trait Plot. * @return The region the plot was added to if successful, otherwise None. */ + def addPlot3DToRegion(plot: Plot3D): Unit = { Log("[Regions] - Adding 3D plot to new region") val i = regions.length @@ -115,7 +280,7 @@ class Plotter(scene: Scene, camera: Camera) extends ModelComponent[Action] { addPlot(plot) addAxes(regions(i).maybeGetAxes().get) - addGUI(gui) + //addGUI(gui) } /** @@ -132,6 +297,7 @@ class Plotter(scene: Scene, camera: Camera) extends ModelComponent[Action] { addPlot(plot) addAxes(regions(i).maybeGetAxes().get) } + */ def update(): Unit = { for (r <- regions if r.maybeGetAxes().nonEmpty) { @@ -158,28 +324,39 @@ class Plotter(scene: Scene, camera: Camera) extends ModelComponent[Action] { } def setVisiblePointRange(start: Int, end: Int): Unit = { - val plot2D = PLOT(1) - Log.show("Position:") - Log.show(plot2D.getPoints.position) - val prevStart = plot2D.firstVisiblePointIndex - val prevEnd = plot2D.visiblePoints + prevStart - plot2D.setVisiblePointRange(start, end) - - // 1. Shift plot (-x) proportional to |start| - val numPoints = plot2D.numPoints - val points = plot2D.getPoints - val deltaT = 1.0 / numPoints // distance between points along x-axis - - val prevDist2FirstVisible = 1.0 / (prevEnd - prevStart) * prevStart - val dist2FirstVisible = 1.0 / plot2D.visiblePoints * start - points.translateX(prevDist2FirstVisible - dist2FirstVisible) - - // 2. Scale plot (x) proportional to | end - start | - Log.show("Scale: ") - Log.show(points.scale) - val scaleChange = (1.0*(prevEnd - prevStart)) / (1.0*(end - start)) - Log.show(s"Scale Change: $scaleChange") - points.scale.setX(scaleChange * points.scale.x) + + for(plot <- getPlots) { + plot match { + //TODO Case for Plot3D + case plot2D: TimeSeriesPlot2D => { + Log.show("Position:") + Log.show(plot2D.getPoints.position) + val prevStart = plot2D.firstVisiblePointIndex + val prevEnd = plot2D.visiblePoints + prevStart + plot2D.setVisiblePointRange(start, end) + + // 1. Shift plot (-x) proportional to |start| + val numPoints = plot2D.numPoints + val points = plot2D.getPoints + val deltaT = 1.0 / numPoints // distance between points along x-axis + + val prevDist2FirstVisible = 1.0 / (prevEnd - prevStart) * prevStart + val dist2FirstVisible = 1.0 / plot2D.visiblePoints * start + points.translateX(prevDist2FirstVisible - dist2FirstVisible) + + + // 2. Scale plot (x) proportional to | end - start | + Log.show("Scale: ") + Log.show(points.scale) + val scaleChange = (1.0 * (prevEnd - prevStart)) / (1.0 * (end - start)) + Log.show(s"Scale Change: $scaleChange") + points.scale.setX(scaleChange * points.scale.x) + } + case otherPlot: Plot => { + otherPlot.setVisiblePointRange(start, end) + } + } + } } // Applies an axis change. Changes (1) plot point positions, (2) axes titles, and (3) gui labels @@ -187,34 +364,43 @@ class Plotter(scene: Scene, camera: Camera) extends ModelComponent[Action] { Log.show("[requestAxisChange] start") if(columnIndex >= DATA(0).length) return - val axes: CoordinateAxes = AXES(plotIndex) - val gui: DatGui = GUI(plotIndex) - var plot: Plot3D = null + Log.show("[requestAxisChange] getting axes from Plotter plotIndex = " + plotIndex) + //val axes: CoordinateAxes = AXES(plotIndex) + val axes: CoordinateAxes = getAxes(plotIndex) + //val gui: DatGui = GUI(plotIndex) + var plot: Plot = getPlots(plotIndex) - PLOT(plotIndex) match { + Log.show("[requestAxisChange] matching plot") + plot match { case sm: ShadowManifold ⇒ - val sp: ScatterPlot = ScatterPlot.fromShadowManifold(sm) - PLOT(plotIndex) = sp - sp.switchAxis(axisID, DATA(0)(columnIndex)) // assuming a single data source - plot = sp - - case _: ScatterPlot ⇒ - plot = PLOT(plotIndex).asInstanceOf[ScatterPlot] - plot.asInstanceOf[ScatterPlot].switchAxis(axisID, DATA(0)(columnIndex)) // assuming a single data source - gui.updateFolderLabels(plot.xVar, plot.yVar, plot.zVar) - - //case _: ScatterPlot2D ⇒ + sm.asInstanceOf[ShadowManifold].switchAxis(axisID, DATA(0)(columnIndex), sm.tau) + //sm.updateEmbedding() + case sp: ScatterPlot ⇒ + sp.asInstanceOf[ScatterPlot].switchAxis(axisID, DATA(0)(columnIndex)) // assuming a single data source + case sp2D: ScatterPlot2D ⇒ + sp2D.asInstanceOf[ScatterPlot2D].switchAxis(axisID, DATA(0)(columnIndex)) } - gui.updateFolderLabels(plot.xVar, plot.yVar, plot.zVar) - axes.asInstanceOf[CoordinateAxes3D].setAxesTitles(plot.xVar, plot.yVar, plot.zVar) + Log.show("[requestAxisChange] setting axis titles") + //gui.updateFolderLabels(plot.xVar, plot.yVar, plot.zVar) + plot match { + case _: Plot3D => + axes.asInstanceOf[CoordinateAxes3D].setAxesTitles(plot.xVar, plot.yVar, plot.asInstanceOf[Plot3D].zVar) + case _: Plot2D => + axes.asInstanceOf[CoordinateAxes2D].setAxesTitles(plot.xVar, plot.yVar) + } + Log.show("[requestAxisChange] done changing axes") + /* // We set the new variable bound to the 3D Plots x axis to the 2D plots y axes if(axisID == XAxis) { PLOT(plotIndex + 1).asInstanceOf[ScatterPlot2D].switchAxis(YAxis, DATA(0)(columnIndex)) AXES(plotIndex + 1).asInstanceOf[CoordinateAxes2D].setAxisTitle(DATA(0)(columnIndex).id, YAxis) } + */ + /* + Log.show("[requestAxisChange] Updating axis titles...") AXES(plotIndex) match { case a2D: CoordinateAxes2D ⇒ @@ -227,18 +413,36 @@ class Plotter(scene: Scene, camera: Camera) extends ModelComponent[Action] { axisID match { // Currently viewing a scatter-plot, so we can settle with modifying a single axis case XAxis => a3D.setAxisTitle(plot.xVar, XAxis) - gui.updateFolderLabels(x = plot.xVar) + //gui.updateFolderLabels(x = plot.xVar) case YAxis => a3D.setAxisTitle(plot.yVar, YAxis) - gui.updateFolderLabels(y = plot.yVar) + //gui.updateFolderLabels(y = plot.yVar) case ZAxis => - a3D.setAxisTitle(plot.zVar, ZAxis) - gui.updateFolderLabels(z = plot.zVar) + a3D.setAxisTitle(plot.asInstanceOf[Plot3D].zVar, ZAxis) + //gui.updateFolderLabels(z = plot.zVar) } } + */ } // -- eof + def requestEmbeddingUpdate(plot: Plot, columnIndex: Int, tau: Int): Unit = { + Log.show("[Plotter] [requestEmbeddingUpdate] columnIndex = " + columnIndex + " tau = " + tau) + plot match { + case sm: ShadowManifold => sm.switchAxis(XAxis, getData(columnIndex), tau) + case sp: ScatterPlot => { + val newPlot: Plot = newShadowManifoldWithData(columnIndex, tau) + newPlot.asInstanceOf[ShadowManifold].updateEmbedding(tau) + replacePlot(sp, newPlot) + } + } + + AXES(getPlotIndex(plot)) match { + case a2D: CoordinateAxes2D ⇒ a2D.setAxesTitles(plot.xVar, plot.yVar) + case a3D: CoordinateAxes3D ⇒ a3D.setAxesTitles(plot.xVar, plot.yVar, plot.asInstanceOf[ShadowManifold].zVar) + } + } + def requestEmbedding(axisID: AxisID, columnIndex: Int, tau: Int, plotIndex: Int = 0): Unit = { Log.show("[Plotter.requestEmbedding()]") @@ -277,7 +481,6 @@ class Plotter(scene: Scene, camera: Camera) extends ModelComponent[Action] { } } // -- eof - // Point highlighting and such def hoverAction(laser: InputDetails, select: Boolean): Unit = { @@ -332,6 +535,7 @@ class Plotter(scene: Scene, camera: Camera) extends ModelComponent[Action] { val plot = r.plot.get for(p <- plot.selectedPointIndices) PointOperations.deselect(plot, p) plot.selectedPointIndices = plot.selectedPointIndices.empty + PointOperations.updateSelectedSummary(plot) } } } @@ -350,17 +554,32 @@ class Plotter(scene: Scene, camera: Camera) extends ModelComponent[Action] { case 1 => getPos(0).set(0, 1, -2) // north case 2 => + /* getPos(0).set(-1, 1, -1) // north west getPos(1).set(1, 1, -1) // north east + */ + getPos(0).set(0, 1, -2) // north + getPos(1).set(1.5, 1, -2) // north east case 3 => + /* getPos(0).set(-1, 1, -1) // north west getPos(1).set(1, 1, -1) // north east getPos(2).set(-2, 1, 0) // west + */ + getPos(0).set(0, 1, -2) // north + getPos(1).set(1.5, 1, -2) // north east + getPos(2).set(-1.5, 1, -2) // north west case 4 => + /* getPos(0).set(-1, 1, -1) // north west getPos(1).set(1, 1, -1) // north east getPos(2).set(-2, 1, 0) // west getPos(3).set(2, 1, 0) // east + */ + getPos(0).set(0, 1, -2) // north + getPos(1).set(1.5, 1, -2) // north east + getPos(2).set(-1.5, 1, -2) // north west + getPos(3).set(2, 1, 0) // east case _ => // Undefined for more regions } num diff --git a/src/main/scala/vrdv/model/package.scala b/src/main/scala/vrdv/model/package.scala index fad3662..127fdad 100644 --- a/src/main/scala/vrdv/model/package.scala +++ b/src/main/scala/vrdv/model/package.scala @@ -80,13 +80,13 @@ package object model { val white: Color = new Color(0xffffff)//ColorKeywords.white) val floorMaterial: MeshLambertMaterial = new MeshLambertMaterial() floorMaterial.color = white - val floorGeometry: PlaneGeometry = new PlaneGeometry( 1, 1 ) + val floorGeometry: PlaneGeometry = new PlaneGeometry( 4, 4 ) // was width = 1, height = 1 val floor: Mesh = new Mesh(floorGeometry, floorMaterial) floor.receiveShadow = false floor.rotateX(-3.1415/2) // Add 6x6m Grid broken into 36 sections - val floorGrid: GridHelper = new GridHelperExt(1, 2, black, black) + val floorGrid: GridHelper = new GridHelperExt(4, 8, black, black) // was size = 1, step = 2 floorGrid.position.setY(0.001) floorGrid.material.linewidth = 2.0 scene.add(floorGrid) diff --git a/src/main/scala/vrdv/obj3D/DatGui.scala b/src/main/scala/vrdv/obj3D/DatGui.scala index 6085908..187c8a6 100644 --- a/src/main/scala/vrdv/obj3D/DatGui.scala +++ b/src/main/scala/vrdv/obj3D/DatGui.scala @@ -2,6 +2,7 @@ package vrdv.obj3D import facade.Dat import facade.Dat.{GuiButton, GuiSlider} +import util.Log import vrdv.input.InputDetails import vrdv.model.Plotter import vrdv.obj3D.plots._ @@ -23,6 +24,11 @@ class DatGui { // 0: HPD Folder, 1: SPS Folder var folders: Array[Dat.GUI] = Array() + var samples: js.Object = js.Dynamic.literal( + "someBoolean" → false, + "someValue" → "a" + ) + def addFolder(folder: Dat.GUI): Unit = { object3D.addFolder(folder) folders = folders :+ folder @@ -79,6 +85,19 @@ class DatGui { object DatGui { + def sample: DatGui = { + val gui = new DatGui() + // # Checkbox sample + gui.object3D.add(gui.samples, "someBoolean") + + // # Drop-down sample + // NOTE: Position of selected value is lower than it should be. + val dropDownOptions = js.Array("a", "b", "c") + gui.object3D.add(gui.samples, "someValue", dropDownOptions) + + gui + } + def apply(plot: Plot3D, axes: CoordinateAxes, mc: Plotter): DatGui = { val gui = new DatGui() createHighlightedPointDataFolder(gui, plot) @@ -115,6 +134,13 @@ object DatGui { Button(3, embeddingFolder).setLabels("Embed!", "Embed xVar") gui.object3D.addFolder(embeddingFolder) + val testFolder = Dat.GUIVR.create("Testing") + testFolder.addButton(() => { + Log.show("Test button clicked!") + }) + Button(0, testFolder).setLabels("TestButton!", "Test button.") + gui.object3D.addFolder(testFolder) + gui } diff --git a/src/main/scala/vrdv/obj3D/DatGuiW.scala b/src/main/scala/vrdv/obj3D/DatGuiW.scala new file mode 100644 index 0000000..a0dde37 --- /dev/null +++ b/src/main/scala/vrdv/obj3D/DatGuiW.scala @@ -0,0 +1,90 @@ +package vrdv.obj3D + +import facade.Dat +//import facade.Dat.{GuiButton, GuiSlider} +import facade.Dat._ +import util.Log +import vrdv.input.InputDetails +import vrdv.model.Plotter +import vrdv.obj3D.DatGuiW.WButton +import vrdv.obj3D.plots._ + +import scala.scalajs.js + +/** + * Created by Dorian Thiessen on 2018-05-10. + * Modified by Wade MCDonald 2018-11-01 + */ +class DatGuiW(title: String, xInit: Double, yInit: Double, zInit: Double) { + val object3D: Dat.GUI = DatGuiW(title, xInit, yInit, zInit) + + var folders: Array[Dat.GUI] = Array() + + def addFolder(folder: Dat.GUI): Unit = { + object3D.addFolder(folder) + folders = folders :+ folder + } + + def addButton(function: () => Unit, name: String, description: String): Unit = { + val idx = object3D.children(0).children.length + object3D.addButton(function) + WButton(idx, object3D).setLabels(name, description) + } + + def addCheckbox(something: js.Object, id: String, description: String): GuiComponent = { + object3D.add(something, id).name(description) + } + + def addDropdown(something: js.Object, id: String, options: js.Array[String]): GuiComponent = { + //object3D.addDropdown(something, id, options) + object3D.add(something, id, options) + } + + def setVisible(vis: Boolean): Unit = { + object3D.visible = vis + } +} + +/* + def sample: DatGui = { + val gui = new DatGui() + // # Checkbox sample + gui.object3D.add(gui.samples, "someBoolean") + + // # Drop-down sample + // NOTE: Position of selected value is lower than it should be. + val dropDownOptions = js.Array("a", "b", "c") + gui.object3D.add(gui.samples, "someValue", dropDownOptions) + + gui + } + */ + +object DatGuiW { + + def apply(title: String, xInit: Double, yInit: Double, zInit: Double): Dat.GUI = { + val gui = Dat.GUIVR.create(title) + gui.position.set(xInit, yInit, zInit) + //gui.rotateY(3.14/4 * 1.1) + gui + } + + object WButton { + case class WButton(instance: GuiButton) { + + def setLabel_Description(str: String): Unit = instance.name(str) + + def setLabel_Button(str: String): Unit = instance + .children(0).children(1).children(0).children(0) + .asInstanceOf[js.Dynamic].updateLabel(str) + + def setLabels(onButton: String, onDesc: String): Unit = { + setLabel_Button(onButton) + setLabel_Description(onDesc) + } + } + + // Assumes button is not nested in a sub-folder + def apply(i: Int, folder: Dat.GUI): WButton = WButton(folder.children(0).children(i).asInstanceOf[GuiButton]) + } +} diff --git a/src/main/scala/vrdv/obj3D/GlobalMenu.scala b/src/main/scala/vrdv/obj3D/GlobalMenu.scala new file mode 100644 index 0000000..fc6f517 --- /dev/null +++ b/src/main/scala/vrdv/obj3D/GlobalMenu.scala @@ -0,0 +1,96 @@ +package vrdv.obj3D + +import facade.Dat.GuiSlider +import vrdv.model.Plotter +import vrdv.input.InputDetails + +import scala.scalajs.js + + +/** + * Created by Wade McDonald 2018-11-01 + */ +class GlobalMenu(plotter: Plotter) + extends DatGuiW("Global Menu", 2, 1, 0) { + + object3D.rotation.y = -3.14 / 2.0 + + override def setVisible(vis: Boolean): Unit = super.setVisible(vis) + + def addPlot: Unit = { + if(object3D.visible) { + plotter.initPlot3DWithData + //setVisible(false) + //object3D.position.set(2, 1, 0) + //object3D.rotation.y = -3.14 / 2.0 + } + } + + //addButton(() => { addPlot }, "Create", "Graph 1") + //addButton(() => { addPlot }, "Create", "Graph 2") + //addButton(() => { addPlot }, "Create", "Graph 3") + + //Filter Folder + val filterRange: js.Object = js.Dynamic.literal( + "Start" → 0, + "End" → 0, + "Snap 10" → false, + "Snap 100" → false + ) + + def getRange: Range = { + val start = filterRange.asInstanceOf[js.Dynamic].selectDynamic("Start").asInstanceOf[Int] + val end = filterRange.asInstanceOf[js.Dynamic].selectDynamic("End").asInstanceOf[Int] + start to end + } + + val filterFolder = new DatGuiW("Time Filter", 0, 0, 0) + filterFolder.addCheckbox(filterRange, "Snap 10", "Snap to 10").onChange(() => setFilterStep) + filterFolder.addCheckbox(filterRange, "Snap 100", "Snap to 100").onChange(() => setFilterStep) + val numPoints = plotter.getPlot(0).numPoints + val filterLowSlider: GuiSlider = filterFolder.object3D.add(filterRange, "Start", 0, numPoints - 1) + .step(getFilterStep).name("Start index").asInstanceOf[GuiSlider] + val filterHighSlider: GuiSlider = filterFolder.object3D.add(filterRange, "End", 0, numPoints - 1) + .step(getFilterStep).name("End index").asInstanceOf[GuiSlider] + filterFolder.addButton(() => applyFilter, "Filter", "Time Filter") + object3D.addFolder(filterFolder.object3D) + filterFolder.object3D.open() + + def setFilterStep: Unit = { + filterLowSlider.step(getFilterStep) + filterHighSlider.step(getFilterStep) + } + + def getFilterStep: Int = { + if(filterRange.asInstanceOf[js.Dynamic].selectDynamic("Snap 100").asInstanceOf[Boolean]) 100 + else if(filterRange.asInstanceOf[js.Dynamic].selectDynamic("Snap 10").asInstanceOf[Boolean]) 10 + else 1 + } + + def applyFilter = { + val range = getRange + plotter.setVisiblePointRange(range.start, range.end) + } + + //Raycaster Thresholds Folder + + val raycasterThresholds: js.Object = js.Dynamic.literal( + "Right" → 0.1, + "Left" → 0.1 + ) + + def getLeftThreshold: Float = raycasterThresholds.asInstanceOf[js.Dynamic].selectDynamic("Left").asInstanceOf[Float] + def getRightThreshold: Float = raycasterThresholds.asInstanceOf[js.Dynamic].selectDynamic("Right").asInstanceOf[Float] + + val rcThresholdFolder = new DatGuiW("Selection Sensitivity", 0, 0, 0) + rcThresholdFolder.object3D.add(raycasterThresholds, "Left", 0, 0.1).step(0.001).name("Left") + rcThresholdFolder.object3D.add(raycasterThresholds, "Right", 0, 0.1).step(0.001).name("Right") + rcThresholdFolder.addButton(() => InputDetails.updateThresholds(getLeftThreshold, getRightThreshold), + "Update!", "Apply Threshold") + object3D.addFolder(rcThresholdFolder.object3D) + //rcThresholdFolder.object3D.open() + + //Hide Gui Button + addButton(() => {plotter.toggleGuiVisibility}, "Hide", "Hide GUI") + +} \ No newline at end of file diff --git a/src/main/scala/vrdv/obj3D/InitialMenu.scala b/src/main/scala/vrdv/obj3D/InitialMenu.scala new file mode 100644 index 0000000..d74aa9a --- /dev/null +++ b/src/main/scala/vrdv/obj3D/InitialMenu.scala @@ -0,0 +1,34 @@ +package vrdv.obj3D + +import facade.Dat.{GuiComponent, GuiDropdown, GuiSlider} +import util.Log +import vrdv.model.Plotter + +import scala.scalajs.js + + +/** + * Created by Wade McDonald 2018-11-01 + */ +class InitialMenu(plotter: Plotter) + extends DatGuiW("Welcome", 0, 2, -3) { + + override def setVisible(vis: Boolean): Unit = super.setVisible(vis) + + def addPlot: Unit = { + if(object3D.visible) { + plotter.initPlot3DWithData + if(plotter.getPlots.length == 3) setVisible(false) + object3D.position.set(-2, 1, 1) + object3D.rotation.y = 3.14 / 2.0 + plotter.showGlobalMenu + } + } + + addButton(() => { addPlot }, "Create", "Graph 1") + addButton(() => { addPlot }, "Create", "Graph 2") + addButton(() => { addPlot }, "Create", "Graph 3") + + //addButton(() => {plotter.toggleGuiVisibility}, "Hide", "Hide GUI") + +} \ No newline at end of file diff --git a/src/main/scala/vrdv/obj3D/Region.scala b/src/main/scala/vrdv/obj3D/Region.scala index fd35a37..3571796 100644 --- a/src/main/scala/vrdv/obj3D/Region.scala +++ b/src/main/scala/vrdv/obj3D/Region.scala @@ -1,6 +1,7 @@ package vrdv.obj3D import org.scalajs.threejs.Object3D +import util.Log import vrdv.obj3D import vrdv.obj3D.plots._ @@ -15,26 +16,36 @@ case class Region(id: Int, object3D: Object3D = new Object3D()) { var gui: Option[DatGui] = None obj3D.setMoveable(object3D) + def maybeGetPlot(): Option[Plot] = plot + private var maybeAxes: Option[CoordinateAxes] = None def maybeGetAxes(): Option[CoordinateAxes] = maybeAxes def addPlot(p: Plot): Unit = { + val axes: CoordinateAxes = + p match { + case p3D: Plot3D ⇒ + val axes: CoordinateAxes3D = default3DAxes() + axes.createAxesTitles(p3D.xVar, p3D.yVar, p3D.zVar) + axes + case p2D: Plot2D ⇒ + val axes = default2DAxes() + axes.createAxesTitles(p2D.xVar, p2D.yVar) + axes + } + // Remove previous plot if it exists and add the new one - if(plot.nonEmpty) remove(plot.get.getPoints) + if(plot.nonEmpty) { + remove(plot.get.getPoints) + Log.show("[Region] clearing region " + id) + for(c <- object3D.children) {if(Option(c).isDefined) object3D.remove(c)} + } + + Log.show(("[Region] Adding plot...")) plot = Some(p) object3D.add(p.getPoints) - var axes: CoordinateAxes = - p match { - case p3D: Plot3D ⇒ - val axes: CoordinateAxes3D = default3DAxes() - axes.createAxesTitles(p3D.xVar, p3D.yVar, p3D.zVar) - axes - case p2D: Plot2D ⇒ - val axes = default2DAxes() - axes.createAxesTitles(p2D.xVar, p2D.yVar) - axes - } + Log.show(("[Region] Adding axes...")) maybeAxes = Some(axes) add(axes) } diff --git a/src/main/scala/vrdv/obj3D/SettingsGui.scala b/src/main/scala/vrdv/obj3D/SettingsGui.scala new file mode 100644 index 0000000..8dca54f --- /dev/null +++ b/src/main/scala/vrdv/obj3D/SettingsGui.scala @@ -0,0 +1,208 @@ +package vrdv.obj3D + +import facade.Dat._ +import util.Log +import vrdv.input._ +import vrdv.model.Plotter +import vrdv.obj3D.plots._ + +import scala.scalajs.js + +class SettingsGui(plot: Plot, axes: CoordinateAxes, plotter: Plotter) + extends DatGuiW("Graph Settings", axes.position.x - 2.0, axes.position.y + 2.0, axes.position.z){ + + var attachedPlot: Plot = plot + + override def setVisible(vis: Boolean): Unit = super.setVisible(vis) + + val rawTau: js.Object = js.Dynamic.literal( + "TauOnes" -> 0, + "TauTens" -> 0, + "TauHundreds" -> 0) + + val axisData: js.Object = js.Dynamic.literal( + "xAxis" → attachedPlot.xVar, + "yAxis" → attachedPlot.yVar, + "zAxis" → attachedPlot.asInstanceOf[Plot3D].zVar + ) + + val plotType: js.Object = js.Dynamic.literal( + "Graph Type" → "3D Scatter" + ) + val plotTypeOptions = js.Array("3D Scatter", "2D Scatter", "Shadow Manifold") + + def getTau: Int = + rawTau.asInstanceOf[js.Dynamic].selectDynamic("TauOnes").asInstanceOf[Int] + + rawTau.asInstanceOf[js.Dynamic].selectDynamic("TauTens").asInstanceOf[Int] + + rawTau.asInstanceOf[js.Dynamic].selectDynamic("TauHundreds").asInstanceOf[Int] + + val graphTypeDropdown = addDropdown(plotType, "Graph Type", plotTypeOptions) + .onChange(() => {Log.show("Graph type menu changed."); changeGraphType()}) + + def changeGraphType(): Unit = { + val gtString = plotType.asInstanceOf[js.Dynamic].selectDynamic("Graph Type").asInstanceOf[String] + val plotIndex = plotter.getPlotIndex(attachedPlot) + + gtString match { + case "3D Scatter" => { + val xCol = axisTitles.indexOf(axisData.asInstanceOf[js.Dynamic].selectDynamic("xAxis")) + val yCol = axisTitles.indexOf(axisData.asInstanceOf[js.Dynamic].selectDynamic("yAxis")) + val zCol = axisTitles.indexOf(axisData.asInstanceOf[js.Dynamic].selectDynamic("zAxis")) + plotter.replacePlot(attachedPlot, plotter.newPlot3DWithData(xCol, yCol, zCol)) + attachedPlot = plotter.getPlot(plotIndex) + updateFoldersForGraphType("3D Scatter") + } + case "2D Scatter" => { + val columnID = axisTitles.indexOf(axisData.asInstanceOf[js.Dynamic].selectDynamic("yAxis")) + plotter.replacePlot(attachedPlot, plotter.newPlot2DWithData(columnID)) + attachedPlot = plotter.getPlot(plotIndex) + updateFoldersForGraphType("2D Scatter") + } + case "Shadow Manifold" => { + val columnID = axisTitles.indexOf(axisData.asInstanceOf[js.Dynamic].selectDynamic("xAxis")) + val sm = plotter.newShadowManifoldWithData(columnID, getTau) + sm.asInstanceOf[ShadowManifold].updateEmbedding(getTau) + plotter.replacePlot(attachedPlot, sm) + attachedPlot = plotter.getPlot(plotIndex) + updateFoldersForGraphType("Shadow Manifold") + + /* + AXES(plotIndex) match { + case a2D: CoordinateAxes2D ⇒ a2D.setAxesTitles(newSM.xVar, newSM.yVar) + case a3D: CoordinateAxes3D ⇒ a3D.setAxesTitles(newSM.xVar, newSM.yVar, newSM.zVar) + } + */ + } + } + } + + def updateFoldersForGraphType(graphType: String = "3D Scatter"): Unit = { + //Remove any folders currently in the Gui + /* + if(axesFolder.object3D.parent != null) { + axesFolder.object3D.parent.remove(axesFolder.object3D) + } + if(axesFolderXOnly.object3D.parent != null) { + axesFolderXOnly.object3D.parent.remove(axesFolderXOnly.object3D) + } + if(axesFolderYOnly.object3D.parent != null) { + axesFolderYOnly.object3D.parent.remove(axesFolderYOnly.object3D) + } + if(embeddingFolder.object3D.parent != null) { + embeddingFolder.object3D.parent.remove(embeddingFolder.object3D) + } + */ + + //setup correct folders for graph type + graphType match { + case "3D Scatter" => { + //object3D.addFolder(axesFolder.object3D) + //axesFolder.object3D.open() + xDropdown.visible = true + yDropdown.visible = true + zDropdown.visible = true + embeddingFolder.setVisible(false) + embeddingFolder.object3D.close() + } + + case "2D Scatter" => { + //object3D.addFolder(axesFolderYOnly.object3D) + //axesFolderYOnly.object3D.close() + //axesFolderYOnly.object3D.open() + xDropdown.visible = false + yDropdown.visible = true + zDropdown.visible = false + embeddingFolder.setVisible(false) + embeddingFolder.object3D.close() + } + + case "Shadow Manifold" => { + //object3D.addFolder(axesFolderXOnly.object3D) + //axesFolderXOnly.object3D.open() + //object3D.addFolder(embeddingFolder.object3D) + //embeddingFolder.object3D.open() + xDropdown.visible = true + yDropdown.visible = false + zDropdown.visible = false + embeddingFolder.setVisible(true) + embeddingFolder.object3D.open() + } + } + + object3D.close() + object3D.open() + + + } + + //Axes Folder XYZ + val axesFolder = new DatGuiW("Select Data", 0,0,0) + var axisTitles: js.Array[String] = js.Array() + for(d <- plotter.getData.map(_.id)) axisTitles = axisTitles :+ d + val xDropdown = axesFolder.addDropdown(axisData, "xAxis", axisTitles).onChange(() => callForAxisUpdate(0)) + val yDropdown = axesFolder.addDropdown(axisData, "yAxis", axisTitles).onChange(() => callForAxisUpdate(1)) + val zDropdown = axesFolder.addDropdown(axisData, "zAxis", axisTitles).onChange(() => callForAxisUpdate(2)) + object3D.addFolder(axesFolder.object3D) + axesFolder.object3D.open() + + //Axes Folder X Only + val axesFolderXOnly = new DatGuiW("Select Data", 0,0,0) + axesFolderXOnly.addDropdown(axisData, "xAxis", axisTitles).onChange(() => callForAxisUpdate(0)) + //object3D.addFolder(axesFolderXOnly.object3D) + axesFolderXOnly.object3D.open() + + //Axes Folder Y Only + val axesFolderYOnly = new DatGuiW("Select Data", 0,0,0) + axesFolderYOnly.addDropdown(axisData, "yAxis", axisTitles).onChange(() => callForAxisUpdate(1)) + //object3D.addFolder(axesFolderYOnly.object3D) + axesFolderYOnly.object3D.open() + + def callForAxisUpdate(id: Int): Unit = { + val axisString = id match { + case 0 => "xAxis" + case 1 => "yAxis" + case 2 => "zAxis" + } + val columnID = axisTitles.indexOf(axisData.asInstanceOf[js.Dynamic].selectDynamic(axisString)) + val plotIndex = plotter.getPlotIndex(attachedPlot) + Log.show("[SettingsGui] requesting axis change plotIndex = " + plotIndex) + plotter.requestAxisChange(plotIndex, id, columnID) + } + + //Embedding Folder + val embeddingFolder = new DatGuiW("Embedding", 0,0,0) + embeddingFolder.object3D.add(rawTau, "TauOnes", 0, 10).step(1).name("Tau Ones") + embeddingFolder.object3D.add(rawTau, "TauTens", 0, 90).step(10).name("Tau Tens") + embeddingFolder.object3D.add(rawTau, "TauHundreds", 0, 900).step(100).name("Tau Hundreds") + embeddingFolder.addButton(() => updateEmbedding, "Embed!", "Embed Shadow Manifold") + object3D.addFolder(embeddingFolder.object3D) + embeddingFolder.object3D.close() + + def updateEmbedding: Unit = { + Log.show("[SettingsGui] Updating embedding...") + val columnID = axisTitles.indexOf(axisData.asInstanceOf[js.Dynamic].selectDynamic("xAxis")) + plotter.requestEmbeddingUpdate(attachedPlot, columnID, getTau) + } + + + + //Reset button + //TODO Reset Button + //addButton(() => resetToDefaults, "Reset", "Reset to Defaults") + + /* + def resetToDefaults = { + //TODO needs work: controls don't reset display + Log.show("Reset to Defaults Button pressed.") + filterRange.asInstanceOf[js.Dynamic].updateDynamic("Start")(0) + filterRange.asInstanceOf[js.Dynamic].updateDynamic("End")(attachedPlot.numPoints - 1) + applyFilter + + val plotIndex = plotter.getPlotIndex(attachedPlot) + Log.show("[SettingsGui] requesting axis change plotIndex = " + plotIndex) + plotter.requestAxisChange(plotIndex, 0, 0) + plotter.requestAxisChange(plotIndex, 1, 1) + plotter.requestAxisChange(plotIndex, 2, 2) + } + */ +} diff --git a/src/main/scala/vrdv/obj3D/plots/ShadowManifold.scala b/src/main/scala/vrdv/obj3D/plots/ShadowManifold.scala index 0181900..8bdf2ad 100644 --- a/src/main/scala/vrdv/obj3D/plots/ShadowManifold.scala +++ b/src/main/scala/vrdv/obj3D/plots/ShadowManifold.scala @@ -1,7 +1,8 @@ package vrdv.obj3D.plots -import resources.Data +import resources.{Data, Res} import util.{Log, ScaleCenterProperties, Stats} +import vrdv.obj3D.CustomColors import scala.scalajs.js import scala.scalajs.js.typedarray.Float32Array @@ -49,9 +50,45 @@ case class ShadowManifold(var data: Data, var tau: Int, var scp: ScaleCenterProp scp = PointOperations.confineToRegion3D(points, (minX, minY, minZ), (maxX, maxY, maxZ)) } + + def switchAxis(axisID: AxisID, newColumn: Data, newTau: Int): Unit = { + assert(axisID == XAxis, s"[ShadowManifold] [switchAxis] - Attempted switching invalid axis: $axisID") + Log.show("[ShadowManifold] [switchAxis]") + varName = newColumn.id + data = newColumn + + tau = newTau + + updateEmbedding(tau) + } + + def updateEmbedding(newTau: Int): Unit = { + Log.show("[ShadowManifold] [updateEmbedding] tau = " + tau + ", newTau = " + newTau) + val positionsAttr = PointsUtils.positions(getPoints) + val positionsArr = positionsAttr.array.asInstanceOf[Float32Array] + + val firstIndex = 2 * newTau // (E - 1) * Tau + if(firstIndex >= positionsArr.length*3) { + Log.show("[ShadowManifold] [updateEmbedding] tau too high to generate shadow manifold") + return + } // Hard fail - tau too high to generate shadow manifold + for(pointIndex <- firstIndex until varValues.length) { + for(dimension <- 0 to 2) positionsArr((pointIndex - firstIndex)*3 + dimension) = varValues(pointIndex - newTau*dimension).toFloat + } + + Log.show(s"[ShadowManifold] [updateEmbedding] Shadow Manifold made with variable[$varName] with newTau $newTau has ${varValues.length - firstIndex} points.") + + fixScale() + + getPositions.needsUpdate = true + requestFullGeometryUpdate() + } } object ShadowManifold { + def apply(data: Data, tau: Int): ShadowManifold = { + apply(data, Res.getLastLoadedTextureID, CustomColors.BLUE_HUE_SHIFT, tau) + } def apply(data: Data, texture: Int, hue: Double, tau: Int = 1): ShadowManifold = { val (points, props, vStats): (Points, ScaleCenterProperties, Array[Stats]) = PointsBuilder() diff --git a/src/main/scala/vrdv/package.scala b/src/main/scala/vrdv/package.scala index 9b824b0..342e136 100644 --- a/src/main/scala/vrdv/package.scala +++ b/src/main/scala/vrdv/package.scala @@ -63,7 +63,8 @@ package object vrdv { if(maybeModelManager.isEmpty) return val modelManager = maybeModelManager.get if(data.isEmpty) Log.show(" Empty data on plot request") - else modelManager.plotter.plot2D3D(FileAsText(data).collect(), CustomColors.BLUE_HUE_SHIFT) + //else modelManager.plotter.plot2D3D(FileAsText(data).collect(), CustomColors.BLUE_HUE_SHIFT) + else modelManager.plotter.setupData(FileAsText(data).collect(), CustomColors.BLUE_HUE_SHIFT) } /** diff --git a/src/main/scala/vrdv/view/ViewManager.scala b/src/main/scala/vrdv/view/ViewManager.scala index b798c32..3f7ba15 100644 --- a/src/main/scala/vrdv/view/ViewManager.scala +++ b/src/main/scala/vrdv/view/ViewManager.scala @@ -26,7 +26,9 @@ private[vrdv] class ViewManager(mc: RenderRequirements) extends SuppliesRenderer renderer.setSize(window.innerWidth, window.innerHeight) renderer.devicePixelRatio = window.devicePixelRatio - //renderer.vr.enabled = true + + //Comment out the next line to use keyboard controls in browser - VR will not work + renderer.vr.enabled = true renderer.vr.setAnimationLoop(renderVR) dom.window.requestAnimationFrame(renderNonVR)