Skip to content

Commit b73afd0

Browse files
committed
feat(hui): spiral board
1 parent d830bc0 commit b73afd0

File tree

1 file changed

+64
-29
lines changed

1 file changed

+64
-29
lines changed

src/main/kotlin/sc/gui/view/HuIBoard.kt

Lines changed: 64 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package sc.gui.view
22

33
import javafx.animation.KeyFrame
44
import javafx.animation.Timeline
5-
import javafx.geometry.HPos
65
import javafx.geometry.Orientation
76
import javafx.geometry.Point2D
87
import javafx.geometry.Pos
@@ -17,25 +16,21 @@ import javafx.scene.layout.*
1716
import javafx.util.Duration
1817
import sc.api.plugins.Team
1918
import sc.gui.AppStyle
19+
import sc.gui.util.listenImmediately
2020
import sc.plugin2025.*
2121
import sc.plugin2025.Field
2222
import sc.plugin2025.util.HuIConstants
2323
import tornadofx.*
24-
25-
private const val BOARDSIZE = 9
24+
import kotlin.math.*
2625

2726
class HuIBoard: GameBoard<GameState>() {
28-
val grid = GridPane().apply {
29-
hgap = AppStyle.formSpacing
30-
vgap = AppStyle.formSpacing
31-
32-
}
27+
val grid = AnchorPane()
3328
val cards = Array(2) { VBox() }
3429

3530
private val graphicSize = squareSize.doubleBinding {
3631
minOf(
37-
root.width.div(BOARDSIZE + 4 /* cards on the sides */),
38-
viewHeight / BOARDSIZE
32+
root.width.div(12 + 4 /* cards on the sides */),
33+
viewHeight / 12
3934
)
4035
}
4136

@@ -59,6 +54,7 @@ class HuIBoard: GameBoard<GameState>() {
5954
private val players = Team.values().map {
6055
createImage("player_" + it.color, 0.8).apply {
6156
effect = playerEffects[it.index]
57+
isMouseTransparent = true
6258
}
6359
}
6460
private val toClear = ArrayList<Node>()
@@ -76,11 +72,13 @@ class HuIBoard: GameBoard<GameState>() {
7672
false
7773
)
7874
if(index != 0)
79-
putOnPosition(
80-
Label(index.toString()).apply { isMouseTransparent = true },
81-
index,
82-
false
83-
)
75+
runLater { // So the labels are in front of the fields
76+
putOnPosition(
77+
Label(index.toString()).apply { isMouseTransparent = true },
78+
index,
79+
false
80+
)
81+
}
8482
}
8583
} else {
8684
toClear.forEach {
@@ -157,7 +155,7 @@ class HuIBoard: GameBoard<GameState>() {
157155
this.children.addAll(
158156
label.move(
159157
Duration.seconds(animFactor),
160-
Point2D(0.0, graphicSize.value * -.6),
158+
Point2D(0.0, graphicSize.value * -1.0),
161159
play = false,
162160
),
163161
label.fade(
@@ -195,16 +193,50 @@ class HuIBoard: GameBoard<GameState>() {
195193

196194
cards[activeTeam.index].apply {
197195
clear()
198-
children.addAll(state.getHare(activeTeam).getCards().map { createImage(it.graphicName(), 1.6) })
196+
children.addAll(state.getHare(activeTeam).getCards().map { createImage(it.graphicName(), 1.5) })
199197
}
200198
}
201199

200+
private var spiralFactor = 0.0
201+
/** Translated from https://stackoverflow.com/questions/78472829/equidistant-points-along-an-archimedean-spiral-with-fixed-gap-between-points-and*/
202+
private fun spiral(radius: Double, numCycles: Double, nPoints: Int): DoubleArray {
203+
val dr = radius / numCycles
204+
val thetaMax = 2 * PI * numCycles
205+
val a = radius / thetaMax
206+
spiralFactor = radius / (2 * PI * numCycles.toInt())
207+
val sMax = (a / 2) * (thetaMax * sqrt(1 + thetaMax.pow(2)) + ln(thetaMax + sqrt(1 + thetaMax.pow(2))))
208+
val s = DoubleArray(nPoints) { dr / 2 + it * (sMax - dr / 2) / nPoints }
209+
210+
val theta = DoubleArray(nPoints)
211+
for(i in s.indices) {
212+
var t = 0.0
213+
var told = t + 1
214+
while(abs(t - told) > 1.0e-10) {
215+
told = t
216+
t = sqrt((-1 + sqrt(1 + 4 * (2 * s[i] / a - ln(t + sqrt(1 + t.pow(2)))).pow(2))) / 2)
217+
}
218+
theta[i] = t
219+
}
220+
return theta
221+
}
222+
223+
val spiralRadius = 5.0
224+
val archimedeanSpiral = spiral(spiralRadius, 3.65, HuIConstants.NUM_FIELDS).reversed()
225+
202226
private fun <T: Node> putOnPosition(node: T, position: Int, clear: Boolean = true): T {
203227
if(clear) {
204228
grid.children.remove(node)
205229
toClear.add(node)
206230
}
207-
grid.add(node, position % BOARDSIZE, position / BOARDSIZE)
231+
grid.add(node)
232+
val pos = archimedeanSpiral[position]
233+
graphicSize.listenImmediately { value ->
234+
val adjustedValue = value.toDouble()
235+
node.anchorpaneConstraints {
236+
leftAnchor = adjustedValue * (spiralFactor * pos * cos(pos) + spiralRadius)
237+
bottomAnchor = adjustedValue * (spiralFactor * pos * sin(pos) + spiralRadius)
238+
}
239+
}
208240
return node
209241
}
210242

@@ -218,6 +250,7 @@ class HuIBoard: GameBoard<GameState>() {
218250
fields[pos].onClickMove(EatSalad)
219251
putOnPosition(
220252
Button("Salat fressen").apply {
253+
translateYProperty().bind(graphicSize.divide(-2))
221254
addClass("small")
222255
onLeftClick { sendHumanMove(EatSalad) }
223256
},
@@ -229,9 +262,8 @@ class HuIBoard: GameBoard<GameState>() {
229262
state.possibleExchangeCarrotMoves().forEach { car ->
230263
putOnPosition(
231264
Button(carrotCostString(car.amount)).apply {
232-
translateYProperty().bind(graphicSize.doubleBinding {
233-
-car.amount * (it?.toDouble()?.div(20) ?: 3.0)
234-
})
265+
translateX = AppStyle.spacing
266+
translateYProperty().bind(graphicSize.multiply(-car.amount / 20.0 - .2))
235267
onLeftClick { sendHumanMove(car) }
236268
},
237269
state.currentPlayer.position
@@ -256,14 +288,19 @@ class HuIBoard: GameBoard<GameState>() {
256288
if(currentPos + maxAdvance < targetPos || state.checkAdvance(distance) != null)
257289
return@forEachIndexed
258290
val flow = FlowPane(Orientation.HORIZONTAL).apply {
259-
this.prefWidthProperty().bind(graphicSize)
260-
this.maxWidthProperty().bind(graphicSize)
261-
this.hgap = AppStyle.miniSpacing
262-
this.vgap = AppStyle.miniSpacing
291+
maxWidthProperty().bind(graphicSize)
292+
hgap = AppStyle.miniSpacing
293+
vgap = AppStyle.miniSpacing
263294
}
264295
var totalCards = 0
265296
state.possibleCardMoves(distance)?.also {
266-
putOnPosition(Group(Group(flow).apply { this.isManaged = false }), targetPos)
297+
putOnPosition(
298+
Group(Group(flow).apply { isManaged = false }).apply {
299+
translateX = AppStyle.spacing
300+
translateY = -AppStyle.formSpacing
301+
},
302+
targetPos
303+
)
267304
totalCards = it.sumOf { it.getCards().size }
268305
}?.forEach { advance ->
269306
val cards = advance.getCards()
@@ -307,9 +344,7 @@ class HuIBoard: GameBoard<GameState>() {
307344

308345
private fun carrotCost(value: Int, position: Int) =
309346
putOnPosition(Label(carrotCostString(value)).apply {
310-
gridpaneConstraints {
311-
this.hAlignment = HPos.CENTER
312-
}
347+
translateY = -.8 * graphicSize.value
313348
isMouseTransparent = true
314349
}, position)
315350

0 commit comments

Comments
 (0)