Skip to content

Commit

Permalink
Further progress in #39
Browse files Browse the repository at this point in the history
  • Loading branch information
v6ak committed Sep 30, 2023
1 parent 1498800 commit 9167cd8
Show file tree
Hide file tree
Showing 17 changed files with 444 additions and 144 deletions.
11 changes: 11 additions & 0 deletions client/src/main/scala/com/v6ak/scalajs/time/Seconds.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.v6ak.scalajs.time

case class Seconds(totalSeconds: Int) extends AnyVal {
def hours: Int = totalSeconds / 3600

def minutes: Int = (totalSeconds / 60) % 60

def seconds: Int = totalSeconds % 60

override def toString: String = f"$hours:$minutes:$seconds"
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,12 @@ case class TimeInterval(totalMinutes: Int) extends AnyVal{
Pace(paceSecPerKm.round(MathContext.UNLIMITED).toInt)
}

def /(divisor: Int): Seconds = Seconds(totalMinutes * 60 / divisor)

def hours = totalMinutes/60
def minutes = totalMinutes%60
def -(other: TimeInterval) = TimeInterval(this.totalMinutes - other.totalMinutes)
def +(other: TimeInterval) = TimeInterval(this.totalMinutes + other.totalMinutes)
override def toString: String = f"$hours:$minutes%02d"
}

Expand Down
2 changes: 2 additions & 0 deletions client/src/main/scala/com/v6ak/zbdb/Bootstrap.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@ object Bootstrap {
val toggle = data.toggle
val dismiss = data.dismiss
def glyphicon(name: String) = span(`class`:=s"glyphicon glyphicon-${name}", aria.hidden := "true")
def btn = button(`class` := "btn")
def btnDefault = btn(`class` := "btn-default")
}
3 changes: 3 additions & 0 deletions client/src/main/scala/com/v6ak/zbdb/Checkpoint.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.v6ak.zbdb

final case class Checkpoint(pos: Int, place: String, cumLen: BigDecimal)
44 changes: 44 additions & 0 deletions client/src/main/scala/com/v6ak/zbdb/ClassSwitches.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.v6ak.zbdb

import org.scalajs.dom
import org.scalajs.dom._
import scalatags.JsDom.all._


final class ClassSwitches(initialSwitchesState: Map[String, String]) {
private val switchesState = collection.mutable.Map(initialSwitchesState.toSeq: _*)

def values = switchesState.values

private def update(switchName: String, newClass: String, allValues: Set[String]) = {
val oldClasses = allValues - newClass
val classList = dom.document.body.classList
classList.add(newClass)
oldClasses.foreach(classList.remove)
switchesState(switchName) = newClass
}

def classSelect(switchName: String)(items: (String, String)*) = select(
onchange := { e: Event =>
val el = e.currentTarget.asInstanceOf[HTMLSelectElement]
update(switchName, el.value, items.map(_._1).toSet)
}
)(
for ((cls, name) <- items) yield
option(value := cls, if (cls == switchesState(switchName)) selected := true else "")(name)
)

def checkbox(switchName: String, description: Frag)(onClass: String, offClass: String) = label(
input(
`type` := "checkbox",
if (onClass == switchesState(switchName)) checked := true else "",
onchange := { e: Event =>
val el = e.currentTarget.asInstanceOf[HTMLInputElement]
update(switchName, if (el.checked) onClass else offClass, Set(onClass, offClass))
}
),
" ",
description,
)

}
2 changes: 2 additions & 0 deletions client/src/main/scala/com/v6ak/zbdb/HtmlUtils.scala
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,6 @@ object HtmlUtils {

@inline def fseq(frags: Frag*): Seq[Frag] = frags

def spaceSeparated(values: Frag*): Frag = values.map(fseq(_, " "))

}
3 changes: 3 additions & 0 deletions client/src/main/scala/com/v6ak/zbdb/Overtakes.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.v6ak.zbdb

final case class Overtakes(overtook: Seq[Participant], overtakenBy: Seq[Participant])
2 changes: 1 addition & 1 deletion client/src/main/scala/com/v6ak/zbdb/Parser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ object Parser{
cumulativeTrackLength = parseTrackLength(cumulativeTrackLengthString)
)
} catch {
case e =>
case e: Throwable =>
throw new RuntimeException(s"error when parsing header: $header", e)
}
case unmatchedHeader =>
Expand Down
10 changes: 5 additions & 5 deletions client/src/main/scala/com/v6ak/zbdb/PartTimeInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,16 @@ abstract sealed class PartTimeInfo{
def durationOption: Option[Int] = endTimeOption.map(_ - startTime)
def lastTime: Moment
def hasEndTime: Boolean = endTimeOption.isDefined
def outran(other: PartTimeInfo.Finished): Boolean
def outran(other: PartTimeInfo): Boolean = other match {
case of: PartTimeInfo.Finished => outran(of)
def overtook(other: PartTimeInfo.Finished): Boolean
def overtook(other: PartTimeInfo): Boolean = other match {
case of: PartTimeInfo.Finished => overtook(of)
case _ => false
}
}

object PartTimeInfo{
final case class Finished(startTime: Moment, endTime: Moment, intervalTime: TimeInterval) extends PartTimeInfo {
def outran(other: Finished): Boolean = this.startTime >= other.startTime && this.endTime < other.endTime
def overtook(other: Finished): Boolean = this.startTime >= other.startTime && this.endTime < other.endTime

def crosses(other: Finished): Boolean = ((this.startTime >= other.startTime) && (this.endTime <= other.endTime)) || ((this.startTime <= other.startTime) && (this.endTime >= other.endTime))

Expand All @@ -32,6 +32,6 @@ object PartTimeInfo{

override def lastTime: Moment = startTime

override def outran(other: Finished): Boolean = false
override def overtook(other: Finished): Boolean = false
}
}
14 changes: 12 additions & 2 deletions client/src/main/scala/com/v6ak/zbdb/Participant.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,20 @@ final case class Participant(

def fullName: String = firstName+" "+lastName

def pauses: Seq[Int] = {
def pauses: Seq[Pause] = {
dom.console.log("pauses: partTimes.size", partTimes.size)
dom.console.log("pauses: partTimes.sliding(2).size", partTimes.sliding(2).size)
partTimes.sliding(2).map{case Seq(previous, next) => next.startTime - previous.endTimeOption.get}.toSeq
partTimes match {
case Seq() | Seq(_) => Seq()
case partTimesWithPauses => partTimesWithPauses.sliding(2).map { case Seq(previous, next) =>
Pause(
startTime = previous.endTimeOption.get,
endTime = next.startTime
)
}.toSeq
}
}

def pauseTimes = pauses.map(p => p.endTime - p.startTime)

}
92 changes: 90 additions & 2 deletions client/src/main/scala/com/v6ak/zbdb/ParticipantTable.scala
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
package com.v6ak.zbdb

import com.example.moment.Moment
import com.v6ak.scalajs.time.TimeInterval
import com.v6ak.scalajs.time.TimeInterval.TimeIntervalOrdering
import com.v6ak.zbdb.PartTimeInfo.Finished
import scala.language.implicitConversions

final case class FullPartInfo(
previousPartMetaOption: Option[Part],
partMeta: Part,
part: PartTimeInfo,
nextPartOption: Option[PartTimeInfo],
)

object ParticipantTable {
}
Expand Down Expand Up @@ -36,7 +44,7 @@ final case class ParticipantTable (startTime: Moment, parts: Seq[Part], data: Se
val maxSize = a.size max b.size
val ea = expandSeq(a, emptyA, maxSize)
val eb = expandSeq(b, emptyB, maxSize)
(ea, eb).zipped
ea lazyZip eb
}

val firsts = data.foldLeft(Seq.empty[BestParticipantData]){ (fastestSoFar, participant) =>
Expand All @@ -48,6 +56,7 @@ final case class ParticipantTable (startTime: Moment, parts: Seq[Part], data: Se
}

def partData(row: Participant, pos: Int) = row.partTimes.lift(pos)
def pauseData(row: Participant, pos: Int) = row.pauses.lift(pos)

def finishedPartData(row: Participant, pos: Int): Option[PartTimeInfo.Finished] = partData(row, pos).collect {
case x: PartTimeInfo.Finished => x
Expand All @@ -61,5 +70,84 @@ final case class ParticipantTable (startTime: Moment, parts: Seq[Part], data: Se
}
}

def filterOthersPauses(pos: Int, current: Participant)(f: (Pause, Pause) => Boolean) = {
val currentPartInfo = current.pauses(pos)
data.filter { p =>
(p != current) &&
pauseData(p, pos).exists(f(currentPartInfo, _))
}
}

private def partWalkEvents(row: Participant)(
fullPartInfo: FullPartInfo,
pos: Int,
): Seq[WalkEvent] = {
import fullPartInfo._
val checkpoint = Checkpoint(pos, partMeta.place, partMeta.cumulativeTrackLength)
Seq(
WalkEvent.Departure(
time = part.startTime,
togetherWith = filterOthers(pos, row)((me, other) => me.startTime isSame other.startTime),
checkpoint = checkpoint,
isStart = previousPartMetaOption.isEmpty
)
) ++ (
part match {
case PartTimeInfo.Finished(_startTime, endTime, intervalTime) =>
val isFinish = pos == parts.size - 1
//noinspection ConvertibleToMethodValue
Seq(
WalkEvent.Walk(
duration = intervalTime,
len = partMeta.trackLength,
overtakes = Overtakes(
overtook = filterOthers(pos, row)((me, other) => me overtook other),
overtakenBy = filterOthers(pos, row)((me, other) => other overtook me),
),
),
WalkEvent.Arrival(
time = endTime,
togetherWith = filterOthers(pos, row)((me, other) =>
other.endTimeOption.exists(endTime isSame _)
),
checkpoint = checkpoint,
isFinish = isFinish,
)
) ++ nextPartOption.fold[Seq[WalkEvent]](
if (isFinish) Seq()
else Seq(WalkEvent.GaveUp.AtCheckpoint(checkpoint))
) { nextPart =>
Seq(
WalkEvent.WaitingOnCheckpoint(
checkpoint = checkpoint,
duration = TimeInterval((nextPart.startTime - endTime) / 60 / 1000),
overtakes = Overtakes(
overtook = filterOthersPauses(pos, row)((me, other) => me overtook other),
overtakenBy = filterOthersPauses(pos, row)((me, other) => other overtook me),
)
)
)
}
case PartTimeInfo.Unfinished(_startTime) => Seq(WalkEvent.GaveUp.DuringWalk(pos + 1))
}
)
}


def walkEvents(row: Participant): Seq[WalkEvent] = {
val prevParts = Seq(None) ++ parts.map(Some(_))
val nextPartInfos: Seq[Option[PartTimeInfo]] = row.partTimes.drop(1).map(Some(_)) ++ Seq(None)

(
prevParts lazyZip
parts lazyZip
row.partTimes lazyZip
nextPartInfos
)
.map(FullPartInfo)
.zipWithIndex
.flatMap((partWalkEvents(row) _).tupled)
}


}
8 changes: 8 additions & 0 deletions client/src/main/scala/com/v6ak/zbdb/Pause.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.v6ak.zbdb

import com.example.moment.Moment
import com.example.RichMoment._

final case class Pause(startTime: Moment, endTime: Moment) {
def overtook(other: Pause): Boolean = this.startTime >= other.startTime && this.endTime < other.endTime
}
2 changes: 1 addition & 1 deletion client/src/main/scala/com/v6ak/zbdb/PlotRenderer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ final class PlotRenderer(participantTable: ParticipantTable) {

private def generatePausesPlotData(rows: Seq[Participant]) = {
val data = rows.map{p =>
PlotLine(row = p, label = p.fullName, points = js.Array((p.pauses, parts).zipped.map((pause, part) => js.Array(part.cumulativeTrackLength.toDouble, pause/1000/60)): _*))
PlotLine(row = p, label = p.fullName, points = js.Array((p.pauseTimes, parts).zipped.map((pause, part) => js.Array(part.cumulativeTrackLength.toDouble, pause/1000/60)): _*))
}
dom.console.log("rows.head.pauses = " + rows.head.pauses.toIndexedSeq.toString)
val series = js.Array(data.map(_.seriesOptions): _*)
Expand Down
4 changes: 2 additions & 2 deletions client/src/main/scala/com/v6ak/zbdb/Renderer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,8 @@ final class Renderer private(participantTable: ParticipantTable, processingError
val details: Frag = Seq(
compList("Startoval(a) zároveň s", select((me, other) => me.startTime isSame other.startTime)),
compList("Dorazil(a) zároveň s", select((me, other) => me.endTime isSame other.endTime)),
compList("Předběhl(a)", select((me, other) => me outran other)),
compList("Byl(a) předběhnuta", select((me, other) => other outran me))
compList("Předběhl(a)", select((me, other) => me overtook other)),
compList("Byl(a) předběhnuta", select((me, other) => other overtook me))
)
val detailsRendered = details.render
while (popup.hasChildNodes()) {
Expand Down

0 comments on commit 9167cd8

Please sign in to comment.