Compare commits

..

No commits in common. "develop" and "v1.11.1" have entirely different histories.

345 changed files with 4727 additions and 8843 deletions

View File

@ -17,27 +17,10 @@ variables:
stages:
- build
- test
- upload
- deploy
- release
test:
stage: test
before_script:
- sbt -v sbtVersion
script:
- sbt test
# Rest in piece.
#scalafmt:
# stage: test
# allow_failure: true
# before_script:
# - sbt -v sbtVersion
# script:
# - sbt scalafmtCheckAll
build:
stage: build
before_script:

View File

@ -1,35 +0,0 @@
version = 3.9.9
runner.dialect = scala213
preset = default
maxColumn = 120
align = {
preset = none
openParenDefnSite = false
}
newlines = {
source = keep
}
rewrite = {
rules = [SortModifiers, Imports]
sortModifiers.preset = styleGuide
imports.sort = scalastyle
trailingCommas.style = multiple
insertBraces.minLines = 3
}
docstrings = {
style = SpaceAsterisk
blankFirstLine = unfold
oneline = unfold
wrap = keep
forceBlankLineBefore = false
}
indent {
defnSite = 2
extendSite = 2
withSiteRelativeToExtends = 2
}

View File

@ -61,7 +61,7 @@ We'll make sure your setups are grounded in reality
by having the maximum card tier in a slot depend on your computer case.
Oh, did I forget to mention Ocelot Desktop has APUs as well?
Explore the wonders of distributed computing by adding a couple of extra
Explore the wonders of distributed computing by adding a couple of other
computers to your workspace.
(Or a thousand — if you think your host can handle this.)
Network cards allow these newly spawned machines to talk to each other.
@ -82,7 +82,7 @@ drives.
Manage your screen real estate to avoid distractions.
All nodes are draggable, as are windows.
Screen windows in particular are also resizeable —
And screen windows in particular are also resizeable —
click and drag the bottom-right corner if they take up too much space.
Or hold <kbd>Shift</kbd> and let the window consume it all.
@ -125,8 +125,6 @@ A few smaller features are worth mentioning, too:
shows performance graphs: the memory, processor time, and call budget.
- Hold the <kbd>Ctrl</kbd> key while dragging blocks to snap them to the grid.
Full list of hotkeys and supported features is available in the [FAQ][ocelot-desktop-faq].
## Download
Decided to give Ocelot Desktop a shot?
@ -134,7 +132,7 @@ Decided to give Ocelot Desktop a shot?
or [the latest development build][download-dev] ([mirror][download-dev-mirror]).
## Hacking
Just import the project in your favorite IDE.
Just import the project in your favorite IDE.
Make sure to have Scala and SBT installed (your IDE may assist you with that).
We include [ocelot-brain][] as a Git submodule: don't forget to fetch it!
@ -172,7 +170,7 @@ home directory.
## Credits
- **LeshaInc:** the author of Ocelot Desktop and the graphics guru.
- **Totoro:** the creator of [ocelot-brain][] and [ocelot.online][ocelot-online].
- **bpm140:** produced the marvelous Ocelot Desktop [landing page][ocelot-desktop].
- **bpm140:** produced a marvelous Ocelot Desktop landing page.
- **rason:** stirred development at the critical moment!
- **NE0:** the bug extermination specialist.
- **ECS:** introduced the exciting realm of cloud computing.
@ -203,6 +201,5 @@ home directory.
[ocelot-brain]: https://gitlab.com/cc-ru/ocelot/ocelot-brain
[ocelot-online]: https://ocelot.fomalhaut.me/
[ocelot-desktop]: https://ocelot.fomalhaut.me/desktop/
[ocelot-desktop-faq]: https://ocelot.fomalhaut.me/faq.html
[irc]: ircs://irc.esper.net:6697/cc.ru
[discord]: https://discord.com/invite/FM9qWGm

View File

@ -1,5 +1,5 @@
name := "ocelot-desktop"
version := "1.14.2"
version := "1.11.1"
scalaVersion := "2.13.10"
lazy val root = project.in(file("."))
@ -18,12 +18,9 @@ lazy val brain = ProjectRef(file("lib/ocelot-brain"), "ocelot-brain")
libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.19" % "test"
libraryDependencies += "org.scalatest" %% "scalatest-funsuite" % "3.2.19" % "test"
libraryDependencies += "org.apache.logging.log4j" % "log4j-core" % "2.25.1"
libraryDependencies += "org.apache.logging.log4j" % "log4j-api" % "2.25.1"
libraryDependencies += "org.apache.logging.log4j" % "log4j-slf4j-impl" % "2.25.1"
libraryDependencies += "org.apache.logging.log4j" % "log4j-core" % "2.20.0"
libraryDependencies += "org.apache.logging.log4j" % "log4j-api" % "2.20.0"
libraryDependencies += "org.apache.logging.log4j" % "log4j-slf4j-impl" % "2.20.0"
val lwjglVersion = "2.9.3"
@ -32,9 +29,7 @@ libraryDependencies += "org.lwjgl.lwjgl" % "lwjgl-platform" % lwjglVersion class
libraryDependencies += "org.lwjgl.lwjgl" % "lwjgl-platform" % lwjglVersion classifier "natives-windows"
libraryDependencies += "org.lwjgl.lwjgl" % "lwjgl-platform" % lwjglVersion classifier "natives-osx"
Compile / unmanagedResourceDirectories += baseDirectory.value / "lib" / "native"
libraryDependencies += "com.github.stephengold" % "j-ogg-all" % "1.0.6"
libraryDependencies += "com.github.stephengold" % "j-ogg-all" % "1.0.3"
libraryDependencies += "com.github.wendykierp" % "JTransforms" % "3.1"
libraryDependencies += "com.github.sarxos" % "webcam-capture" % "0.3.12"

View File

@ -1,25 +0,0 @@
# How to compile LWJGL2 for Apple Silicon
LWJGL2 does not provide official native ARM support, therefore Ocelot uses a specially modified version by **shadowfacts**
Article: https://shadowfacts.net/2022/lwjgl-arm64/
Repository: https://github.com/shadowfacts/lwjgl2-arm64
This procedure is completely optional, as the precompiled library is already checked into the repository at `lib/native/liblwjgl-arm64.dylib`
## Compilation
This assumes that you are running macOS on **Apple Silicon** *(cross-compiling LWJGL seems to be impossible)*
1. Acquire a JDK8 built for ARM - for example, [Zulu 8 JDK](https://www.azul.com/downloads/?version=java-8-lts&os=macos&architecture=arm-64-bit&package=jdk#zulu), and add it to your `JAVA_HOME`
2. Get [`maven`](https://maven.apache.org/) and [`ant`](https://ant.apache.org/) - you may install them via `brew`, but be careful, as they will try to install a JDK as a dependency
3. Clone the LWJGL repository:
```bash
% git clone https://github.com/shadowfacts/lwjgl-arm64.git
```
4. Run the following commands in the repo:
```bash
% ant generate-all
% ant jars
% ant compile-native
```
5. Copy the compiled library from `libs/macosx/liblwjgl.dylib` into the `ocelot-desktop` project as `lib/native/liblwjgl-arm64.dylib`

View File

@ -9,7 +9,7 @@ Let's assume you want to release a new version, say, `1.33.7`.
3. Update the version of Ocelot Desktop in `build.sbt`; commit the changes.
The commit message should just say "Version 1.33.7".
4. Switch to `master` and merge `develop` into it.
5. Create an annotated tag: `git tag -a v1.33.7`.
5. Create an annotated tag: `git -a v1.33.7`.
On the first line, write "Version 1.33.7", followed by a blank line.
Then describe the changes in Markdown.
Make sure not to use `#` in the text, since git will treat it as a comment.

Binary file not shown.

@ -1 +1 @@
Subproject commit bec1cc6b1e9e588692f753e9c617063c74967fed
Subproject commit 072ad32d0e512829c9ef50d12f56df7664e19b4b

1
project/assembly.sbt Normal file
View File

@ -0,0 +1 @@
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.1.1")

View File

@ -1,2 +1,2 @@
# suppress inspection "UnusedProperty" for whole file
sbt.version = 1.10.7
sbt.version = 1.8.3

1
project/buildinfo.sbt Normal file
View File

@ -0,0 +1 @@
addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.11.0")

View File

@ -1,3 +0,0 @@
addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.13.1")
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.3.1")
addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.5.4")

0
spritepack/spritepack.sh Executable file → Normal file
View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 239 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 169 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 210 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 206 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 170 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 207 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 196 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 161 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 158 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 161 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 644 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 101 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

BIN
sprites/screen/BorderB.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 B

BIN
sprites/screen/BorderT.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 B

BIN
sprites/screen/CornerBL.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 B

BIN
sprites/screen/CornerBR.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 546 B

BIN
sprites/screen/CornerTL.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 B

BIN
sprites/screen/CornerTR.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 171 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 183 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 183 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 201 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 188 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 203 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 190 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 219 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 229 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 210 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 228 B

View File

@ -1,31 +0,0 @@
/*
* Copyright (c) 2002-2007 Lightweight Java Game Library Project
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'Light Weight Java Game Library' nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

View File

@ -1,17 +1,16 @@
Accent = #bd353e
AboutLogo = #ffffffee
AboutForeground = #eeeeee
AboutForegroundSubtle = #eeeeeeee
AboutButtonForeground = #222222
AboutButtonBorder = #444444
AboutButtonBackground = #777777
AboutButtonBackgroundActive = #888888
AboutLogo = #ffffffee
AboutForeground = #eeeeee
AboutForegroundSubtle = #eeeeeeee
AboutButtonForeground = #222222
AboutButtonBorder = #444444
AboutButtonBackground = #777777
PortDown = #8382d8
PortUp = #75bdc1
PortNorth = #c8ca5f
PortSouth = #ed8ef4
PortSouth = #990da3
PortEast = #7ec95f
PortWest = #db7d75
PortAny = #9b9b9b
@ -23,7 +22,6 @@ Tier3 = #c354cd
Label = #333333
LabelError = #aa0000
LabelDisabled = #888888
Scrollbar = #e5e5e526
ScrollbarThumb = #cc3f72
@ -43,14 +41,15 @@ ContextMenuHover = #363636
ContextMenuSeparator = #444444
ContextMenuIcon = #7F7F7F
ComponentSelectorBackground = #222222cc
ComponentSelectorBorder = #444444
ComponentSelectorText = #b0b0b0
ComputerAddress = #333333
ScreenOff = #000000
WindowBackground = #c6c6c6
StatusBar = #181818
StatusBarActive = #ffffff04
StatusBarBorder = #222222
StatusBarForeground = #777777
StatusBarIcon = #bbbbbb
@ -67,17 +66,14 @@ TextInputBorder = #888888
TextInputBorderDisabled = #666666
TextInputBorderFocused = #336666
TextInputBackground = #aaaaaa
TextInputBackgroundActive = #bbbbbb
TextInputBackgroundSelected = #336666
TextInputBackgroundDisabled = #aaaaaa
TextInputForeground = #333333
TextInputForegroundSelected = #aaaaaa
TextInputForegroundDisabled = #888888
TextInputBorderError = #aa8888
TextInputBorderErrorDisabled = #aa8888
TextInputBorderErrorFocused = #cc6666
ButtonBackground = #aaaaaa
ButtonBackgroundActive = #bbbbbb
ButtonBorder = #888888
ButtonForeground = #333333
ButtonForegroundPressed = #444444
@ -88,16 +84,12 @@ ButtonConfirm = #336633
BottomDrawerBorder = #888888
HistogramBarEmpty = #336633
HistogramBarTop = #ccfdcc
HistogramBarFill = #64cc65
HistogramGrid = #336633
HistogramFill = #73ff7360
HistogramFillWarning = #ffda7360
HistogramFillError = #ff737f60
HistogramEdge = #ccfdcc
HistogramEdgeWarning = #fdeacc
HistogramEdgeError = #fdccd0
HistogramBarEmpty = #336633
HistogramBarTop = #ccfdcc
HistogramBarFill = #64cc65
HistogramGrid = #336633
HistogramFill = #73ff7360
HistogramEdge = #ccfdcc
VerticalMenuBackground = #c6c6c6
VerticalMenuEntryIcon = #888888
@ -105,18 +97,16 @@ VerticalMenuEntryActive = #dfdfdf
VerticalMenuEntryForeground = #333333
VerticalMenuBorder = #dfdfdf
SliderBackground = #aaaaaa
SliderBackgroundActive = #bbbbbb
SliderBorder = #888888
SliderTick = #989898
SliderHandler = #bbbbbb
SliderForeground = #333333
SliderBackground = #aaaaaa
SliderBorder = #888888
SliderTick = #989898
SliderHandler = #bbbbbb
SliderForeground = #333333
CheckboxBackground = #aaaaaa
CheckboxBackgroundActive = #bbbbbb
CheckboxBorder = #888888
CheckboxForeground = #333333
CheckboxLabel = #333333
CheckboxBackground = #aaaaaa
CheckboxBorder = #888888
CheckboxForeground = #333333
CheckboxLabel = #333333
NotificationError = #FF204E
NotificationWarning = #ffbf00
@ -187,14 +177,3 @@ LogEntryTxForeground = #cccccc
LogEntryTxBorder = #183a41
OcelotCardTooltip = #c1905f
RackRelayButtonText = #e0e0e0
Flash = #ffffff
RelayTextLow = #009900
RelayTextMid = #999900
RelayTextHigh = #990000
BoomCardGlowStart = #ff4010
BoomCardGlowEnd = #ff1010

Binary file not shown.

Before

Width:  |  Height:  |  Size: 150 KiB

After

Width:  |  Height:  |  Size: 144 KiB

View File

@ -1,347 +1,322 @@
BackgroundPattern 0 0 304 304
BarSegment 308 305 16 4
Empty 134 632 16 16
EmptySlot 246 567 18 18
BarSegment 385 434 16 4
Empty 249 655 16 16
EmptySlot 220 707 18 18
Knob 203 434 50 50
KnobCenter 254 434 50 50
KnobLimits 305 434 50 50
Loading 0 305 48 448
Logo 305 0 168 200
ShadowBorder 279 344 1 24
ShadowCorner 233 674 24 24
TabArrow 356 452 8 14
blocks/Generic 151 632 16 16
blocks/HologramEffect 291 344 4 4
blocks/HologramProjector1Top 168 632 16 16
blocks/HologramProjector2Top 185 632 16 16
blocks/HologramProjectorSide 202 632 16 16
buttons/BottomDrawerClose 265 567 18 18
buttons/BottomDrawerOpen 284 567 18 18
buttons/OpenFMRadioCloseOff 327 316 7 8
buttons/OpenFMRadioCloseOn 335 316 7 8
buttons/OpenFMRadioRedstoneOff 282 316 8 8
buttons/OpenFMRadioRedstoneOn 291 316 8 8
buttons/OpenFMRadioStartOff 258 674 24 24
buttons/OpenFMRadioStartOn 283 674 24 24
buttons/OpenFMRadioStopOff 308 674 24 24
buttons/OpenFMRadioStopOn 333 674 24 24
buttons/OpenFMRadioVolumeDownOff 365 452 10 10
buttons/OpenFMRadioVolumeDownOn 376 452 10 10
buttons/OpenFMRadioVolumeUpOff 387 452 10 10
buttons/OpenFMRadioVolumeUpOn 398 452 10 10
buttons/PowerOff 303 567 18 18
buttons/PowerOn 322 567 18 18
buttons/RackRelayOff 134 655 65 18
buttons/RackRelayOn 200 655 65 18
icons/Antenna 219 632 16 16
icons/ArrowRight 236 632 16 16
icons/AspectRatio 253 632 16 16
icons/Book 270 632 16 16
icons/ButtonCheck 143 600 17 17
icons/ButtonClipboard 161 600 17 17
icons/ButtonRandomize 179 600 17 17
icons/CPU 287 632 16 16
icons/Card 304 632 16 16
icons/Close 218 600 15 14
icons/Code 321 632 16 16
icons/ComponentBus 338 632 16 16
icons/Copy 355 632 16 16
icons/Cross 372 632 16 16
icons/Cut 389 632 16 16
icons/Delete 406 632 16 16
icons/DragLMB 509 567 21 14
icons/DragRMB 531 567 21 14
icons/EEPROM 423 632 16 16
icons/Edit 440 632 16 16
icons/Eject 457 632 16 16
icons/File 474 632 16 16
icons/Floppy 491 632 16 16
icons/Folder 508 632 16 16
icons/FolderSlash 525 632 16 16
icons/Grid 177 567 22 22
icons/GridOff 200 567 22 22
icons/Guitar 542 632 16 16
icons/HDD 559 632 16 16
icons/Help 576 632 16 16
icons/Home 223 567 22 22
icons/Keyboard 593 632 16 16
icons/KeyboardOff 610 632 16 16
icons/LMB 453 434 11 14
icons/Label 627 632 16 16
icons/LinesHorizontal 644 632 16 16
icons/Link 661 632 16 16
icons/LinkSlash 678 632 16 16
icons/Memory 695 632 16 16
icons/Microchip 712 632 16 16
icons/NA 729 632 16 16
icons/NotificationError 477 434 11 11
icons/NotificationInfo 489 434 11 11
icons/NotificationWarning 501 434 11 11
icons/Ocelot 746 632 16 16
icons/Paste 763 632 16 16
icons/Pause 780 632 16 16
icons/Pin 408 434 14 14
icons/Play 797 632 16 16
icons/Plus 814 632 16 16
icons/Power 831 632 16 16
icons/RMB 465 434 11 14
icons/Restart 848 632 16 16
icons/Save 865 632 16 16
icons/SaveAs 882 632 16 16
icons/Server 899 632 16 16
icons/SettingsKeymap 356 434 12 17
icons/SettingsSound 369 434 12 17
icons/SettingsSystem 382 434 12 17
icons/SettingsUI 395 434 12 17
icons/SideAny 513 434 11 11
icons/SideDown 525 434 11 11
icons/SideEast 537 434 11 11
icons/SideNone 549 434 11 11
icons/SideNorth 561 434 11 11
icons/SideSouth 573 434 11 11
icons/SideUndefined 585 434 11 11
icons/SideUp 597 434 11 11
icons/SideWest 609 434 11 11
icons/Tier0 916 632 16 16
icons/Tier1 933 632 16 16
icons/Tier2 950 632 16 16
icons/Tiers 967 632 16 16
icons/Unpin 423 434 14 14
icons/WaveLFSR 288 540 24 10
icons/WaveNoise 313 540 24 10
icons/WaveSawtooth 338 540 24 10
icons/WaveSine 363 540 24 10
icons/WaveSquare 388 540 24 10
icons/WaveTriangle 413 540 24 10
icons/Window 984 632 16 16
icons/WireArrowLeft 281 344 4 8
icons/WireArrowRight 286 344 4 8
ShadowBorder 279 305 1 24
ShadowCorner 216 674 24 24
TabArrow 134 634 8 14
blocks/Generic 266 655 16 16
blocks/HologramEffect 287 314 4 4
blocks/HologramProjector1Top 283 655 16 16
blocks/HologramProjector2Top 300 655 16 16
blocks/HologramProjectorSide 317 655 16 16
buttons/BottomDrawerClose 239 707 18 18
buttons/BottomDrawerOpen 258 707 18 18
buttons/OpenFMRadioCloseOff 404 445 7 8
buttons/OpenFMRadioCloseOn 412 445 7 8
buttons/OpenFMRadioRedstoneOff 359 445 8 8
buttons/OpenFMRadioRedstoneOn 368 445 8 8
buttons/OpenFMRadioStartOff 241 674 24 24
buttons/OpenFMRadioStartOn 266 674 24 24
buttons/OpenFMRadioStopOff 291 674 24 24
buttons/OpenFMRadioStopOn 316 674 24 24
buttons/OpenFMRadioVolumeDownOff 143 634 10 10
buttons/OpenFMRadioVolumeDownOn 154 634 10 10
buttons/OpenFMRadioVolumeUpOff 165 634 10 10
buttons/OpenFMRadioVolumeUpOn 176 634 10 10
buttons/PowerOff 277 707 18 18
buttons/PowerOn 296 707 18 18
buttons/RackRelayOff 117 655 65 18
buttons/RackRelayOn 183 655 65 18
icons/Antenna 334 655 16 16
icons/ArrowRight 351 655 16 16
icons/AspectRatio 368 655 16 16
icons/Book 385 655 16 16
icons/ButtonCheck 341 674 17 17
icons/ButtonClipboard 359 674 17 17
icons/ButtonRandomize 377 674 17 17
icons/CPU 402 655 16 16
icons/Card 419 655 16 16
icons/Close 134 601 15 14
icons/Code 436 655 16 16
icons/ComponentBus 453 655 16 16
icons/Copy 470 655 16 16
icons/Cross 487 655 16 16
icons/Delete 504 655 16 16
icons/DragLMB 483 707 21 14
icons/DragRMB 505 707 21 14
icons/EEPROM 521 655 16 16
icons/Edit 538 655 16 16
icons/Eject 555 655 16 16
icons/File 572 655 16 16
icons/Floppy 589 655 16 16
icons/Folder 606 655 16 16
icons/FolderSlash 623 655 16 16
icons/Grid 151 707 22 22
icons/GridOff 174 707 22 22
icons/Guitar 640 655 16 16
icons/HDD 657 655 16 16
icons/Help 674 655 16 16
icons/Home 197 707 22 22
icons/LMB 218 616 11 14
icons/Label 691 655 16 16
icons/LinesHorizontal 708 655 16 16
icons/Link 725 655 16 16
icons/LinkSlash 742 655 16 16
icons/Memory 759 655 16 16
icons/Microchip 776 655 16 16
icons/NA 793 655 16 16
icons/NotificationError 242 616 11 11
icons/NotificationInfo 254 616 11 11
icons/NotificationWarning 266 616 11 11
icons/Ocelot 810 655 16 16
icons/Pin 173 616 14 14
icons/Plus 827 655 16 16
icons/Power 844 655 16 16
icons/RMB 230 616 11 14
icons/Restart 861 655 16 16
icons/Save 878 655 16 16
icons/SaveAs 895 655 16 16
icons/Server 912 655 16 16
icons/SettingsSound 134 616 12 17
icons/SettingsSystem 147 616 12 17
icons/SettingsUI 160 616 12 17
icons/Tier0 929 655 16 16
icons/Tier1 946 655 16 16
icons/Tier2 963 655 16 16
icons/Tiers 980 655 16 16
icons/Unpin 188 616 14 14
icons/WaveLFSR 799 584 24 10
icons/WaveNoise 824 584 24 10
icons/WaveSawtooth 849 584 24 10
icons/WaveSine 874 584 24 10
icons/WaveSquare 899 584 24 10
icons/WaveTriangle 924 584 24 10
icons/Window 997 655 16 16
icons/WireArrowLeft 281 305 4 8
icons/WireArrowRight 286 305 4 8
items/APU0 49 655 16 96
items/APU1 66 655 16 96
items/APU2 83 655 16 96
items/CPU0 1001 632 16 16
items/CPU1 358 674 16 16
items/CPU2 375 674 16 16
items/CardBase 392 674 16 16
items/CircuitBoard 409 674 16 16
items/ComponentBus0 426 674 16 16
items/ComponentBus1 443 674 16 16
items/ComponentBus2 460 674 16 16
items/ComponentBus3 477 674 16 16
items/CPU0 201 540 16 16
items/CPU1 218 540 16 16
items/CPU2 235 540 16 16
items/CardBase 252 540 16 16
items/CircuitBoard 269 540 16 16
items/ComponentBus0 286 540 16 16
items/ComponentBus1 303 540 16 16
items/ComponentBus2 320 540 16 16
items/ComponentBus3 337 540 16 16
items/DataCard0 49 526 16 128
items/DataCard1 66 526 16 128
items/DataCard2 83 526 16 128
items/DebugCard 494 674 16 16
items/DiskDriveMountable 511 674 16 16
items/EEPROM 528 674 16 16
items/FloppyDisk_dyeBlack 545 674 16 16
items/FloppyDisk_dyeBlue 562 674 16 16
items/FloppyDisk_dyeBrown 579 674 16 16
items/FloppyDisk_dyeCyan 596 674 16 16
items/FloppyDisk_dyeGray 613 674 16 16
items/FloppyDisk_dyeGreen 630 674 16 16
items/FloppyDisk_dyeLightBlue 647 674 16 16
items/FloppyDisk_dyeLightGray 664 674 16 16
items/FloppyDisk_dyeLime 681 674 16 16
items/FloppyDisk_dyeMagenta 698 674 16 16
items/FloppyDisk_dyeOrange 715 674 16 16
items/FloppyDisk_dyePink 732 674 16 16
items/FloppyDisk_dyePurple 749 674 16 16
items/FloppyDisk_dyeRed 766 674 16 16
items/FloppyDisk_dyeWhite 783 674 16 16
items/FloppyDisk_dyeYellow 800 674 16 16
items/GraphicsCard0 817 674 16 16
items/GraphicsCard1 834 674 16 16
items/GraphicsCard2 851 674 16 16
items/HardDiskDrive0 868 674 16 16
items/HardDiskDrive1 885 674 16 16
items/HardDiskDrive2 902 674 16 16
items/InternetCard 143 567 16 32
items/DebugCard 354 540 16 16
items/DiskDriveMountable 371 540 16 16
items/EEPROM 388 540 16 16
items/FloppyDisk_dyeBlack 405 540 16 16
items/FloppyDisk_dyeBlue 422 540 16 16
items/FloppyDisk_dyeBrown 439 540 16 16
items/FloppyDisk_dyeCyan 456 540 16 16
items/FloppyDisk_dyeGray 473 540 16 16
items/FloppyDisk_dyeGreen 490 540 16 16
items/FloppyDisk_dyeLightBlue 507 540 16 16
items/FloppyDisk_dyeLightGray 524 540 16 16
items/FloppyDisk_dyeLime 541 540 16 16
items/FloppyDisk_dyeMagenta 558 540 16 16
items/FloppyDisk_dyeOrange 575 540 16 16
items/FloppyDisk_dyePink 592 540 16 16
items/FloppyDisk_dyePurple 609 540 16 16
items/FloppyDisk_dyeRed 626 540 16 16
items/FloppyDisk_dyeWhite 643 540 16 16
items/FloppyDisk_dyeYellow 660 540 16 16
items/GraphicsCard0 677 540 16 16
items/GraphicsCard1 694 540 16 16
items/GraphicsCard2 711 540 16 16
items/HardDiskDrive0 728 540 16 16
items/HardDiskDrive1 745 540 16 16
items/HardDiskDrive2 762 540 16 16
items/InternetCard 117 707 16 32
items/LinkedCard 100 655 16 96
items/Memory0 919 674 16 16
items/Memory1 936 674 16 16
items/Memory2 953 674 16 16
items/Memory3 970 674 16 16
items/Memory4 987 674 16 16
items/Memory5 1004 674 16 16
items/Memory6 134 707 16 16
items/NetworkCard 151 707 16 16
items/Memory0 779 540 16 16
items/Memory1 796 540 16 16
items/Memory2 813 540 16 16
items/Memory3 830 540 16 16
items/Memory4 847 540 16 16
items/Memory5 864 540 16 16
items/Memory6 881 540 16 16
items/NetworkCard 898 540 16 16
items/OcelotCard 100 526 16 128
items/RedstoneCard0 168 707 16 16
items/RedstoneCard1 185 707 16 16
items/SelfDestructingCard 160 567 16 32
items/Server0 202 707 16 16
items/Server1 219 707 16 16
items/Server2 236 707 16 16
items/Server3 253 707 16 16
items/RedstoneCard0 915 540 16 16
items/RedstoneCard1 932 540 16 16
items/SelfDestructingCard 134 707 16 32
items/Server0 949 540 16 16
items/Server1 966 540 16 16
items/Server2 983 540 16 16
items/Server3 1000 540 16 16
items/SoundCard 117 526 16 128
items/TapeCopper 270 707 16 16
items/TapeDiamond 287 707 16 16
items/TapeGold 304 707 16 16
items/TapeGreg 321 707 16 16
items/TapeIg 338 707 16 16
items/TapeIron 355 707 16 16
items/TapeNetherStar 372 707 16 16
items/TapeSteel 389 707 16 16
items/WirelessNetworkCard0 406 707 16 16
items/WirelessNetworkCard1 423 707 16 16
light-panel/BookmarkLeft 269 540 18 14
light-panel/BookmarkRight 197 600 20 14
light-panel/BorderB 296 344 4 4
light-panel/BorderL 284 353 4 2
light-panel/BorderR 301 344 4 4
light-panel/BorderT 306 344 4 4
light-panel/CornerBL 311 344 4 4
light-panel/CornerBR 316 344 4 4
light-panel/CornerTL 321 344 4 4
light-panel/CornerTR 326 344 4 4
light-panel/Fill 410 344 2 2
light-panel/Vent 279 305 2 38
nodes/Cable 300 316 8 8
nodes/Camera 440 707 16 16
nodes/Chest 438 434 14 14
nodes/HologramProjector0 457 707 16 16
nodes/HologramProjector1 474 707 16 16
nodes/IronNoteBlock 491 707 16 16
nodes/Lamp 508 707 16 16
nodes/LampFrame 525 707 16 16
items/TapeCopper 134 567 16 16
items/TapeDiamond 151 567 16 16
items/TapeGold 168 567 16 16
items/TapeGreg 185 567 16 16
items/TapeIg 202 567 16 16
items/TapeIron 219 567 16 16
items/TapeNetherStar 236 567 16 16
items/TapeSteel 253 567 16 16
items/WirelessNetworkCard0 270 567 16 16
items/WirelessNetworkCard1 287 567 16 16
light-panel/BookmarkLeft 780 584 18 14
light-panel/BookmarkRight 395 674 20 14
light-panel/BorderB 292 314 4 4
light-panel/BorderL 382 314 4 2
light-panel/BorderR 297 314 4 4
light-panel/BorderT 302 314 4 4
light-panel/CornerBL 307 314 4 4
light-panel/CornerBR 312 314 4 4
light-panel/CornerTL 317 314 4 4
light-panel/CornerTR 322 314 4 4
light-panel/Fill 285 325 2 2
light-panel/Vent 356 434 2 38
nodes/Cable 377 445 8 8
nodes/Camera 304 567 16 16
nodes/Chest 203 616 14 14
nodes/HologramProjector0 321 567 16 16
nodes/HologramProjector1 338 567 16 16
nodes/IronNoteBlock 355 567 16 16
nodes/Lamp 372 567 16 16
nodes/LampFrame 389 567 16 16
nodes/LampGlow 49 305 128 128
nodes/NewNode 542 707 16 16
nodes/NoteBlock 559 707 16 16
nodes/OpenFMRadio 576 707 16 16
nodes/Relay 593 707 16 16
nodes/TapeDrive 610 707 16 16
nodes/computer/Default 627 707 16 16
nodes/computer/DiskActivity 644 707 16 16
nodes/computer/Error 661 707 16 16
nodes/computer/On 678 707 16 16
nodes/disk-drive/Default 695 707 16 16
nodes/disk-drive/DiskActivity 712 707 16 16
nodes/disk-drive/Floppy 729 707 16 16
nodes/holidays/Christmas 134 674 32 32
nodes/holidays/Halloween 167 674 32 32
nodes/holidays/Valentines 200 674 32 32
nodes/microcontroller/Default 746 707 16 16
nodes/microcontroller/Error 763 707 16 16
nodes/microcontroller/On 780 707 16 16
nodes/ocelot-block/Default 117 655 16 80
nodes/ocelot-block/Rx 797 707 16 16
nodes/ocelot-block/Tx 814 707 16 16
nodes/rack/Default 831 707 16 16
nodes/rack/Empty 848 707 16 16
nodes/rack/drive/0/Default 865 707 16 16
nodes/rack/drive/0/DiskActivity 882 707 16 16
nodes/rack/drive/0/Floppy 899 707 16 16
nodes/rack/drive/1/Default 916 707 16 16
nodes/rack/drive/1/DiskActivity 933 707 16 16
nodes/rack/drive/1/Floppy 950 707 16 16
nodes/rack/drive/2/Default 967 707 16 16
nodes/rack/drive/2/DiskActivity 984 707 16 16
nodes/rack/drive/2/Floppy 1001 707 16 16
nodes/rack/drive/3/Default 266 655 16 16
nodes/rack/drive/3/DiskActivity 283 655 16 16
nodes/rack/drive/3/Floppy 300 655 16 16
nodes/rack/drive/Floppy 317 655 16 16
nodes/rack/server/0/Default 334 655 16 16
nodes/rack/server/0/DiskActivity 351 655 16 16
nodes/rack/server/0/Error 368 655 16 16
nodes/rack/server/0/NetworkActivity 385 655 16 16
nodes/rack/server/0/On 402 655 16 16
nodes/rack/server/1/Default 419 655 16 16
nodes/rack/server/1/DiskActivity 436 655 16 16
nodes/rack/server/1/Error 453 655 16 16
nodes/rack/server/1/NetworkActivity 470 655 16 16
nodes/rack/server/1/On 487 655 16 16
nodes/rack/server/2/Default 504 655 16 16
nodes/rack/server/2/DiskActivity 521 655 16 16
nodes/rack/server/2/Error 538 655 16 16
nodes/rack/server/2/NetworkActivity 555 655 16 16
nodes/rack/server/2/On 572 655 16 16
nodes/rack/server/3/Default 589 655 16 16
nodes/rack/server/3/DiskActivity 606 655 16 16
nodes/rack/server/3/Error 623 655 16 16
nodes/rack/server/3/NetworkActivity 640 655 16 16
nodes/rack/server/3/On 657 655 16 16
nodes/raid/0/DiskActivity 674 655 16 16
nodes/raid/0/Error 691 655 16 16
nodes/raid/1/DiskActivity 708 655 16 16
nodes/raid/1/Error 725 655 16 16
nodes/raid/2/DiskActivity 742 655 16 16
nodes/raid/2/Error 759 655 16 16
nodes/raid/Default 776 655 16 16
nodes/screen/BottomLeft 793 655 16 16
nodes/screen/BottomMiddle 810 655 16 16
nodes/screen/BottomRight 827 655 16 16
nodes/screen/ColumnBottom 844 655 16 16
nodes/screen/ColumnMiddle 861 655 16 16
nodes/screen/ColumnTop 878 655 16 16
nodes/screen/Middle 895 655 16 16
nodes/screen/MiddleLeft 912 655 16 16
nodes/screen/MiddleRight 929 655 16 16
nodes/screen/PowerOnOverlay 946 655 16 16
nodes/screen/RowLeft 963 655 16 16
nodes/screen/RowMiddle 980 655 16 16
nodes/screen/RowRight 997 655 16 16
nodes/screen/Standalone 201 540 16 16
nodes/screen/TopLeft 218 540 16 16
nodes/screen/TopMiddle 235 540 16 16
nodes/screen/TopRight 252 540 16 16
panel/BorderB 331 344 4 4
panel/BorderL 289 353 4 2
panel/BorderR 336 344 4 4
panel/BorderT 341 344 4 4
panel/CornerBL 346 344 4 4
panel/CornerBR 351 344 4 4
panel/CornerTL 356 344 4 4
panel/CornerTR 361 344 4 4
panel/Fill 413 344 2 2
particles/Note 300 305 7 10
particles/Smoke 134 567 8 64
screen/InnerBorderB 281 360 2 4
screen/InnerBorderT 284 360 2 4
screen/InnerCornerBL 366 344 4 4
screen/InnerCornerBR 371 344 4 4
screen/InnerCornerTL 376 344 4 4
screen/InnerCornerTR 381 344 4 4
screen/OuterBorderT 281 353 2 6
screen/OuterCornerBL 309 316 8 8
screen/OuterCornerBR 318 316 8 8
screen/OuterCornerTL 282 305 8 10
screen/OuterCornerTR 291 305 8 10
window/BorderDark 406 344 1 4
window/BorderLight 408 344 1 4
window/CornerBL 386 344 4 4
window/CornerBR 391 344 4 4
window/CornerTL 396 344 4 4
window/CornerTR 401 344 4 4
nodes/NewNode 406 567 16 16
nodes/NoteBlock 423 567 16 16
nodes/OpenFMRadio 440 567 16 16
nodes/Relay 457 567 16 16
nodes/TapeDrive 474 567 16 16
nodes/computer/Default 491 567 16 16
nodes/computer/DiskActivity 508 567 16 16
nodes/computer/Error 525 567 16 16
nodes/computer/On 542 567 16 16
nodes/disk-drive/Default 559 567 16 16
nodes/disk-drive/DiskActivity 576 567 16 16
nodes/disk-drive/Floppy 593 567 16 16
nodes/holidays/Christmas 117 674 32 32
nodes/holidays/Halloween 150 674 32 32
nodes/holidays/Valentines 183 674 32 32
nodes/microcontroller/Default 610 567 16 16
nodes/microcontroller/Error 627 567 16 16
nodes/microcontroller/On 644 567 16 16
nodes/rack/Default 661 567 16 16
nodes/rack/Empty 678 567 16 16
nodes/rack/drive/0/Default 695 567 16 16
nodes/rack/drive/0/DiskActivity 712 567 16 16
nodes/rack/drive/0/Floppy 729 567 16 16
nodes/rack/drive/1/Default 746 567 16 16
nodes/rack/drive/1/DiskActivity 763 567 16 16
nodes/rack/drive/1/Floppy 780 567 16 16
nodes/rack/drive/2/Default 797 567 16 16
nodes/rack/drive/2/DiskActivity 814 567 16 16
nodes/rack/drive/2/Floppy 831 567 16 16
nodes/rack/drive/3/Default 848 567 16 16
nodes/rack/drive/3/DiskActivity 865 567 16 16
nodes/rack/drive/3/Floppy 882 567 16 16
nodes/rack/drive/Floppy 899 567 16 16
nodes/rack/server/0/Default 916 567 16 16
nodes/rack/server/0/DiskActivity 933 567 16 16
nodes/rack/server/0/Error 950 567 16 16
nodes/rack/server/0/NetworkActivity 967 567 16 16
nodes/rack/server/0/On 984 567 16 16
nodes/rack/server/1/Default 1001 567 16 16
nodes/rack/server/1/DiskActivity 134 584 16 16
nodes/rack/server/1/Error 151 584 16 16
nodes/rack/server/1/NetworkActivity 168 584 16 16
nodes/rack/server/1/On 185 584 16 16
nodes/rack/server/2/Default 202 584 16 16
nodes/rack/server/2/DiskActivity 219 584 16 16
nodes/rack/server/2/Error 236 584 16 16
nodes/rack/server/2/NetworkActivity 253 584 16 16
nodes/rack/server/2/On 270 584 16 16
nodes/rack/server/3/Default 287 584 16 16
nodes/rack/server/3/DiskActivity 304 584 16 16
nodes/rack/server/3/Error 321 584 16 16
nodes/rack/server/3/NetworkActivity 338 584 16 16
nodes/rack/server/3/On 355 584 16 16
nodes/raid/0/DiskActivity 372 584 16 16
nodes/raid/0/Error 389 584 16 16
nodes/raid/1/DiskActivity 406 584 16 16
nodes/raid/1/Error 423 584 16 16
nodes/raid/2/DiskActivity 440 584 16 16
nodes/raid/2/Error 457 584 16 16
nodes/raid/Default 474 584 16 16
nodes/screen/BottomLeft 491 584 16 16
nodes/screen/BottomMiddle 508 584 16 16
nodes/screen/BottomRight 525 584 16 16
nodes/screen/ColumnBottom 542 584 16 16
nodes/screen/ColumnMiddle 559 584 16 16
nodes/screen/ColumnTop 576 584 16 16
nodes/screen/Middle 593 584 16 16
nodes/screen/MiddleLeft 610 584 16 16
nodes/screen/MiddleRight 627 584 16 16
nodes/screen/PowerOnOverlay 644 584 16 16
nodes/screen/RowLeft 661 584 16 16
nodes/screen/RowMiddle 678 584 16 16
nodes/screen/RowRight 695 584 16 16
nodes/screen/Standalone 712 584 16 16
nodes/screen/TopLeft 729 584 16 16
nodes/screen/TopMiddle 746 584 16 16
nodes/screen/TopRight 763 584 16 16
panel/BorderB 327 314 4 4
panel/BorderL 387 314 4 2
panel/BorderR 332 314 4 4
panel/BorderT 337 314 4 4
panel/CornerBL 342 314 4 4
panel/CornerBR 347 314 4 4
panel/CornerTL 352 314 4 4
panel/CornerTR 357 314 4 4
panel/Fill 288 325 2 2
particles/Note 377 434 7 10
screen/BorderB 284 314 2 8
screen/BorderT 281 314 2 10
screen/CornerBL 386 445 8 8
screen/CornerBR 395 445 8 8
screen/CornerTL 359 434 8 10
screen/CornerTR 368 434 8 10
window/BorderDark 281 325 1 4
window/BorderLight 283 325 1 4
window/CornerBL 362 314 4 4
window/CornerBR 367 314 4 4
window/CornerTL 372 314 4 4
window/CornerTR 377 314 4 4
window/OpenFMRadio 474 0 232 105
window/case/Motherboard 123 434 79 70
window/rack/Lines 49 434 73 91
window/rack/Motherboard 178 305 100 78
window/rack/NetworkBack 293 365 1 2
window/rack/NetworkBottom 295 365 1 2
window/rack/NetworkConnector 297 365 1 2
window/rack/NetworkLeft 299 365 1 2
window/rack/NetworkRight 301 365 1 2
window/rack/NetworkTop 303 365 1 2
window/rack/NodeBack 287 360 5 1
window/rack/NodeBottom 293 360 5 1
window/rack/NodeLeft 299 360 5 1
window/rack/NodeRight 305 360 5 1
window/rack/NodeTop 311 360 5 1
window/rack/SideBack 281 365 1 3
window/rack/SideBottom 283 365 1 3
window/rack/SideConnector 285 365 1 3
window/rack/SideLeft 287 365 1 3
window/rack/SideRight 289 365 1 3
window/rack/SideTop 291 365 1 3
window/rack/NetworkBack 299 319 1 2
window/rack/NetworkBottom 301 319 1 2
window/rack/NetworkConnector 303 319 1 2
window/rack/NetworkLeft 305 319 1 2
window/rack/NetworkRight 307 319 1 2
window/rack/NetworkTop 309 319 1 2
window/rack/NodeBack 392 314 5 1
window/rack/NodeBottom 398 314 5 1
window/rack/NodeLeft 404 314 5 1
window/rack/NodeRight 410 314 5 1
window/rack/NodeTop 416 314 5 1
window/rack/SideBack 287 319 1 3
window/rack/SideBottom 289 319 1 3
window/rack/SideConnector 291 319 1 3
window/rack/SideLeft 293 319 1 3
window/rack/SideRight 295 319 1 3
window/rack/SideTop 297 319 1 3
window/raid/Slots 134 540 66 26
window/tape/Back 341 567 20 15
window/tape/BackPressed 362 567 20 15
window/tape/Forward 383 567 20 15
window/tape/ForwardPressed 404 567 20 15
window/tape/Play 425 567 20 15
window/tape/PlayPressed 446 567 20 15
window/tape/Back 315 707 20 15
window/tape/BackPressed 336 707 20 15
window/tape/Forward 357 707 20 15
window/tape/ForwardPressed 378 707 20 15
window/tape/Play 399 707 20 15
window/tape/PlayPressed 420 707 20 15
window/tape/Screen 134 526 146 13
window/tape/Stop 467 567 20 15
window/tape/StopPressed 488 567 20 15
window/tape/Stop 441 707 20 15
window/tape/StopPressed 462 707 20 15

View File

@ -46,31 +46,11 @@ ocelot {
# Otherwise, Ocelot doesn't bother asking and saves the workspace right away if it knows where.
saveOnExit: true
# If enabled, Ocelot will automatically save the current workspace every `autosavePeriod` seconds.
# This will overwrite an existing save if it exists, or create a temporary save that will be loaded at the next Ocelot startup.
autosave: true
# Delay before the next autosave happens (in seconds).
autosavePeriod: 300
# If enabled, Ocelot loads the most recently opened workspace automatically on startup.
openLastWorkspace: true
# If true, screen nodes will render mini-previews of their content in realtime
# Otherwise, content will be shown only in windows
renderScreenDataOnNodes: true
# Delay before a tooltip appears when hovering over items (in seconds)
tooltipDelayItem: 0.001
# Delay before a tooltip appears when hovering over UI elements (in seconds)
tooltipDelayUI: 0.3
# Whether to show small holiday decorations (pumpkins on Halloween for example)
enableFestiveDecorations: true
}
render {
# Whether mipmap scaling is enabled for screen windows (when shrinking).
screenWindowMipmap: true
renderScreenDataOnNodes: false
}
}

View File

@ -1,4 +1,4 @@
#version 150
#version 140
in vec3 inPos;
in vec3 inNormal;

View File

@ -28,7 +28,7 @@ class ColorScheme extends Logging {
val bytes = java.lang.Long.parseLong(value.substring(1), 16)
val color = if (value.length == 9) {
val rgb = bytes >> 8
IntColor(rgb.toInt).toRGBANorm.withAlpha((bytes & 0xff).toFloat / 255f)
IntColor(rgb.toInt).toRGBANorm.withAlpha((bytes & 0xFF).toFloat / 255f)
} else IntColor(bytes.toInt).toRGBANorm
entries.addOne((key, color))
}

View File

@ -1,7 +1,6 @@
package ocelot.desktop
import buildinfo.BuildInfo
import ocelot.desktop.geometry.Size2D
import ocelot.desktop.inventory.Items
import ocelot.desktop.ui.UiHandler
import ocelot.desktop.ui.swing.SplashScreen
@ -30,11 +29,13 @@ import scala.io.Source
import scala.jdk.CollectionConverters._
import scala.util.{Failure, Success, Try, Using}
// LoggingConfiguration configures Log4j appenders & loggers, it should come before Logging in inheritance hierarchy
object OcelotDesktop extends LoggingConfiguration with Logging {
object OcelotDesktop
// This configures Log4j appenders & loggers, it should come before Logging in inheritance hierarchy
extends LoggingConfiguration
with Logging
{
System.setProperty("awt.useSystemAAFontSettings", "on")
System.setProperty("swing.aatext", "true")
System.setProperty("LWJGL_WM_CLASS", "Ocelot Desktop")
// Required to make all subsequent swing components look "native"
try UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName)
@ -48,16 +49,6 @@ object OcelotDesktop extends LoggingConfiguration with Logging {
val tpsCounter = new FPSCalculator
val ticker = new Ticker
private var _emulationPaused = false
def emulationPaused: Boolean = _emulationPaused
def emulationPaused_=(paused: Boolean): Unit = {
_emulationPaused = paused
// avoid sudden jumps of TPS counter after a pause
if (!paused) tpsCounter.skipSecond()
}
private val TickerIntervalHistorySize = 5
val tickerIntervalHistory = new mutable.Queue[Duration](TickerIntervalHistorySize)
@ -77,10 +68,11 @@ object OcelotDesktop extends LoggingConfiguration with Logging {
splashScreen.setStatus("Loading configuration...", 0.10f)
val customConfigPath = args.get(CommandLine.ConfigPath).flatten
val desktopConfigPath: Path = {
val desktopConfigPath: Path =
if (customConfigPath.isDefined) {
Paths.get(customConfigPath.get)
} else {
}
else {
// TODO: migration for old locations of ocelot.conf, can be safely removed later
// TODO: uncomment this line and delete everything below it when you're ready!
// OcelotPaths.desktopConfig
@ -89,24 +81,23 @@ object OcelotDesktop extends LoggingConfiguration with Logging {
try {
if (!Files.exists(newConfigPath)) {
val oldConfigPath = {
val oldConfigPath =
if (SystemUtils.IS_OS_WINDOWS)
Paths.get(OcelotPaths.windowsAppDataDirectoryName, "Ocelot", "ocelot.conf")
else
Paths.get(OcelotPaths.linuxHomeDirectoryName, ".config", "ocelot", "ocelot.conf")
}
if (Files.exists(oldConfigPath))
Files.move(oldConfigPath, newConfigPath)
}
} catch {
}
catch {
case _: Throwable =>
}
// TODO: end of upper todo <3
newConfigPath
}
}
Settings.load(desktopConfigPath)
@ -132,7 +123,8 @@ object OcelotDesktop extends LoggingConfiguration with Logging {
val loadRecentWorkspace = Settings.get.recentWorkspace.isDefined && Settings.get.openLastWorkspace
root = new RootWidget(!loadRecentWorkspace)
root.size = Size2D(Display.getWidth / Settings.get.scaleFactor, Display.getHeight / Settings.get.scaleFactor)
root.width = Display.getWidth
root.height = Display.getHeight
UiHandler.setRoot(root)
splashScreen.setStatus("Loading workspace...", 0.90f)
@ -164,30 +156,22 @@ object OcelotDesktop extends LoggingConfiguration with Logging {
}
}
val updateThread = new Thread(
() =>
try {
val currentThread = Thread.currentThread()
val updateThread = new Thread(() => try {
val currentThread = Thread.currentThread()
while (!currentThread.isInterrupted) {
if (!emulationPaused) {
Profiler.measure("tick") {
withTickLockAcquired {
workspace.update()
updateThreadTasks.run()
tpsCounter.tick()
}
}
}
ticker.waitNext(!emulationPaused)
while (!currentThread.isInterrupted) {
Profiler.measure("tick") {
withTickLockAcquired {
workspace.update()
tpsCounter.tick()
}
} catch {
case _: InterruptedException => // ignore
},
"update-thread",
)
}
ticker.waitNext()
}
} catch {
case _: InterruptedException => // ignore
}, "update-thread")
updateThread.start()
splashScreen.dispose()
@ -197,8 +181,7 @@ object OcelotDesktop extends LoggingConfiguration with Logging {
logger.info("Cleaning up")
updateThread.interrupt()
try updateThread.join()
catch {
try updateThread.join() catch {
case _: InterruptedException =>
}
@ -248,29 +231,7 @@ object OcelotDesktop extends LoggingConfiguration with Logging {
root.workspaceView.load(frontendNBT)
if (frontendNBT.hasKey("players")) {
players.clear()
players.addAll(
frontendNBT.getTagList("players", NBT.TAG_STRING).map((player: NBTTagString) => User(player.getString))
)
}
}
private var lastAutosave: Long = -1
def resetAutosave(): Unit = {
if (Settings.get.autosave) {
lastAutosave = System.currentTimeMillis()
logger.debug("Autosave interval was reset.")
}
}
def updateAutosave(): Unit = {
if (Settings.get.autosave) {
if (lastAutosave == -1 || System.currentTimeMillis() - lastAutosave > Settings.get.autosavePeriod * 1000) {
val path = savePath.getOrElse(tmpPath)
logger.debug(s"Autosave to $path...")
saveTo(path)
lastAutosave = System.currentTimeMillis()
}
players.addAll(frontendNBT.getTagList("players", NBT.TAG_STRING).map((player: NBTTagString) => User(player.getString)))
}
}
@ -280,37 +241,31 @@ object OcelotDesktop extends LoggingConfiguration with Logging {
private def savePath_=(path: Option[Path]): Unit = {
_savePath = path
UiHandler.windowTitleSuffix = path.map(_.toString)
UiHandler.requestWindowTitle(UiHandler.baseWindowTitle + path.map(" - " + _).getOrElse(""))
}
private val tmpPath = Files.createTempDirectory("ocelot-save")
Runtime.getRuntime.addShutdownHook(new Thread(() => {
// if autosave is turned on and the project is not saved properly, we would like to keep the temporary project
if (!Settings.get.autosave || savePath.isEmpty) {
FileUtils.deleteDirectory(tmpPath.toFile)
}
FileUtils.deleteDirectory(tmpPath.toFile)
}))
def newWorkspace(): Unit = showCloseConfirmationDialog("Save workspace before opening a new one?") {
root.workspaceView.newWorkspace()
savePath = None
Settings.get.recentWorkspace = None
resetAutosave()
}
private def saveTo(outputPath: Path): Unit = {
val oldPath = workspace.path
if (oldPath != outputPath) {
val (oldFiles, newFiles) = {
val (oldFiles, newFiles) =
Using.resources(Files.list(oldPath), Files.list(outputPath)) { (oldDirStream, newDirStream) =>
val oldFiles = oldDirStream.iterator.asScala.toArray
val newFiles = newDirStream.iterator.asScala.toArray
(oldFiles, newFiles)
}
}
val toRemove = newFiles.intersect(oldFiles)
@ -328,7 +283,8 @@ object OcelotDesktop extends LoggingConfiguration with Logging {
if (Files.isDirectory(path)) {
FileUtils.copyDirectory(oldFile, newFile)
} else {
}
else {
FileUtils.copyFile(oldFile, newFile)
}
}
@ -371,12 +327,11 @@ object OcelotDesktop extends LoggingConfiguration with Logging {
def saveAs(): Unit = showSaveDialog()
def showOpenDialog(): Unit = {
def showOpenDialog(): Unit =
showFileChooserDialog(JFileChooser.OPEN_DIALOG, JFileChooser.DIRECTORIES_ONLY) {
case Some(dir) => load(dir)
case None => Success(())
}
}
def load(dir: File): Try[Unit] = {
val path = Paths.get(dir.getCanonicalPath, "workspace.nbt")
@ -393,9 +348,6 @@ object OcelotDesktop extends LoggingConfiguration with Logging {
Settings.get.recentWorkspace = Some(dir.getCanonicalPath)
workspace.path = dir.toPath
loadWorld(nbt)
resetAutosave()
logger.info(s"Workspace successfully loaded from: $path")
}
}
} else Failure(new FileNotFoundException("Specified directory does not contain 'workspace.nbt'"))
@ -422,16 +374,13 @@ object OcelotDesktop extends LoggingConfiguration with Logging {
val selectedFile =
Option.when(chooser.showDialog(null, null) == JFileChooser.APPROVE_OPTION)(chooser.getSelectedFile)
// users of [showFileChooserDialog] expect that the continuation is run on the main thread.
// let's meet this expectation.
UiHandler.UiThreadTasks.add(() => {
val result = f(selectedFile)
val result = f(selectedFile)
result match {
case f @ Failure(_) => showFailureMessage(f)
case Success(_) =>
}
result match {
case f @ Failure(_) => showFailureMessage(f)
case Success(_) =>
}
})
}).start()
}
@ -442,13 +391,13 @@ object OcelotDesktop extends LoggingConfiguration with Logging {
new NotificationDialog(
s"Something went wrong!\n($exception)\nCheck the log file for a full stacktrace.",
NotificationType.Error,
NotificationType.Error
).addCloseButton().show()
}
def showAddPlayerDialog(): Unit = new InputDialog(
"Add new player",
text => OcelotDesktop.selectPlayer(text),
text => OcelotDesktop.selectPlayer(text)
).show()
def player: User = if (players.nonEmpty) players.head else User("myself")
@ -475,22 +424,18 @@ object OcelotDesktop extends LoggingConfiguration with Logging {
}
var workspace: Workspace = _
val updateThreadTasks = new TaskQueue()
private def createWorkspace(): Unit = {
workspace = new Workspace(tmpPath)
}
private def prepareSavePath(path: Path)(continuation: => Unit): Unit = {
val nonEmpty = {
try Using.resource(Files.list(path))(_.iterator.asScala.nonEmpty)
catch {
case _: FileNotFoundException | _: NoSuchFileException =>
logger.info(s"Save path $path does not exist: creating a new directory")
Files.createDirectory(path)
val nonEmpty = try Using.resource(Files.list(path))(_.iterator.asScala.nonEmpty) catch {
case _: FileNotFoundException | _: NoSuchFileException =>
logger.info(s"Save path $path does not exist: creating a new directory")
Files.createDirectory(path)
false
}
false
}
if (nonEmpty) {
@ -499,7 +444,7 @@ object OcelotDesktop extends LoggingConfiguration with Logging {
|Files in the save directory will be included in the workspace.
|They may be overwritten, causing loss of data.
|Proceed with saving anyway?""".stripMargin,
NotificationType.Warning,
NotificationType.Warning
) {
addButton("Cancel") {
close()
@ -512,7 +457,7 @@ object OcelotDesktop extends LoggingConfiguration with Logging {
} else continuation
}
private def showSaveDialog(continuation: => Unit): Unit = {
private def showSaveDialog(continuation: => Unit): Unit =
showFileChooserDialog(JFileChooser.SAVE_DIALOG, JFileChooser.DIRECTORIES_ONLY) { dir =>
Try {
if (dir.nonEmpty) {
@ -526,14 +471,11 @@ object OcelotDesktop extends LoggingConfiguration with Logging {
save(continuation)
Settings.get.recentWorkspace = dir.map(_.getCanonicalPath)
resetAutosave()
}
}
}
}
}
}
private def showCloseConfirmationDialog(prompt: Option[String])(continuation: => Unit): Unit = {
if (UiHandler.root.modalDialogPool.children.exists(_.isInstanceOf[CloseConfirmationDialog])) {

View File

@ -5,9 +5,11 @@ import ocelot.desktop.Settings.ExtendedConfig
import ocelot.desktop.util.{Logging, SettingsData}
import org.apache.commons.lang3.SystemUtils
import java.io.InputStream
import java.nio.charset.StandardCharsets
import java.nio.file.{Files, Path}
import java.util
import scala.io.{Codec, Source}
class Settings(val config: Config) extends SettingsData {
// TODO: refactor this mess (having to declare every field 3 times is extremely error-prone)
@ -42,24 +44,12 @@ class Settings(val config: Config) extends SettingsData {
windowSize.y -= 16
}
if (config.hasPath("ocelot.keymap")) {
keymap.load(config.getConfig("ocelot.keymap"))
}
recentWorkspace = config.getOptionalString("ocelot.workspace.recent")
pinNewWindows = config.getBooleanOrElse("ocelot.workspace.pinNewWindows", default = true)
unfocusedWindowTransparency = config.getDoubleOrElse("ocelot.workspace.unfocusedWindowTransparency", 0.5).toFloat
unfocusedWindowHide = config.getBooleanOrElse("ocelot.workspace.unfocusedWindowHide", default = false)
unfocusedWindowTransparency = config.getDoubleOrElse("ocelot.workspace.unfocusedWindowTransparency", 0.5)
saveOnExit = config.getBooleanOrElse("ocelot.workspace.saveOnExit", default = true)
autosave = config.getBooleanOrElse("ocelot.workspace.autosave", default = true)
autosavePeriod = config.getIntOrElse("ocelot.workspace.autosavePeriod", default = 300)
openLastWorkspace = config.getBooleanOrElse("ocelot.workspace.openLastWorkspace", default = true)
renderScreenDataOnNodes = config.getBooleanOrElse("ocelot.workspace.renderScreenDataOnNodes", default = true)
tooltipDelayItem = (config.getDoubleOrElse("ocelot.workspace.tooltipDelayItem", 0.001) max 0.001 min 2.0).toFloat
tooltipDelayUI = (config.getDoubleOrElse("ocelot.workspace.tooltipDelayUI", 0.3) max 0.001 min 2.0).toFloat
enableFestiveDecorations = config.getBooleanOrElse("ocelot.workspace.enableFestiveDecorations", default = true)
screenWindowMipmap = config.getBooleanOrElse("ocelot.render.screenWindowMipmap", default = true)
renderScreenDataOnNodes = config.getBooleanOrElse("ocelot.workspace.renderScreenDataOnNodes", default = false)
}
object Settings extends Logging {
@ -76,31 +66,23 @@ object Settings extends Logging {
if (config.hasPath(path)) config.getBoolean(path)
else default
def getIntOrElse(path: String, default: Int): Int =
if (config.hasPath(path)) config.getInt(path)
else default
def getDoubleOrElse(path: String, default: Double): Double =
if (config.hasPath(path)) config.getDouble(path)
else default
def withValuePreserveOrigin(path: String, value: Any): Config = {
config.withValue(
path,
def withValuePreserveOrigin(path: String, value: Any): Config =
config.withValue(path,
if (config.hasPath(path))
ConfigValueFactory.fromAnyRef(value).withOrigin(config.getValue(path).origin())
else ConfigValueFactory.fromAnyRef(value),
else ConfigValueFactory.fromAnyRef(value)
)
}
def withValuePreserveOrigin(path: String, value: Option[Any]): Config = {
config.withValue(
path,
def withValuePreserveOrigin(path: String, value: Option[Any]): Config =
config.withValue(path,
if (config.hasPath(path))
ConfigValueFactory.fromAnyRef(value.orNull).withOrigin(config.getValue(path).origin())
else ConfigValueFactory.fromAnyRef(value.orNull),
else ConfigValueFactory.fromAnyRef(value.orNull)
)
}
def withValue(path: String, value: Int2D): Config = {
config.withValue(path, ConfigValueFactory.fromIterable(util.Arrays.asList(value.x, value.y)))
@ -108,16 +90,12 @@ object Settings extends Logging {
def withValue(path: String, value: Option[Any]): Config =
config.withValue(path, ConfigValueFactory.fromAnyRef(value.orNull))
def withValue(path: String, value: Int): Config =
config.withValue(path, ConfigValueFactory.fromAnyRef(value))
}
class Int2D(var x: Int, var y: Int) {
var isSet: Boolean = false
def this() = this(0, 0)
def this(list: util.List[Integer]) = {
this()
if (list.size() == 2) {
@ -138,18 +116,41 @@ object Settings extends Logging {
def get: Settings = settings
def load(path: Path): Unit = {
import java.lang.System.{lineSeparator => EOL}
if (Files.exists(path)) {
var stream: InputStream = null
try {
settings = new Settings(ConfigFactory.parseFile(path.toFile))
stream = Files.newInputStream(path)
val source = Source.fromInputStream(stream)(Codec.UTF8)
val plain = source.getLines().mkString("", EOL, EOL)
val config = ConfigFactory.parseString(plain)
settings = new Settings(config)
source.close()
logger.info(s"Loaded Ocelot Desktop configuration from: $path")
return
} catch {
case t: Throwable => logger.error(s"Failed to parse $path!", t)
}
catch {
case _: Throwable =>
logger.info(s"Failed to parse $path, using default Ocelot Desktop configuration.")
}
finally {
if (stream != null)
stream.close()
}
}
logger.info(s"Using default Ocelot Desktop configuration...")
settings = new Settings(ConfigFactory.parseResources(OcelotDesktop.getClass, "/ocelot/desktop/ocelot.conf"))
val defaults = {
val in = getClass.getResourceAsStream("/ocelot/desktop/ocelot.conf")
val config = Source.fromInputStream(in)(Codec.UTF8).getLines().mkString("", EOL, EOL)
in.close()
ConfigFactory.parseString(config)
}
settings = new Settings(defaults)
}
def save(path: Path): Unit = {
@ -171,19 +172,12 @@ object Settings extends Logging {
.withValuePreserveOrigin("ocelot.window.fullscreen", settings.windowFullscreen)
.withValuePreserveOrigin("ocelot.window.disableVsync", settings.disableVsync)
.withValuePreserveOrigin("ocelot.window.debugLwjgl", settings.debugLwjgl)
.withValue("ocelot.keymap", settings.keymap.save())
.withValue("ocelot.workspace.recent", settings.recentWorkspace)
.withValuePreserveOrigin("ocelot.workspace.pinNewWindows", settings.pinNewWindows)
.withValuePreserveOrigin("ocelot.workspace.unfocusedWindowTransparency", settings.unfocusedWindowTransparency)
.withValuePreserveOrigin("ocelot.workspace.unfocusedWindowHide", settings.unfocusedWindowHide)
.withValuePreserveOrigin("ocelot.workspace.saveOnExit", settings.saveOnExit)
.withValuePreserveOrigin("ocelot.workspace.autosave", settings.autosave)
.withValuePreserveOrigin("ocelot.workspace.autosavePeriod", settings.autosavePeriod)
.withValuePreserveOrigin("ocelot.workspace.openLastWorkspace", settings.openLastWorkspace)
.withValuePreserveOrigin("ocelot.workspace.renderScreenDataOnNodes", settings.renderScreenDataOnNodes)
.withValuePreserveOrigin("ocelot.workspace.tooltipDelayItem", settings.tooltipDelayItem)
.withValuePreserveOrigin("ocelot.workspace.tooltipDelayUI", settings.tooltipDelayUI)
.withValuePreserveOrigin("ocelot.workspace.enableFestiveDecorations", settings.enableFestiveDecorations)
if (!Files.exists(path.getParent))
Files.createDirectory(path.getParent)

View File

@ -21,7 +21,7 @@ object AL10W extends Logging {
val exc = OpenAlException(func, errName, err)
if (Settings.get.logAudioErrorStacktrace) {
logger.error(exc.getMessage, exc)
logger.error(exc)
} else {
logger.error(exc.getMessage)
}

View File

@ -15,7 +15,9 @@ object Audio extends Logging {
private val sources = new mutable.HashMap[SoundSource, Int]
private var _disabled = true
/** Should be called _before_ initializing any sound-related resources */
/**
* Should be called _before_ initializing any sound-related resources
*/
def init(): Unit = {
try {
AL.create()
@ -30,10 +32,14 @@ object Audio extends Logging {
def isDisabled: Boolean = _disabled
def numSources: Int = synchronized {
sources.size
}
def newStream(
soundCategory: SoundCategory.Value,
pitch: Float = 1f,
volume: Float = 1f,
volume: Float = 1f
): (SoundStream, SoundSource) = {
var source: SoundSource = null
@ -77,7 +83,7 @@ object Audio extends Logging {
}
}
source = SoundSource.fromStream(stream, soundCategory, looping = false, pitch, volume).build()
source = SoundSource.fromStream(stream, soundCategory, looping = false, pitch, volume)
(stream, source)
}
@ -114,7 +120,7 @@ object Audio extends Logging {
tx.onFailure { AL10W.alDeleteSources(sourceId) }
source.kind match {
case SoundSource.Kind.Buffer(buffer) =>
case SoundSource.KindSoundBuffer(buffer) =>
buffer.bufferId match {
case Some(bufferId) =>
AL10W.alSourcei(sourceId, AL10.AL_BUFFER, bufferId)
@ -124,14 +130,14 @@ object Audio extends Logging {
tx.abort()
}
case SoundSource.Kind.Samples(samples) =>
case SoundSource.KindSoundSamples(samples) =>
Transaction.run { innerTx =>
val bufferId = samples.genBuffer().getOrElse { tx.abort() }
innerTx.onFailure { AL10W.alDeleteBuffers(bufferId) }
AL10W.alSourceQueueBuffers(sourceId, bufferId)
}
case SoundSource.Kind.Stream(_) =>
case SoundSource.KindStream(_) =>
}
AL10W.alSourcef(sourceId, AL10.AL_PITCH, source.pitch)
@ -182,7 +188,7 @@ object Audio extends Logging {
AL10W.alSourcef(
sourceId,
AL10.AL_GAIN,
source.volume * SoundCategory.getSettingsValue(source.soundCategory) * Settings.get.volumeMaster,
source.volume * SoundCategory.getSettingsValue(source.soundCategory) * Settings.get.volumeMaster
)
}
@ -242,12 +248,4 @@ object Audio extends Logging {
AL10W.alDeleteBuffers(buf.get(i))
}
}
def removeAllSources(): Unit = synchronized {
for (sourceId <- sources.values) {
deleteSource(sourceId)
}
sources.clear()
}
}

View File

@ -3,7 +3,7 @@ package ocelot.desktop.audio
import java.nio.ByteBuffer
object BeepGenerator {
def newBeep(pattern: String, frequency: Short, duration: Short): SoundSource.Factory = {
def newBeep(pattern: String, frequency: Short, duration: Short): SoundSource = {
val sampleCounts = pattern.toCharArray
.map(ch => if (ch == '.') duration else 2 * duration)
.map(_ * Audio.sampleRate / 1000)
@ -19,8 +19,8 @@ object BeepGenerator {
val value = (math.signum(math.sin(angle)) * 8192).toShort
offset += step
if (offset > 1) offset -= 1
data.put((value & 0xff).toByte)
data.put(((value >> 8) & 0xff).toByte)
data.put((value & 0xFF).toByte)
data.put(((value >> 8) & 0xFF).toByte)
}
if (data.hasRemaining) {
for (_ <- 0 until pauseSampleCount) {

View File

@ -1,6 +0,0 @@
package ocelot.desktop.audio
trait ClickSoundSourceFactory {
def press: SoundSource.Factory
def release: SoundSource.Factory
}

View File

@ -8,8 +8,7 @@ import java.nio.ByteBuffer
object OggDecoder {
def decode(input: InputStream): SoundSamples = {
val stream =
new VorbisStream(new BasicStream(input).getLogicalStreams.iterator().next().asInstanceOf[LogicalOggStream])
val stream = new VorbisStream(new BasicStream(input).getLogicalStreams.iterator().next().asInstanceOf[LogicalOggStream])
val rate = stream.getIdentificationHeader.getSampleRate
val channels = stream.getIdentificationHeader.getChannels

View File

@ -3,10 +3,12 @@ package ocelot.desktop.audio
import scala.util.control
import scala.util.control.Exception.Catch
case class OpenAlException(func: String, errName: String, code: Int) extends Exception(s"OpenAL error: $func: $errName")
case class OpenAlException(func: String, errName: String, code: Int)
extends Exception(s"OpenAL error: $func: $errName")
object OpenAlException {
def defaulting[T](default: => T): Catch[T] = control.Exception.failAsValue(classOf[OpenAlException])(default)
def ignoring: Catch[Unit] = defaulting(())
}

View File

@ -47,8 +47,8 @@ class SoundBuffer(val file: String) extends Resource with Logging {
val channels = AL10W.alGetBufferi(bufferId, AL10.AL_CHANNELS)
val bits = AL10W.alGetBufferi(bufferId, AL10.AL_BITS)
sizeBytes * 8 / channels / bits
}
sizeBytes * 8 / channels / bits
}
case None => 0
}

View File

@ -6,7 +6,6 @@ import scala.collection.mutable.ArrayBuffer
object SoundBuffers extends Resource {
lazy val MachineComputerRunning: SoundBuffer = load("/ocelot/desktop/sounds/machine/computer_running.ogg")
lazy val MachineFloppyAccess: Array[SoundBuffer] = Array(
load("/ocelot/desktop/sounds/machine/floppy_access1.ogg"),
load("/ocelot/desktop/sounds/machine/floppy_access2.ogg"),
@ -15,10 +14,8 @@ object SoundBuffers extends Resource {
load("/ocelot/desktop/sounds/machine/floppy_access5.ogg"),
load("/ocelot/desktop/sounds/machine/floppy_access6.ogg"),
)
lazy val MachineFloppyEject: SoundBuffer = load("/ocelot/desktop/sounds/machine/floppy_eject.ogg")
lazy val MachineFloppyInsert: SoundBuffer = load("/ocelot/desktop/sounds/machine/floppy_insert.ogg")
lazy val MachineHDDAccess: Array[SoundBuffer] = Array(
load("/ocelot/desktop/sounds/machine/hdd_access1.ogg"),
load("/ocelot/desktop/sounds/machine/hdd_access2.ogg"),
@ -28,27 +25,20 @@ object SoundBuffers extends Resource {
load("/ocelot/desktop/sounds/machine/hdd_access6.ogg"),
)
lazy val MachineTapeButtonPress: SoundBuffer = load("/ocelot/desktop/sounds/machine/tape_button_press.ogg")
lazy val MachineTapeButtonRelease: SoundBuffer = load("/ocelot/desktop/sounds/machine/tape_button_release.ogg")
lazy val MachineTapeButton: SoundBuffer = load("/ocelot/desktop/sounds/machine/tape_button.ogg")
lazy val MachineTapeEject: SoundBuffer = load("/ocelot/desktop/sounds/machine/tape_eject.ogg")
lazy val MachineTapeInsert: SoundBuffer = load("/ocelot/desktop/sounds/machine/tape_insert.ogg")
lazy val MachineTapeRewind: SoundBuffer = load("/ocelot/desktop/sounds/machine/tape_rewind.ogg")
lazy val InterfaceClickPress: SoundBuffer = load("/ocelot/desktop/sounds/interface/click_press.ogg")
lazy val InterfaceClickRelease: SoundBuffer = load("/ocelot/desktop/sounds/interface/click_release.ogg")
lazy val InterfaceTickPress: SoundBuffer = load("/ocelot/desktop/sounds/interface/tick_press.ogg")
lazy val InterfaceTickRelease: SoundBuffer = load("/ocelot/desktop/sounds/interface/tick_release.ogg")
lazy val InterfaceShutter: SoundBuffer = load("/ocelot/desktop/sounds/interface/shutter.ogg")
lazy val InterfaceClick: SoundBuffer = load("/ocelot/desktop/sounds/interface/click.ogg")
lazy val InterfaceTick: SoundBuffer = load("/ocelot/desktop/sounds/interface/tick.ogg")
lazy val MinecraftClickPress: SoundBuffer = load("/ocelot/desktop/sounds/minecraft/click_press.ogg")
lazy val MinecraftClickRelease: SoundBuffer = load("/ocelot/desktop/sounds/minecraft/click_release.ogg")
lazy val MinecraftClick: SoundBuffer = load("/ocelot/desktop/sounds/minecraft/click.ogg")
lazy val MinecraftExplosion: SoundBuffer = load("/ocelot/desktop/sounds/minecraft/explosion.ogg")
lazy val SelfDestructingCardCountdownBeep: SoundBuffer = load("/ocelot/desktop/sounds/minecraft/countdown_beep.ogg")
lazy val NoteBlock: Map[String, SoundBuffer] = List(
"banjo", "basedrum", "bass", "bell", "bit", "chime", "cow_bell", "didgeridoo", "flute", "guitar",
"harp", "hat", "iron_xylophone", "pling", "snare", "xylophone",
"harp", "hat", "iron_xylophone", "pling", "snare", "xylophone"
).map(name => {
(name, load(s"/ocelot/desktop/sounds/minecraft/note_block/$name.ogg"))
}).toMap

View File

@ -11,13 +11,13 @@ class SoundSource(
val looping: Boolean,
val pitch: Float,
var volume: Float,
var position: Vector3D = Vector3D(0, 0, 0),
var position: Vector3D = Vector3D(0, 0, 0)
) {
def duration: Option[Duration] = kind match {
case SoundSource.Kind.Buffer(buffer) =>
case SoundSource.KindSoundBuffer(buffer) =>
Some(Duration(buffer.numSamples.toFloat / buffer.sampleRate, TimeUnit.SECONDS))
case SoundSource.Kind.Samples(SoundSamples(buffer, rate, format)) =>
case SoundSource.KindSoundSamples(SoundSamples(buffer, rate, format)) =>
val bps = format match {
case SoundSamples.Format.Stereo16 => 2
case SoundSamples.Format.Mono8 => 1
@ -26,7 +26,7 @@ class SoundSource(
Some(Duration(buffer.limit().toFloat / (rate * bps), TimeUnit.SECONDS))
case SoundSource.Kind.Stream(_) =>
case SoundSource.KindStream(_) =>
None
}
@ -34,15 +34,15 @@ class SoundSource(
Audio.getSourceStatus(this)
}
def playing: Boolean = {
def isPlaying: Boolean = {
status == SoundSource.Status.Playing
}
def paused: Boolean = {
def isPaused: Boolean = {
status == SoundSource.Status.Paused
}
def stopped: Boolean = {
def isStopped: Boolean = {
status == SoundSource.Status.Stopped
}
@ -62,52 +62,26 @@ class SoundSource(
object SoundSource {
sealed trait Kind
object Kind {
case class Buffer(buffer: SoundBuffer) extends Kind
case class Samples(samples: SoundSamples) extends Kind
case class Stream(stream: SoundStream) extends Kind
case class KindSoundBuffer(buffer: SoundBuffer) extends Kind
case class KindSoundSamples(samples: SoundSamples) extends Kind
case class KindStream(stream: SoundStream) extends Kind
def fromBuffer(buffer: SoundBuffer, soundCategory: SoundCategory.Value,
looping: Boolean = false, pitch: Float = 1f, volume: Float = 1f): SoundSource = {
new SoundSource(SoundSource.KindSoundBuffer(buffer), soundCategory, looping, pitch, volume)
}
class Factory(
val kind: Kind,
val soundCategory: SoundCategory.Value,
val looping: Boolean,
val pitch: Float,
val volume: Float,
) {
def build(): SoundSource = new SoundSource(kind, soundCategory, looping, pitch, volume)
def play(): SoundSource = {
val source = build()
source.play()
source
}
def fromSamples(samples: SoundSamples, soundCategory: SoundCategory.Value,
looping: Boolean = false, pitch: Float = 1f, volume: Float = 1f): SoundSource = {
new SoundSource(SoundSource.KindSoundSamples(samples), soundCategory, looping, pitch, volume)
}
def fromBuffer(
buffer: SoundBuffer,
soundCategory: SoundCategory.Value,
looping: Boolean = false,
pitch: Float = 1f,
volume: Float = 1f,
): Factory = new Factory(SoundSource.Kind.Buffer(buffer), soundCategory, looping, pitch, volume)
def fromSamples(
samples: SoundSamples,
soundCategory: SoundCategory.Value,
looping: Boolean = false,
pitch: Float = 1f,
volume: Float = 1f,
): Factory = new Factory(SoundSource.Kind.Samples(samples), soundCategory, looping, pitch, volume)
def fromStream(
stream: SoundStream,
soundCategory: SoundCategory.Value,
looping: Boolean = false,
pitch: Float = 1f,
volume: Float = 1f,
): Factory = new Factory(SoundSource.Kind.Stream(stream), soundCategory, looping, pitch, volume)
def fromStream(stream: SoundStream, soundCategory: SoundCategory.Value,
looping: Boolean = false, pitch: Float = 1f, volume: Float = 1f): SoundSource = {
new SoundSource(SoundSource.KindStream(stream), soundCategory, looping, pitch, volume)
}
object Status extends Enumeration {
val Playing, Paused, Stopped = Value
@ -115,40 +89,13 @@ object SoundSource {
// ----------------------------------------------------------------
object InterfaceClick extends ClickSoundSourceFactory {
override lazy val press: Factory = SoundSource.fromBuffer(SoundBuffers.InterfaceClickPress, SoundCategory.Interface)
lazy val InterfaceClick: SoundSource = SoundSource.fromBuffer(SoundBuffers.InterfaceClick, SoundCategory.Interface)
lazy val InterfaceClickLow: SoundSource = SoundSource.fromBuffer(SoundBuffers.InterfaceClick, SoundCategory.Interface, pitch = 0.8f)
lazy val InterfaceTick: SoundSource = SoundSource.fromBuffer(SoundBuffers.InterfaceTick, SoundCategory.Interface)
override lazy val release: Factory =
SoundSource.fromBuffer(SoundBuffers.InterfaceClickRelease, SoundCategory.Interface)
}
lazy val MinecraftClick: SoundSource = SoundSource.fromBuffer(SoundBuffers.MinecraftClick, SoundCategory.Interface)
lazy val MinecraftExplosion: SoundSource = SoundSource.fromBuffer(SoundBuffers.MinecraftExplosion, SoundCategory.Environment)
object InterfaceClickLow extends ClickSoundSourceFactory {
override lazy val press: Factory =
SoundSource.fromBuffer(SoundBuffers.InterfaceClickPress, SoundCategory.Interface, pitch = 0.8f)
override lazy val release: Factory =
SoundSource.fromBuffer(SoundBuffers.InterfaceClickRelease, SoundCategory.Interface, pitch = 0.8f)
}
object InterfaceTick extends ClickSoundSourceFactory {
override lazy val press: Factory = SoundSource.fromBuffer(SoundBuffers.InterfaceTickPress, SoundCategory.Interface)
override lazy val release: Factory =
SoundSource.fromBuffer(SoundBuffers.InterfaceTickRelease, SoundCategory.Interface)
}
lazy val InterfaceShutter: Factory = SoundSource.fromBuffer(SoundBuffers.InterfaceShutter, SoundCategory.Interface)
object MinecraftClick extends ClickSoundSourceFactory {
override lazy val press: Factory = SoundSource.fromBuffer(SoundBuffers.MinecraftClickPress, SoundCategory.Interface)
override lazy val release: Factory =
SoundSource.fromBuffer(SoundBuffers.MinecraftClickRelease, SoundCategory.Interface)
}
lazy val MachineFloppyInsert: Factory =
SoundSource.fromBuffer(SoundBuffers.MachineFloppyInsert, SoundCategory.Environment)
lazy val MachineFloppyEject: Factory =
SoundSource.fromBuffer(SoundBuffers.MachineFloppyEject, SoundCategory.Environment)
}
lazy val MachineFloppyInsert: SoundSource = SoundSource.fromBuffer(SoundBuffers.MachineFloppyInsert, SoundCategory.Environment)
lazy val MachineFloppyEject: SoundSource = SoundSource.fromBuffer(SoundBuffers.MachineFloppyEject, SoundCategory.Environment)
}

View File

@ -6,8 +6,8 @@ case class IntColor(color: Int) extends Color {
override def toRGBA: RGBAColor = {
RGBAColor(
(color >> 16).toShort,
((color >> 8) & 0xff).toShort,
(color & 0xff).toShort,
((color >> 8) & 0xFF).toShort,
(color & 0xFF).toShort,
)
}

View File

@ -14,7 +14,7 @@ case class RGBAColor(r: Short, g: Short, b: Short, a: Short = 255) extends Color
r.toFloat / 255f,
g.toFloat / 255f,
b.toFloat / 255f,
a.toFloat / 255f,
a.toFloat / 255f
)
override def toHSVA: HSVAColor = toRGBANorm.toHSVA

View File

@ -1,25 +1,22 @@
package ocelot.desktop.color
import ocelot.desktop.util.NumberUtils.ExtendedFloat
import ocelot.desktop.geometry.Vector3D
import java.nio.ByteBuffer
case class RGBAColorNorm(r: Float, g: Float, b: Float, a: Float = 1f) extends Color {
require(0 <= r && r <= 1.0f, "Invalid RED channel")
require(0 <= g && g <= 1.0f, "Invalid GREEN channel")
require(0 <= b && b <= 1.0f, "Invalid BLUE channel")
require(0 <= a && a <= 1.0f, "Invalid ALPHA channel")
assert(0 <= r && r <= 1.0f, "Invalid RED channel")
assert(0 <= g && g <= 1.0f, "Invalid GREEN channel")
assert(0 <= b && b <= 1.0f, "Invalid BLUE channel")
assert(0 <= a && a <= 1.0f, "Invalid ALPHA channel")
def components: Array[Float] = Array(r, g, b, a)
def rgbVector: Vector3D = Vector3D(r, g, b)
def mapRgb(f: Float => Float): RGBAColorNorm = copy(r = f(r), g = f(g), b = f(b))
def mapA(f: Float => Float): RGBAColorNorm = copy(a = f(a))
private final def componentToLinear(x: Float): Float = {
final private def componentToLinear(x: Float): Float = {
if (x <= 0.0404482362771082)
x / 12.92f
else
@ -61,15 +58,6 @@ case class RGBAColorNorm(r: Float, g: Float, b: Float, a: Float = 1f) extends Co
HSVAColor(hue, saturation, value, a)
}
def lerp(dst: RGBAColorNorm, t: Float): RGBAColorNorm = {
RGBAColorNorm(
r.lerp(dst.r, t),
g.lerp(dst.g, t),
b.lerp(dst.b, t),
a.lerp(dst.a, t),
)
}
def withAlpha(alpha: Float): RGBAColorNorm = RGBAColorNorm(r, g, b, alpha)
// ʕʔ

View File

@ -43,7 +43,7 @@ class Camera extends Entity with GenericCamera with DeviceInfo {
DeviceAttribute.Class -> DeviceClass.Multimedia,
DeviceAttribute.Description -> "Dungeon Scanner 2.5D",
DeviceAttribute.Vendor -> Constants.DeviceInfo.DefaultVendor,
DeviceAttribute.Product -> webcamCapture.map(_.name).getOrElse("Blind Pirate"),
DeviceAttribute.Product -> webcamCapture.map(_.name).getOrElse("Blind Pirate")
)
override def load(nbt: NBTTagCompound, workspace: Workspace): Unit = {

View File

@ -1,22 +0,0 @@
package ocelot.desktop.entity
import ocelot.desktop.entity.traits.OcelotInterface
import totoro.ocelot.brain.Constants
import totoro.ocelot.brain.entity.traits.DeviceInfo
import totoro.ocelot.brain.entity.traits.DeviceInfo.{DeviceAttribute, DeviceClass}
import totoro.ocelot.brain.network.{Network, Node, Visibility}
class OcelotBlock extends OcelotInterface with DeviceInfo {
override val node: Node = Network.newNode(this, Visibility.Network)
.withComponent("ocelot", Visibility.Network)
.create()
private final lazy val deviceInfo = Map(
DeviceAttribute.Class -> DeviceClass.Generic,
DeviceAttribute.Description -> "Cardboard box simulator",
DeviceAttribute.Vendor -> Constants.DeviceInfo.DefaultVendor,
DeviceAttribute.Product -> "Catbus",
)
override def getDeviceInfo: Map[String, String] = deviceInfo
}

View File

@ -1,14 +1,18 @@
package ocelot.desktop.entity
import ocelot.desktop.entity.traits.OcelotInterface
import ocelot.desktop.entity.OcelotCard.{LogDirection, LogEvent}
import totoro.ocelot.brain.Constants
import totoro.ocelot.brain.entity.machine.{Arguments, Callback, Context}
import totoro.ocelot.brain.entity.traits.DeviceInfo.{DeviceAttribute, DeviceClass}
import totoro.ocelot.brain.entity.traits.{DeviceInfo, Tiered}
import totoro.ocelot.brain.entity.traits.{DeviceInfo, Entity, Environment, Tiered, result}
import totoro.ocelot.brain.event.{EventBus, NodeEvent}
import totoro.ocelot.brain.network.{Network, Node, Visibility}
import totoro.ocelot.brain.util.Tier
import totoro.ocelot.brain.util.Tier.Tier
class OcelotCard extends OcelotInterface with DeviceInfo with Tiered {
import java.time.Instant
class OcelotCard extends Entity with Environment with DeviceInfo with Tiered {
override val node: Node = Network.newNode(this, Visibility.Neighbors)
.withComponent("ocelot", Visibility.Neighbors)
.create()
@ -23,4 +27,47 @@ class OcelotCard extends OcelotInterface with DeviceInfo with Tiered {
)
override def getDeviceInfo: Map[String, String] = deviceInfo
@Callback(direct = true, doc = """function(message: string) -- Logs a message to the Ocelot console.""")
def log(context: Context, args: Arguments): Array[AnyRef] = {
val message = args.checkString(0)
EventBus.send(LogEvent(node.address, message, LogDirection.CardToUser))
result()
}
@Callback(direct = true, doc = """function(): integer -- Returns the current Unix timestamp (UTC, in milliseconds).""")
def getTimestamp(context: Context, args: Arguments): Array[AnyRef] = {
result(Instant.now().toEpochMilli)
}
@Callback(direct = true, doc = """function(): integer -- Returns a high-resolution timer value (in nanoseconds).""")
def getInstant(context: Context, args: Arguments): Array[AnyRef] = {
result(System.nanoTime())
}
@Callback(direct = true, doc = """function(): number -- Returns the remaining call budget.""")
def getRemainingCallBudget(context: Context, args: Arguments): Array[AnyRef] = {
result(context.getRemainingCallBudget)
}
@Callback(direct = true, doc = """function(): number -- Returns the maximum call budget.""")
def getMaxCallBudget(context: Context, args: Arguments): Array[AnyRef] = {
result(context.getMaxCallBudget)
}
def pushMessage(message: String): Unit = {
node.sendToReachable("computer.signal", "ocelot_message", message)
EventBus.send(LogEvent(node.address, message, LogDirection.UserToCard))
}
}
object OcelotCard {
case class LogEvent(address: String, message: String, direction: LogDirection) extends NodeEvent
sealed trait LogDirection
object LogDirection {
case object CardToUser extends LogDirection
case object UserToCard extends LogDirection
}
}

View File

@ -19,12 +19,11 @@ import javax.sound.sampled.AudioFormat.Encoding
import javax.sound.sampled.{AudioFormat, AudioSystem}
class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
override val node: Component = {
override val node: Component =
Network
.newNode(this, Visibility.Network)
.withComponent("openfm_radio", Visibility.Network)
.create()
}
.newNode(this, Visibility.Network)
.withComponent("openfm_radio", Visibility.Network)
.create()
// --------------------------- URL ---------------------------
@ -45,18 +44,14 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
private def playSynchronously(): Unit = {
try {
// Trying to parse URL and sending request to host
val connection = {
val connection =
new URI(url.get)
.toURL
.openConnection
.asInstanceOf[HttpURLConnection]
}
.toURL
.openConnection
.asInstanceOf[HttpURLConnection]
connection.setRequestMethod("GET")
connection.setRequestProperty(
"User-Agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36",
)
connection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36")
connection.setRequestProperty("Content-Language", "en-US")
connection.setDoInput(true)
connection.setDoOutput(true)
@ -70,22 +65,21 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
2,
4,
44100,
false,
false
),
// Obtaining audio input stream from HTTP connection
AudioSystem.getAudioInputStream(new BufferedInputStream(connection.getInputStream)),
AudioSystem.getAudioInputStream(new BufferedInputStream(connection.getInputStream))
)
// Keeping input stream format parameters here to offload the reading loop
val inputStreamFormat = inputStream.getFormat
val inputStreamSampleRate = inputStreamFormat.getSampleRate.toInt
val inputStreamSoundSampleFormat = {
val inputStreamSoundSampleFormat =
if (inputStreamFormat.getChannels > 1)
Format.Stereo16
else
Format.Mono16
}
// Creating Ocelot output sound stream
val (outputStream, outputSource) = Audio.newStream(SoundCategory.Records, volume = volume)
@ -105,7 +99,7 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
.flip
.asInstanceOf[ByteBuffer],
inputStreamSampleRate,
inputStreamSoundSampleFormat,
inputStreamSoundSampleFormat
))
}
@ -113,7 +107,8 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
}
logger.info("OpenFM input audio stream has reached EOF, closing thread")
} catch {
}
catch {
case _: InterruptedException =>
case e: Exception => logger.error("OpenFM playback exception", e)
}
@ -152,7 +147,7 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
}
def isPlaying: Boolean =
playbackSoundSource.isDefined && playbackSoundSource.get.playing || playbackThread.isDefined
playbackSoundSource.isDefined && playbackSoundSource.get.isPlaying || playbackThread.isDefined
@Callback()
def start(context: Context, args: Arguments): Array[AnyRef] =
@ -173,7 +168,6 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
private var _volume: Float = 1
def volume: Float = _volume
def volume_=(value: Float): Unit = {
_volume = value
@ -217,7 +211,7 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
// --------------------------- Screen color/text ---------------------------
private val defaultScreenText = "OpenFM"
private val defaultScreenColor = IntColor(0x0aff0a)
private val defaultScreenColor = IntColor(0x0AFF0A)
var screenColor: IntColor = defaultScreenColor
private var _screenText: String = defaultScreenText
@ -278,26 +272,23 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
if (nbt.hasKey("url"))
url = Option(nbt.getString("url"))
screenColor = {
screenColor =
if (nbt.hasKey("screenColor"))
IntColor(nbt.getInteger("screenColor"))
else
defaultScreenColor
}
screenText = {
screenText =
if (nbt.hasKey("screenText"))
nbt.getString("screenText")
else
defaultScreenText
}
volume = {
volume =
if (nbt.hasKey("volume"))
nbt.getDouble("volume").toFloat
else
1
}
isListenRedstone = nbt.hasKey("isListenRedstone") && nbt.getBoolean("isListenRedstone")

View File

@ -1,59 +0,0 @@
package ocelot.desktop.entity.traits
import ocelot.desktop.entity.traits.OcelotInterface.LogEvent
import totoro.ocelot.brain.entity.machine.{Arguments, Callback, Context}
import totoro.ocelot.brain.entity.traits.{result, Entity, Environment}
import totoro.ocelot.brain.event.{EventBus, NodeEvent}
import java.time.Instant
trait OcelotInterface extends Entity with Environment {
@Callback(direct = true, doc = """function(message: string) -- Logs a message to the card's console.""")
def log(context: Context, args: Arguments): Array[AnyRef] = {
val message = args.checkString(0)
EventBus.send(LogEvent.CardToUser(node.address, message))
result()
}
@Callback(direct = true, doc = """function() -- Clears messages logged to the card's console.""")
def clearLog(context: Context, args: Arguments): Array[AnyRef] = {
EventBus.send(LogEvent.Clear(node.address))
result()
}
@Callback(direct = true,
doc = """function(): integer -- Returns the current Unix timestamp (UTC, in milliseconds).""")
def getTimestamp(context: Context, args: Arguments): Array[AnyRef] = {
result(Instant.now().toEpochMilli)
}
@Callback(direct = true, doc = """function(): integer -- Returns a high-resolution timer value (in nanoseconds).""")
def getInstant(context: Context, args: Arguments): Array[AnyRef] = {
result(System.nanoTime())
}
@Callback(direct = true, doc = """function(): number -- Returns the remaining call budget.""")
def getRemainingCallBudget(context: Context, args: Arguments): Array[AnyRef] = {
result(context.getRemainingCallBudget)
}
@Callback(direct = true, doc = """function(): number -- Returns the maximum call budget.""")
def getMaxCallBudget(context: Context, args: Arguments): Array[AnyRef] = {
result(context.getMaxCallBudget)
}
def pushMessage(message: String): Unit = {
node.sendToReachable("computer.signal", "ocelot_message", message)
EventBus.send(LogEvent.UserToCard(node.address, message))
}
}
object OcelotInterface {
sealed trait LogEvent extends NodeEvent
object LogEvent {
case class CardToUser(address: String, message: String) extends LogEvent
case class UserToCard(address: String, message: String) extends LogEvent
case class Clear(address: String) extends LogEvent
}
}

View File

@ -27,25 +27,25 @@ case class Basis3D(x: Vector3D, y: Vector3D, z: Vector3D) {
def *(rhs: Vector3D): Vector3D = Vector3D(
x.x * rhs.x + y.x * rhs.y + z.x * rhs.z,
x.y * rhs.x + y.y * rhs.y + z.y * rhs.z,
x.z * rhs.x + y.z * rhs.y + z.z * rhs.z,
x.z * rhs.x + y.z * rhs.y + z.z * rhs.z
)
def *(rhs: Basis3D): Basis3D = Basis3D(
Vector3D(
x.x * rhs.x.x + y.x * rhs.x.y + z.x * rhs.x.z,
x.y * rhs.x.x + y.y * rhs.x.y + z.y * rhs.x.z,
x.z * rhs.x.x + y.z * rhs.x.y + z.z * rhs.x.z,
x.z * rhs.x.x + y.z * rhs.x.y + z.z * rhs.x.z
),
Vector3D(
x.x * rhs.y.x + y.x * rhs.y.y + z.x * rhs.y.z,
x.y * rhs.y.x + y.y * rhs.y.y + z.y * rhs.y.z,
x.z * rhs.y.x + y.z * rhs.y.y + z.z * rhs.y.z,
x.z * rhs.y.x + y.z * rhs.y.y + z.z * rhs.y.z
),
Vector3D(
x.x * rhs.z.x + y.x * rhs.z.y + z.x * rhs.z.z,
x.y * rhs.z.x + y.y * rhs.z.y + z.y * rhs.z.z,
x.z * rhs.z.x + y.z * rhs.z.y + z.z * rhs.z.z,
),
x.z * rhs.z.x + y.z * rhs.z.y + z.z * rhs.z.z
)
)
def det: Float = x.x * (y.y * z.z - z.y * y.z) -
@ -59,18 +59,18 @@ case class Basis3D(x: Vector3D, y: Vector3D, z: Vector3D) {
Vector3D(
(y.y * z.z - z.y * y.z) * s,
(x.z * z.y - x.y * z.z) * s,
(x.y * y.z - x.z * y.y) * s,
(x.y * y.z - x.z * y.y) * s
),
Vector3D(
(y.z * z.x - y.x * z.z) * s,
(x.x * z.z - x.z * z.x) * s,
(y.x * x.z - x.x * y.z) * s,
(y.x * x.z - x.x * y.z) * s
),
Vector3D(
(y.x * z.y - z.x * y.y) * s,
(z.x * x.y - x.x * z.y) * s,
(x.x * y.y - y.x * x.y) * s,
),
(x.x * y.y - y.x * x.y) * s
)
)
}

View File

@ -0,0 +1,9 @@
package ocelot.desktop.geometry
object FloatUtils {
implicit class ExtendedFloat(val v: Float) extends AnyVal {
def lerp(that: Float, alpha: Float): Float = v * (1 - alpha) + that * alpha
def clamp(min: Float = 0f, max: Float = 1f): Float = v.min(max).max(min)
}
}

View File

@ -3,12 +3,11 @@ package ocelot.desktop.geometry
object ProjectionMatrix3D {
def perspective(aspect: Float, fovY: Float, zNear: Float, zFar: Float): ProjectionMatrix3D = {
val f = (1.0 / math.tan(math.toRadians(fovY) / 2.0)).toFloat
// format: off
ProjectionMatrix3D(
f / aspect, 0, 0, 0,
0, f, 0, 0,
0, 0, (zFar + zNear) / (zNear - zFar), (2 * zFar * zNear) / (zNear - zFar),
0, 0, -1, 0,
0, 0, -1, 0
)
}
}
@ -16,6 +15,7 @@ object ProjectionMatrix3D {
case class ProjectionMatrix3D(m11: Float, m12: Float, m13: Float, m14: Float,
m21: Float, m22: Float, m23: Float, m24: Float,
m31: Float, m32: Float, m33: Float, m34: Float,
m41: Float, m42: Float, m43: Float, m44: Float) {
m41: Float, m42: Float, m43: Float, m44: Float)
{
def array: Array[Float] = Array(m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44)
}

View File

@ -55,7 +55,7 @@ case class Quaternion(x: Float, y: Float, z: Float, w: Float) {
y * rhs.z - z * rhs.y + x * rhs.w + w * rhs.x,
z * rhs.x - x * rhs.z + y * rhs.w + w * rhs.y,
x * rhs.y - y * rhs.x + z * rhs.w + w * rhs.z,
w * rhs.w - x * rhs.x - y * rhs.y - z * rhs.z,
w * rhs.w - x * rhs.x - y * rhs.y - z * rhs.z
)
def conj: Quaternion = Quaternion(-x, -y, -z, w)
@ -72,7 +72,7 @@ case class Quaternion(x: Float, y: Float, z: Float, w: Float) {
def basis: Basis3D = Basis3D(
Vector3D(1 - 2 * y * y - 2 * z * z, 2 * x * y + 2 * z * w, 2 * x * z - 2 * y * w),
Vector3D(2 * x * y - 2 * z * w, 1 - 2 * x * x - 2 * z * z, 2 * y * z + 2 * x * w),
Vector3D(2 * x * z + 2 * y * w, 2 * y * z - 2 * x * w, 1 - 2 * x * x - 2 * y * y),
Vector3D(2 * x * z + 2 * y * w, 2 * y * z - 2 * x * w, 1 - 2 * x * x - 2 * y * y)
)
def dot(that: Quaternion): Float = x * that.x + y * that.y + z * that.z + w * that.w

View File

@ -76,9 +76,7 @@ case class Rect2D(x: Float, y: Float, w: Float, h: Float) {
this
}
def inflated(dx: Float, dy: Float): Rect2D = Rect2D(x - dx, y - dy, w + dx * 2, h + dy * 2)
def inflated(delta: Float): Rect2D = inflated(delta, delta)
def inflate(addition: Float): Rect2D = Rect2D(x - addition, y - addition, w + addition * 2, h + addition * 2)
def center: Vector2D = {
min + (size * 0.5f).toVector
@ -88,8 +86,7 @@ case class Rect2D(x: Float, y: Float, w: Float, h: Float) {
Vector2D(x + w / 2f, y),
Vector2D(x + w, y + h / 2f),
Vector2D(x + w / 2f, y + h),
Vector2D(x, y + h / 2f),
)
Vector2D(x, y + h / 2f))
def distanceTo(that: Rect2D): Float = {
((center - that.center).abs - (extent + that.extent)).max(Vector2D(0, 0)).length

View File

@ -32,7 +32,7 @@ case class Size2D(width: Float, height: Float) {
def clamped(min: Size2D, max: Size2D): Size2D = {
Size2D(
math.min(max.width, math.max(min.width, width)),
math.min(max.height, math.max(min.height, height)),
math.min(max.height, math.max(min.height, height))
)
}
@ -51,4 +51,4 @@ case class Size2D(width: Float, height: Float) {
def /(mul: Float): Size2D = Size2D(width / mul, height / mul)
override def toString: String = f"Size2D [ $width%8.2f x $height%8.2f ]"
}
}

View File

@ -5,28 +5,27 @@ import java.nio.ByteBuffer
object Transform2D {
def identity: Transform2D = Transform2D(
1, 0, 0,
0, 1, 0,
0, 1, 0
)
def scale(x: Float, y: Float): Transform2D = Transform2D(
x, 0, 0,
0, y, 0,
0, y, 0
)
def scale(a: Float): Transform2D = Transform2D.scale(a, a)
def translate(x: Float, y: Float): Transform2D = Transform2D(
1, 0, x,
0, 1, y,
0, 1, y
)
def viewport(width: Float, height: Float): Transform2D =
Transform2D.translate(-1f, 1f) >> Transform2D.scale(2f / width, -2f / height)
def rotate(angle: Float): Transform2D = {
val (s, c) = (math.sin(angle).toFloat, math.cos(angle).toFloat)
val (s, c) = (math.sin(angle).asInstanceOf[Float], math.cos(angle).asInstanceOf[Float])
// format: off
Transform2D(
c, -s, 0,
s, c, 0
@ -37,13 +36,13 @@ object Transform2D {
case class Transform2D(m11: Float, m12: Float, m13: Float, m21: Float, m22: Float, m23: Float) {
def array: Array[Float] = Array(m11, m12, m13, m21, m22, m23)
// format: off
// :|
def >>(that: Transform2D): Transform2D = Transform2D(
m11 * that.m11 + m12 * that.m21, m11 * that.m12 + m12 * that.m22, m11 * that.m13 + m12 * that.m23 + m13,
m21 * that.m11 + m22 * that.m21, m21 * that.m12 + m22 * that.m22, m21 * that.m13 + m22 * that.m23 + m23,
)
// format: off
// (__)
def <<(that: Transform2D): Transform2D = Transform2D(
m11 * that.m11 + m21 * that.m12, m12 * that.m11 + m22 * that.m12, m13 * that.m11 + m23 * that.m12 + that.m13,
m11 * that.m21 + m21 * that.m22, m12 * that.m21 + m22 * that.m22, m13 * that.m21 + m23 * that.m22 + that.m23
@ -51,14 +50,13 @@ case class Transform2D(m11: Float, m12: Float, m13: Float, m21: Float, m22: Floa
def *(that: Vector2D): Vector2D = Vector2D(
m11 * that.x + m12 * that.y + m13,
m21 * that.x + m22 * that.y + m23,
m21 * that.x + m22 * that.y + m23
)
override def toString: String = {
override def toString: String =
f"""Transform2D [$m11%6.3f $m12%6.3f $m13%6.3f]
| [$m21%6.3f $m22%6.3f $m23%6.3f]
""".stripMargin
}
// (°°) ┻━┻
def put(buffer: ByteBuffer): Unit = {

View File

@ -26,7 +26,6 @@ object Transform3D {
}
case class Transform3D(basis: Basis3D, origin: Vector3D) {
// format: off
def array: Array[Float] = Array(
basis.x.x, basis.y.x, basis.z.x, origin.x,
basis.x.y, basis.y.y, basis.z.y, origin.y,
@ -45,8 +44,8 @@ case class Transform3D(basis: Basis3D, origin: Vector3D) {
override def toString: String = s"Transform3D [${basis.x}, ${basis.y}, ${basis.z}, $origin]"
def put(buffer: ByteBuffer): Unit = {
buffer.putFloat(basis.x.x); buffer.putFloat(basis.y.x); buffer.putFloat(basis.z.x); buffer.putFloat(origin.x)
buffer.putFloat(basis.x.y); buffer.putFloat(basis.y.y); buffer.putFloat(basis.z.y); buffer.putFloat(origin.y)
buffer.putFloat(basis.x.z); buffer.putFloat(basis.y.z); buffer.putFloat(basis.z.z); buffer.putFloat(origin.z)
buffer.putFloat(basis.x.x); buffer.putFloat(basis.y.x); buffer.putFloat(basis.z.x); buffer.putFloat(origin.x);
buffer.putFloat(basis.x.y); buffer.putFloat(basis.y.y); buffer.putFloat(basis.z.y); buffer.putFloat(origin.y);
buffer.putFloat(basis.x.z); buffer.putFloat(basis.y.z); buffer.putFloat(basis.z.z); buffer.putFloat(origin.z);
}
}

View File

@ -36,6 +36,9 @@ case class Vector2D(x: Float, y: Float) extends Persistable {
def *(scalar: Float): Vector2D = Vector2D(x * scalar, y * scalar)
// TODO: remove
def *(scalar: Double): Vector2D = Vector2D(x * scalar, y * scalar)
def /(scalar: Float): Vector2D = Vector2D(x / scalar, y / scalar)
def snap(v: Float): Vector2D = Vector2D((x / v).floor * v, (y / v).floor * v)

Some files were not shown because too many files have changed in this diff Show More