Compare commits
278 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf78c278af | ||
|
|
3b82541da6 | ||
|
|
2aba4a7969 | ||
|
|
876043bf2f | ||
|
|
22ee84d74c | ||
|
|
cbc80baf71 | ||
|
|
479d13cc0c | ||
|
|
51c4494628 | ||
|
|
cbd3927cc5 | ||
|
|
8f291c4a80 | ||
|
|
7f0fccae80 | ||
|
|
c0eec1fffc | ||
|
|
4cee456454 | ||
|
|
dfbcb0418c | ||
|
|
5e172ee804 | ||
|
|
052f61ad43 | ||
|
|
d676a4a5bf | ||
|
|
2c77f8cba9 | ||
|
|
6a252d86fd | ||
|
|
667a74519a | ||
|
|
b03078ff27 | ||
|
|
04bbf626a1 | ||
|
|
9a3ee8e95c | ||
|
|
cf088ce9f7 | ||
|
|
6db5ff3f37 | ||
|
|
68350f8d62 | ||
|
|
ca8ef6eee1 | ||
|
|
c9f8f4a123 | ||
|
|
b17efa3aa2 | ||
|
|
7316baa390 | ||
|
|
b6e68d40bd | ||
|
|
b67ad92273 | ||
|
|
313e5c8b6a | ||
|
|
6f5f84e5fd | ||
|
|
4e9d7c96e2 | ||
|
|
a8dc52f1b2 | ||
|
|
c796b74ea3 | ||
|
|
9511f586a6 | ||
|
|
90a01493db | ||
|
|
349280d802 | ||
|
|
605023118f | ||
|
|
3bee61832f | ||
|
|
841733f6fe | ||
|
|
81840e4fab | ||
|
|
8724c4a3ad | ||
|
|
99af664d84 | ||
|
|
448089ccb9 | ||
|
|
8fc3759d88 | ||
|
|
d533e33f2b | ||
|
|
e4aca89f92 | ||
|
|
8416a46b5c | ||
|
|
3f9ad3c710 | ||
|
|
d9e65fc67e | ||
|
|
eb8665970d | ||
|
|
9db2416d23 | ||
|
|
3ada4c6845 | ||
|
|
25cabf1d0a | ||
|
|
fd31074d0d | ||
|
|
97e05f2895 | ||
|
|
110e0b85f1 | ||
|
|
b53311aa27 | ||
|
|
33a3cf4773 | ||
|
|
e74a3d5264 | ||
|
|
745aac646f | ||
|
|
f2234677a0 | ||
|
|
604d7f156d | ||
|
|
c73a0ab9d1 | ||
|
|
d769b0bde3 | ||
|
|
4edfd8fff2 | ||
|
|
d28572124f | ||
|
|
cbc16a3d23 | ||
|
|
ce2eeaec0a | ||
|
|
210f4b1a0c | ||
|
|
f6c41f0184 | ||
|
|
5bcc005143 | ||
|
|
498df9ed09 | ||
|
|
1c74adc329 | ||
|
|
87b13e66b1 | ||
|
|
1c19df7cf3 | ||
|
|
9ced3b99b4 | ||
|
|
5dda7c2b0d | ||
|
|
c789722d4c | ||
|
|
22d854ed34 | ||
|
|
34537bd76c | ||
|
|
c1020ff1cb | ||
|
|
79e3d8ca73 | ||
|
|
ed6ff78956 | ||
|
|
9ce89c2b58 | ||
|
|
b759846505 | ||
|
|
055671abc1 | ||
|
|
47ea0e7d9a | ||
|
|
6b1da4162c | ||
|
|
094ac8cb48 | ||
|
|
fd6400f39a | ||
|
|
a042c76b0f | ||
|
|
eb6f6b0d8d | ||
|
|
d0682ea063 | ||
|
|
9192d7fc48 | ||
|
|
09277887a6 | ||
|
|
4bcdebedfa | ||
|
|
d1d2deddff | ||
|
|
c06b048393 | ||
|
|
d05aa8d89d | ||
|
|
7c68939d54 | ||
|
|
901d81aa8b | ||
|
|
a5a9b18771 | ||
|
|
7ecb9a6fe7 | ||
|
|
f6e81d6752 | ||
|
|
09f688cbb3 | ||
|
|
89da2e9f39 | ||
|
|
b697d3c0c4 | ||
|
|
a9393ab2be | ||
|
|
4209a05c62 | ||
|
|
549833f3e9 | ||
|
|
82d98ff4f8 | ||
|
|
d64314a9db | ||
|
|
c3fcf7ed52 | ||
|
|
e3ccfeba9f | ||
|
|
69dcca71dc | ||
|
|
7466b7d4db | ||
|
|
6554a1ef84 | ||
|
|
c510cbb834 | ||
|
|
b248c4beeb | ||
|
|
88f6870aa1 | ||
|
|
e0b8f61b6e | ||
|
|
e23db58c4c | ||
|
|
c0dad8daed | ||
|
|
ee8ff464ad | ||
|
|
24678b803b | ||
|
|
8d3e71c8ad | ||
|
|
fe86cb22de | ||
|
|
401e347227 | ||
|
|
7cdc316d24 | ||
|
|
0dac2b2f1f | ||
|
|
3315764951 | ||
|
|
189d26c4a1 | ||
|
|
223bbd5048 | ||
|
|
87f8aaaed5 | ||
|
|
bdcbdc3fd8 | ||
|
|
424c5fc202 | ||
|
|
0d0046fd05 | ||
|
|
a1cd8cab00 | ||
|
|
00be88f571 | ||
|
|
18fc7b46ac | ||
|
|
47f92b06c1 | ||
|
|
a0754f4cd8 | ||
|
|
af3e887a1d | ||
|
|
04550de173 | ||
|
|
182d42a843 | ||
|
|
6babdcf6d8 | ||
|
|
3c604b976a | ||
|
|
98d352a9e6 | ||
|
|
73a63e75aa | ||
|
|
c9434089ea | ||
|
|
da17dc81a8 | ||
|
|
35f060451b | ||
|
|
34158d2408 | ||
|
|
eafcb3c16d | ||
|
|
816d4ea992 | ||
|
|
0f333a6f26 | ||
|
|
9a90c64291 | ||
|
|
6f4bfc0a6e | ||
|
|
deb0365d87 | ||
|
|
bbfb840430 | ||
|
|
cb902180c6 | ||
|
|
6f1f1fffc7 | ||
|
|
7b89069c40 | ||
|
|
b67cd2a7f2 | ||
|
|
1a69b55f5e | ||
|
|
34bcb0bd4c | ||
|
|
5fa0cdcb0b | ||
|
|
3f4f32d71c | ||
|
|
8d80e41a2f | ||
|
|
23eadac441 | ||
|
|
21f9d25371 | ||
|
|
46ca2709af | ||
|
|
dbbe289af8 | ||
|
|
e29555e592 | ||
|
|
f62acc1cb8 | ||
|
|
6f9558bed1 | ||
|
|
e37a910d69 | ||
|
|
709acb268a | ||
|
|
70bdbcd630 | ||
|
|
ee4f2dcc3b | ||
|
|
edd9916905 | ||
|
|
234442f5ea | ||
|
|
9d323138d3 | ||
|
|
7a6d62efbe | ||
|
|
cd86902dd5 | ||
|
|
63030f505f | ||
|
|
d2845d9320 | ||
|
|
79284270bc | ||
|
|
68643be2b8 | ||
|
|
bdf0037146 | ||
|
|
477b3586f0 | ||
|
|
3374053a5a | ||
|
|
766f0e5ae6 | ||
|
|
11e3254925 | ||
|
|
47a858b788 | ||
|
|
1a64e949dd | ||
|
|
8efa8e84c1 | ||
|
|
e6ac48862e | ||
|
|
6df90c0fca | ||
|
|
34337b2cc3 | ||
|
|
5b6e378000 | ||
|
|
aa635a6d1c | ||
|
|
a81dbb3ba2 | ||
|
|
b1a1cef365 | ||
|
|
a65caad242 | ||
|
|
08ef6f9075 | ||
|
|
82b35d9dbc | ||
|
|
33b08dcef8 | ||
|
|
a69f04bcf3 | ||
|
|
a898e04dc3 | ||
|
|
2772b3ad67 | ||
|
|
9c5bc3189a | ||
|
|
584bb1be9c | ||
|
|
300c5dfe5b | ||
|
|
78fc31abbc | ||
|
|
5518f1941b | ||
|
|
05c4abbd66 | ||
|
|
0e4b6364bf | ||
|
|
73d41071ee | ||
|
|
7a255ec631 | ||
|
|
d12fdee3d7 | ||
|
|
e68fd4f65e | ||
|
|
7fdfdf5f3b | ||
|
|
aea28d3548 | ||
|
|
c48404d654 | ||
|
|
b1a5fe680a | ||
|
|
51771e4c99 | ||
|
|
150d324777 | ||
|
|
50a4e1c7e4 | ||
|
|
a611b02bfa | ||
|
|
ad92266709 | ||
|
|
5722f671aa | ||
|
|
3526586eff | ||
|
|
78c022dd14 | ||
|
|
6c1d41671a | ||
|
|
8ee5b0d5ef | ||
|
|
2d7912c16f | ||
|
|
9dca251543 | ||
|
|
8938242fff | ||
|
|
ac5bfc693d | ||
|
|
d55fe35e20 | ||
|
|
3d6ceaf219 | ||
|
|
c15193a78d | ||
|
|
8c371f3f63 | ||
|
|
5e7777bd4c | ||
|
|
acb1374140 | ||
|
|
e994011cd9 | ||
|
|
a609ae8cb4 | ||
|
|
8ed3350fca | ||
|
|
a483aff078 | ||
|
|
27f7db8b46 | ||
|
|
811d8e0a09 | ||
|
|
9741f70e42 | ||
|
|
e7a2a1c6ee | ||
|
|
2efec95103 | ||
|
|
6bc51ba1a9 | ||
|
|
22f9216890 | ||
|
|
fed823eefc | ||
|
|
d72c666a9e | ||
|
|
d272befce5 | ||
|
|
9072778c1c | ||
|
|
c5b2cf5136 | ||
|
|
afd468763c | ||
|
|
bfd9197340 | ||
|
|
e9d5a5a71a | ||
|
|
5d8a73371b | ||
|
|
344eacd40f | ||
|
|
35e64752fd | ||
|
|
117aa0d2d2 | ||
|
|
05819d4eba | ||
|
|
4616056add | ||
|
|
d7a6e75ab4 | ||
|
|
a0bdcfd17a | ||
|
|
ff628c7829 |
@ -17,10 +17,27 @@ variables:
|
|||||||
|
|
||||||
stages:
|
stages:
|
||||||
- build
|
- build
|
||||||
|
- test
|
||||||
- upload
|
- upload
|
||||||
- deploy
|
- deploy
|
||||||
- release
|
- 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:
|
build:
|
||||||
stage: build
|
stage: build
|
||||||
before_script:
|
before_script:
|
||||||
|
|||||||
35
.scalafmt.conf
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
@ -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.
|
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?
|
Oh, did I forget to mention Ocelot Desktop has APUs as well?
|
||||||
|
|
||||||
Explore the wonders of distributed computing by adding a couple of other
|
Explore the wonders of distributed computing by adding a couple of extra
|
||||||
computers to your workspace.
|
computers to your workspace.
|
||||||
(Or a thousand — if you think your host can handle this.)
|
(Or a thousand — if you think your host can handle this.)
|
||||||
Network cards allow these newly spawned machines to talk to each other.
|
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.
|
Manage your screen real estate to avoid distractions.
|
||||||
All nodes are draggable, as are windows.
|
All nodes are draggable, as are windows.
|
||||||
And screen windows in particular are also resizeable —
|
Screen windows in particular are also resizeable —
|
||||||
click and drag the bottom-right corner if they take up too much space.
|
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.
|
Or hold <kbd>Shift</kbd> and let the window consume it all.
|
||||||
|
|
||||||
@ -125,6 +125,8 @@ A few smaller features are worth mentioning, too:
|
|||||||
shows performance graphs: the memory, processor time, and call budget.
|
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.
|
- 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
|
## Download
|
||||||
Decided to give Ocelot Desktop a shot?
|
Decided to give Ocelot Desktop a shot?
|
||||||
|
|
||||||
@ -170,7 +172,7 @@ home directory.
|
|||||||
## Credits
|
## Credits
|
||||||
- **LeshaInc:** the author of Ocelot Desktop and the graphics guru.
|
- **LeshaInc:** the author of Ocelot Desktop and the graphics guru.
|
||||||
- **Totoro:** the creator of [ocelot-brain][] and [ocelot.online][ocelot-online].
|
- **Totoro:** the creator of [ocelot-brain][] and [ocelot.online][ocelot-online].
|
||||||
- **bpm140:** produced a marvelous Ocelot Desktop landing page.
|
- **bpm140:** produced the marvelous Ocelot Desktop [landing page][ocelot-desktop].
|
||||||
- **rason:** stirred development at the critical moment!
|
- **rason:** stirred development at the critical moment!
|
||||||
- **NE0:** the bug extermination specialist.
|
- **NE0:** the bug extermination specialist.
|
||||||
- **ECS:** introduced the exciting realm of cloud computing.
|
- **ECS:** introduced the exciting realm of cloud computing.
|
||||||
@ -201,5 +203,6 @@ home directory.
|
|||||||
[ocelot-brain]: https://gitlab.com/cc-ru/ocelot/ocelot-brain
|
[ocelot-brain]: https://gitlab.com/cc-ru/ocelot/ocelot-brain
|
||||||
[ocelot-online]: https://ocelot.fomalhaut.me/
|
[ocelot-online]: https://ocelot.fomalhaut.me/
|
||||||
[ocelot-desktop]: https://ocelot.fomalhaut.me/desktop/
|
[ocelot-desktop]: https://ocelot.fomalhaut.me/desktop/
|
||||||
|
[ocelot-desktop-faq]: https://ocelot.fomalhaut.me/faq.html
|
||||||
[irc]: ircs://irc.esper.net:6697/cc.ru
|
[irc]: ircs://irc.esper.net:6697/cc.ru
|
||||||
[discord]: https://discord.com/invite/FM9qWGm
|
[discord]: https://discord.com/invite/FM9qWGm
|
||||||
|
|||||||
15
build.sbt
@ -1,5 +1,5 @@
|
|||||||
name := "ocelot-desktop"
|
name := "ocelot-desktop"
|
||||||
version := "1.11.1"
|
version := "1.14.1"
|
||||||
scalaVersion := "2.13.10"
|
scalaVersion := "2.13.10"
|
||||||
|
|
||||||
lazy val root = project.in(file("."))
|
lazy val root = project.in(file("."))
|
||||||
@ -18,9 +18,12 @@ lazy val brain = ProjectRef(file("lib/ocelot-brain"), "ocelot-brain")
|
|||||||
|
|
||||||
libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value
|
libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value
|
||||||
|
|
||||||
libraryDependencies += "org.apache.logging.log4j" % "log4j-core" % "2.20.0"
|
libraryDependencies += "org.scalatest" %% "scalatest" % "3.2.19" % "test"
|
||||||
libraryDependencies += "org.apache.logging.log4j" % "log4j-api" % "2.20.0"
|
libraryDependencies += "org.scalatest" %% "scalatest-funsuite" % "3.2.19" % "test"
|
||||||
libraryDependencies += "org.apache.logging.log4j" % "log4j-slf4j-impl" % "2.20.0"
|
|
||||||
|
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"
|
||||||
|
|
||||||
val lwjglVersion = "2.9.3"
|
val lwjglVersion = "2.9.3"
|
||||||
|
|
||||||
@ -29,7 +32,9 @@ 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-windows"
|
||||||
libraryDependencies += "org.lwjgl.lwjgl" % "lwjgl-platform" % lwjglVersion classifier "natives-osx"
|
libraryDependencies += "org.lwjgl.lwjgl" % "lwjgl-platform" % lwjglVersion classifier "natives-osx"
|
||||||
|
|
||||||
libraryDependencies += "com.github.stephengold" % "j-ogg-all" % "1.0.3"
|
Compile / unmanagedResourceDirectories += baseDirectory.value / "lib" / "native"
|
||||||
|
|
||||||
|
libraryDependencies += "com.github.stephengold" % "j-ogg-all" % "1.0.6"
|
||||||
libraryDependencies += "com.github.wendykierp" % "JTransforms" % "3.1"
|
libraryDependencies += "com.github.wendykierp" % "JTransforms" % "3.1"
|
||||||
libraryDependencies += "com.github.sarxos" % "webcam-capture" % "0.3.12"
|
libraryDependencies += "com.github.sarxos" % "webcam-capture" % "0.3.12"
|
||||||
|
|
||||||
|
|||||||
25
doc/lwjgl-apple-silicon-compilation.md
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# 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`
|
||||||
@ -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.
|
3. Update the version of Ocelot Desktop in `build.sbt`; commit the changes.
|
||||||
The commit message should just say "Version 1.33.7".
|
The commit message should just say "Version 1.33.7".
|
||||||
4. Switch to `master` and merge `develop` into it.
|
4. Switch to `master` and merge `develop` into it.
|
||||||
5. Create an annotated tag: `git -a v1.33.7`.
|
5. Create an annotated tag: `git tag -a v1.33.7`.
|
||||||
On the first line, write "Version 1.33.7", followed by a blank line.
|
On the first line, write "Version 1.33.7", followed by a blank line.
|
||||||
Then describe the changes in Markdown.
|
Then describe the changes in Markdown.
|
||||||
Make sure not to use `#` in the text, since git will treat it as a comment.
|
Make sure not to use `#` in the text, since git will treat it as a comment.
|
||||||
|
|||||||
BIN
lib/native/liblwjgl-arm64.dylib
Normal file
@ -1 +1 @@
|
|||||||
Subproject commit 072ad32d0e512829c9ef50d12f56df7664e19b4b
|
Subproject commit bec1cc6b1e9e588692f753e9c617063c74967fed
|
||||||
@ -1 +0,0 @@
|
|||||||
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "2.1.1")
|
|
||||||
@ -1,2 +1,2 @@
|
|||||||
# suppress inspection "UnusedProperty" for whole file
|
# suppress inspection "UnusedProperty" for whole file
|
||||||
sbt.version = 1.8.3
|
sbt.version = 1.10.7
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
addSbtPlugin("com.eed3si9n" % "sbt-buildinfo" % "0.11.0")
|
|
||||||
3
project/plugins.sbt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
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
Normal file → Executable file
BIN
sprites/icons/Cut.png
Normal file
|
After Width: | Height: | Size: 239 B |
BIN
sprites/icons/Keyboard.png
Normal file
|
After Width: | Height: | Size: 169 B |
BIN
sprites/icons/KeyboardOff.png
Normal file
|
After Width: | Height: | Size: 210 B |
BIN
sprites/icons/Paste.png
Normal file
|
After Width: | Height: | Size: 206 B |
BIN
sprites/icons/Pause.png
Normal file
|
After Width: | Height: | Size: 170 B |
BIN
sprites/icons/Play.png
Normal file
|
After Width: | Height: | Size: 207 B |
BIN
sprites/icons/SettingsKeymap.png
Normal file
|
After Width: | Height: | Size: 196 B |
BIN
sprites/icons/SideAny.png
Normal file
|
After Width: | Height: | Size: 161 B |
BIN
sprites/icons/SideDown.png
Normal file
|
After Width: | Height: | Size: 158 B |
BIN
sprites/icons/SideEast.png
Normal file
|
After Width: | Height: | Size: 161 B |
BIN
sprites/icons/SideNone.png
Normal file
|
After Width: | Height: | Size: 178 B |
BIN
sprites/icons/SideNorth.png
Normal file
|
After Width: | Height: | Size: 154 B |
BIN
sprites/icons/SideSouth.png
Normal file
|
After Width: | Height: | Size: 150 B |
BIN
sprites/icons/SideUndefined.png
Normal file
|
After Width: | Height: | Size: 160 B |
BIN
sprites/icons/SideUp.png
Normal file
|
After Width: | Height: | Size: 154 B |
BIN
sprites/icons/SideWest.png
Normal file
|
After Width: | Height: | Size: 160 B |
BIN
sprites/nodes/ocelot-block/Default.png
Normal file
|
After Width: | Height: | Size: 644 B |
BIN
sprites/nodes/ocelot-block/Rx.png
Normal file
|
After Width: | Height: | Size: 102 B |
BIN
sprites/nodes/ocelot-block/Tx.png
Normal file
|
After Width: | Height: | Size: 101 B |
BIN
sprites/particles/Smoke.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 120 B |
|
Before Width: | Height: | Size: 123 B |
|
Before Width: | Height: | Size: 167 B |
|
Before Width: | Height: | Size: 546 B |
|
Before Width: | Height: | Size: 157 B |
|
Before Width: | Height: | Size: 171 B |
BIN
sprites/screen/InnerBorderB.png
Normal file
|
After Width: | Height: | Size: 183 B |
BIN
sprites/screen/InnerBorderT.png
Normal file
|
After Width: | Height: | Size: 183 B |
BIN
sprites/screen/InnerCornerBL.png
Normal file
|
After Width: | Height: | Size: 201 B |
BIN
sprites/screen/InnerCornerBR.png
Normal file
|
After Width: | Height: | Size: 188 B |
BIN
sprites/screen/InnerCornerTL.png
Normal file
|
After Width: | Height: | Size: 188 B |
BIN
sprites/screen/InnerCornerTR.png
Normal file
|
After Width: | Height: | Size: 203 B |
BIN
sprites/screen/OuterBorderT.png
Normal file
|
After Width: | Height: | Size: 190 B |
BIN
sprites/screen/OuterCornerBL.png
Normal file
|
After Width: | Height: | Size: 219 B |
BIN
sprites/screen/OuterCornerBR.png
Normal file
|
After Width: | Height: | Size: 229 B |
BIN
sprites/screen/OuterCornerTL.png
Normal file
|
After Width: | Height: | Size: 210 B |
BIN
sprites/screen/OuterCornerTR.png
Normal file
|
After Width: | Height: | Size: 228 B |
31
src/main/resources/LICENSE-lwjgl
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
@ -6,11 +6,12 @@ AboutForegroundSubtle = #eeeeeeee
|
|||||||
AboutButtonForeground = #222222
|
AboutButtonForeground = #222222
|
||||||
AboutButtonBorder = #444444
|
AboutButtonBorder = #444444
|
||||||
AboutButtonBackground = #777777
|
AboutButtonBackground = #777777
|
||||||
|
AboutButtonBackgroundActive = #888888
|
||||||
|
|
||||||
PortDown = #8382d8
|
PortDown = #8382d8
|
||||||
PortUp = #75bdc1
|
PortUp = #75bdc1
|
||||||
PortNorth = #c8ca5f
|
PortNorth = #c8ca5f
|
||||||
PortSouth = #990da3
|
PortSouth = #ed8ef4
|
||||||
PortEast = #7ec95f
|
PortEast = #7ec95f
|
||||||
PortWest = #db7d75
|
PortWest = #db7d75
|
||||||
PortAny = #9b9b9b
|
PortAny = #9b9b9b
|
||||||
@ -22,6 +23,7 @@ Tier3 = #c354cd
|
|||||||
|
|
||||||
Label = #333333
|
Label = #333333
|
||||||
LabelError = #aa0000
|
LabelError = #aa0000
|
||||||
|
LabelDisabled = #888888
|
||||||
|
|
||||||
Scrollbar = #e5e5e526
|
Scrollbar = #e5e5e526
|
||||||
ScrollbarThumb = #cc3f72
|
ScrollbarThumb = #cc3f72
|
||||||
@ -41,15 +43,14 @@ ContextMenuHover = #363636
|
|||||||
ContextMenuSeparator = #444444
|
ContextMenuSeparator = #444444
|
||||||
ContextMenuIcon = #7F7F7F
|
ContextMenuIcon = #7F7F7F
|
||||||
|
|
||||||
ComponentSelectorBackground = #222222cc
|
|
||||||
ComponentSelectorBorder = #444444
|
|
||||||
ComponentSelectorText = #b0b0b0
|
|
||||||
|
|
||||||
ComputerAddress = #333333
|
ComputerAddress = #333333
|
||||||
|
|
||||||
ScreenOff = #000000
|
ScreenOff = #000000
|
||||||
|
|
||||||
|
WindowBackground = #c6c6c6
|
||||||
|
|
||||||
StatusBar = #181818
|
StatusBar = #181818
|
||||||
|
StatusBarActive = #ffffff04
|
||||||
StatusBarBorder = #222222
|
StatusBarBorder = #222222
|
||||||
StatusBarForeground = #777777
|
StatusBarForeground = #777777
|
||||||
StatusBarIcon = #bbbbbb
|
StatusBarIcon = #bbbbbb
|
||||||
@ -66,14 +67,17 @@ TextInputBorder = #888888
|
|||||||
TextInputBorderDisabled = #666666
|
TextInputBorderDisabled = #666666
|
||||||
TextInputBorderFocused = #336666
|
TextInputBorderFocused = #336666
|
||||||
TextInputBackground = #aaaaaa
|
TextInputBackground = #aaaaaa
|
||||||
TextInputBackgroundDisabled = #aaaaaa
|
TextInputBackgroundActive = #bbbbbb
|
||||||
|
TextInputBackgroundSelected = #336666
|
||||||
TextInputForeground = #333333
|
TextInputForeground = #333333
|
||||||
|
TextInputForegroundSelected = #aaaaaa
|
||||||
TextInputForegroundDisabled = #888888
|
TextInputForegroundDisabled = #888888
|
||||||
TextInputBorderError = #aa8888
|
TextInputBorderError = #aa8888
|
||||||
TextInputBorderErrorDisabled = #aa8888
|
TextInputBorderErrorDisabled = #aa8888
|
||||||
TextInputBorderErrorFocused = #cc6666
|
TextInputBorderErrorFocused = #cc6666
|
||||||
|
|
||||||
ButtonBackground = #aaaaaa
|
ButtonBackground = #aaaaaa
|
||||||
|
ButtonBackgroundActive = #bbbbbb
|
||||||
ButtonBorder = #888888
|
ButtonBorder = #888888
|
||||||
ButtonForeground = #333333
|
ButtonForeground = #333333
|
||||||
ButtonForegroundPressed = #444444
|
ButtonForegroundPressed = #444444
|
||||||
@ -89,7 +93,11 @@ HistogramBarTop = #ccfdcc
|
|||||||
HistogramBarFill = #64cc65
|
HistogramBarFill = #64cc65
|
||||||
HistogramGrid = #336633
|
HistogramGrid = #336633
|
||||||
HistogramFill = #73ff7360
|
HistogramFill = #73ff7360
|
||||||
|
HistogramFillWarning = #ffda7360
|
||||||
|
HistogramFillError = #ff737f60
|
||||||
HistogramEdge = #ccfdcc
|
HistogramEdge = #ccfdcc
|
||||||
|
HistogramEdgeWarning = #fdeacc
|
||||||
|
HistogramEdgeError = #fdccd0
|
||||||
|
|
||||||
VerticalMenuBackground = #c6c6c6
|
VerticalMenuBackground = #c6c6c6
|
||||||
VerticalMenuEntryIcon = #888888
|
VerticalMenuEntryIcon = #888888
|
||||||
@ -98,12 +106,14 @@ VerticalMenuEntryForeground = #333333
|
|||||||
VerticalMenuBorder = #dfdfdf
|
VerticalMenuBorder = #dfdfdf
|
||||||
|
|
||||||
SliderBackground = #aaaaaa
|
SliderBackground = #aaaaaa
|
||||||
|
SliderBackgroundActive = #bbbbbb
|
||||||
SliderBorder = #888888
|
SliderBorder = #888888
|
||||||
SliderTick = #989898
|
SliderTick = #989898
|
||||||
SliderHandler = #bbbbbb
|
SliderHandler = #bbbbbb
|
||||||
SliderForeground = #333333
|
SliderForeground = #333333
|
||||||
|
|
||||||
CheckboxBackground = #aaaaaa
|
CheckboxBackground = #aaaaaa
|
||||||
|
CheckboxBackgroundActive = #bbbbbb
|
||||||
CheckboxBorder = #888888
|
CheckboxBorder = #888888
|
||||||
CheckboxForeground = #333333
|
CheckboxForeground = #333333
|
||||||
CheckboxLabel = #333333
|
CheckboxLabel = #333333
|
||||||
@ -177,3 +187,14 @@ LogEntryTxForeground = #cccccc
|
|||||||
LogEntryTxBorder = #183a41
|
LogEntryTxBorder = #183a41
|
||||||
|
|
||||||
OcelotCardTooltip = #c1905f
|
OcelotCardTooltip = #c1905f
|
||||||
|
|
||||||
|
RackRelayButtonText = #e0e0e0
|
||||||
|
|
||||||
|
Flash = #ffffff
|
||||||
|
|
||||||
|
RelayTextLow = #009900
|
||||||
|
RelayTextMid = #999900
|
||||||
|
RelayTextHigh = #990000
|
||||||
|
|
||||||
|
BoomCardGlowStart = #ff4010
|
||||||
|
BoomCardGlowEnd = #ff1010
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 144 KiB After Width: | Height: | Size: 150 KiB |
@ -1,322 +1,347 @@
|
|||||||
BackgroundPattern 0 0 304 304
|
BackgroundPattern 0 0 304 304
|
||||||
BarSegment 385 434 16 4
|
BarSegment 308 305 16 4
|
||||||
Empty 249 655 16 16
|
Empty 134 632 16 16
|
||||||
EmptySlot 220 707 18 18
|
EmptySlot 246 567 18 18
|
||||||
Knob 203 434 50 50
|
Knob 203 434 50 50
|
||||||
KnobCenter 254 434 50 50
|
KnobCenter 254 434 50 50
|
||||||
KnobLimits 305 434 50 50
|
KnobLimits 305 434 50 50
|
||||||
Loading 0 305 48 448
|
Loading 0 305 48 448
|
||||||
Logo 305 0 168 200
|
Logo 305 0 168 200
|
||||||
ShadowBorder 279 305 1 24
|
ShadowBorder 279 344 1 24
|
||||||
ShadowCorner 216 674 24 24
|
ShadowCorner 233 674 24 24
|
||||||
TabArrow 134 634 8 14
|
TabArrow 356 452 8 14
|
||||||
blocks/Generic 266 655 16 16
|
blocks/Generic 151 632 16 16
|
||||||
blocks/HologramEffect 287 314 4 4
|
blocks/HologramEffect 291 344 4 4
|
||||||
blocks/HologramProjector1Top 283 655 16 16
|
blocks/HologramProjector1Top 168 632 16 16
|
||||||
blocks/HologramProjector2Top 300 655 16 16
|
blocks/HologramProjector2Top 185 632 16 16
|
||||||
blocks/HologramProjectorSide 317 655 16 16
|
blocks/HologramProjectorSide 202 632 16 16
|
||||||
buttons/BottomDrawerClose 239 707 18 18
|
buttons/BottomDrawerClose 265 567 18 18
|
||||||
buttons/BottomDrawerOpen 258 707 18 18
|
buttons/BottomDrawerOpen 284 567 18 18
|
||||||
buttons/OpenFMRadioCloseOff 404 445 7 8
|
buttons/OpenFMRadioCloseOff 327 316 7 8
|
||||||
buttons/OpenFMRadioCloseOn 412 445 7 8
|
buttons/OpenFMRadioCloseOn 335 316 7 8
|
||||||
buttons/OpenFMRadioRedstoneOff 359 445 8 8
|
buttons/OpenFMRadioRedstoneOff 282 316 8 8
|
||||||
buttons/OpenFMRadioRedstoneOn 368 445 8 8
|
buttons/OpenFMRadioRedstoneOn 291 316 8 8
|
||||||
buttons/OpenFMRadioStartOff 241 674 24 24
|
buttons/OpenFMRadioStartOff 258 674 24 24
|
||||||
buttons/OpenFMRadioStartOn 266 674 24 24
|
buttons/OpenFMRadioStartOn 283 674 24 24
|
||||||
buttons/OpenFMRadioStopOff 291 674 24 24
|
buttons/OpenFMRadioStopOff 308 674 24 24
|
||||||
buttons/OpenFMRadioStopOn 316 674 24 24
|
buttons/OpenFMRadioStopOn 333 674 24 24
|
||||||
buttons/OpenFMRadioVolumeDownOff 143 634 10 10
|
buttons/OpenFMRadioVolumeDownOff 365 452 10 10
|
||||||
buttons/OpenFMRadioVolumeDownOn 154 634 10 10
|
buttons/OpenFMRadioVolumeDownOn 376 452 10 10
|
||||||
buttons/OpenFMRadioVolumeUpOff 165 634 10 10
|
buttons/OpenFMRadioVolumeUpOff 387 452 10 10
|
||||||
buttons/OpenFMRadioVolumeUpOn 176 634 10 10
|
buttons/OpenFMRadioVolumeUpOn 398 452 10 10
|
||||||
buttons/PowerOff 277 707 18 18
|
buttons/PowerOff 303 567 18 18
|
||||||
buttons/PowerOn 296 707 18 18
|
buttons/PowerOn 322 567 18 18
|
||||||
buttons/RackRelayOff 117 655 65 18
|
buttons/RackRelayOff 134 655 65 18
|
||||||
buttons/RackRelayOn 183 655 65 18
|
buttons/RackRelayOn 200 655 65 18
|
||||||
icons/Antenna 334 655 16 16
|
icons/Antenna 219 632 16 16
|
||||||
icons/ArrowRight 351 655 16 16
|
icons/ArrowRight 236 632 16 16
|
||||||
icons/AspectRatio 368 655 16 16
|
icons/AspectRatio 253 632 16 16
|
||||||
icons/Book 385 655 16 16
|
icons/Book 270 632 16 16
|
||||||
icons/ButtonCheck 341 674 17 17
|
icons/ButtonCheck 143 600 17 17
|
||||||
icons/ButtonClipboard 359 674 17 17
|
icons/ButtonClipboard 161 600 17 17
|
||||||
icons/ButtonRandomize 377 674 17 17
|
icons/ButtonRandomize 179 600 17 17
|
||||||
icons/CPU 402 655 16 16
|
icons/CPU 287 632 16 16
|
||||||
icons/Card 419 655 16 16
|
icons/Card 304 632 16 16
|
||||||
icons/Close 134 601 15 14
|
icons/Close 218 600 15 14
|
||||||
icons/Code 436 655 16 16
|
icons/Code 321 632 16 16
|
||||||
icons/ComponentBus 453 655 16 16
|
icons/ComponentBus 338 632 16 16
|
||||||
icons/Copy 470 655 16 16
|
icons/Copy 355 632 16 16
|
||||||
icons/Cross 487 655 16 16
|
icons/Cross 372 632 16 16
|
||||||
icons/Delete 504 655 16 16
|
icons/Cut 389 632 16 16
|
||||||
icons/DragLMB 483 707 21 14
|
icons/Delete 406 632 16 16
|
||||||
icons/DragRMB 505 707 21 14
|
icons/DragLMB 509 567 21 14
|
||||||
icons/EEPROM 521 655 16 16
|
icons/DragRMB 531 567 21 14
|
||||||
icons/Edit 538 655 16 16
|
icons/EEPROM 423 632 16 16
|
||||||
icons/Eject 555 655 16 16
|
icons/Edit 440 632 16 16
|
||||||
icons/File 572 655 16 16
|
icons/Eject 457 632 16 16
|
||||||
icons/Floppy 589 655 16 16
|
icons/File 474 632 16 16
|
||||||
icons/Folder 606 655 16 16
|
icons/Floppy 491 632 16 16
|
||||||
icons/FolderSlash 623 655 16 16
|
icons/Folder 508 632 16 16
|
||||||
icons/Grid 151 707 22 22
|
icons/FolderSlash 525 632 16 16
|
||||||
icons/GridOff 174 707 22 22
|
icons/Grid 177 567 22 22
|
||||||
icons/Guitar 640 655 16 16
|
icons/GridOff 200 567 22 22
|
||||||
icons/HDD 657 655 16 16
|
icons/Guitar 542 632 16 16
|
||||||
icons/Help 674 655 16 16
|
icons/HDD 559 632 16 16
|
||||||
icons/Home 197 707 22 22
|
icons/Help 576 632 16 16
|
||||||
icons/LMB 218 616 11 14
|
icons/Home 223 567 22 22
|
||||||
icons/Label 691 655 16 16
|
icons/Keyboard 593 632 16 16
|
||||||
icons/LinesHorizontal 708 655 16 16
|
icons/KeyboardOff 610 632 16 16
|
||||||
icons/Link 725 655 16 16
|
icons/LMB 453 434 11 14
|
||||||
icons/LinkSlash 742 655 16 16
|
icons/Label 627 632 16 16
|
||||||
icons/Memory 759 655 16 16
|
icons/LinesHorizontal 644 632 16 16
|
||||||
icons/Microchip 776 655 16 16
|
icons/Link 661 632 16 16
|
||||||
icons/NA 793 655 16 16
|
icons/LinkSlash 678 632 16 16
|
||||||
icons/NotificationError 242 616 11 11
|
icons/Memory 695 632 16 16
|
||||||
icons/NotificationInfo 254 616 11 11
|
icons/Microchip 712 632 16 16
|
||||||
icons/NotificationWarning 266 616 11 11
|
icons/NA 729 632 16 16
|
||||||
icons/Ocelot 810 655 16 16
|
icons/NotificationError 477 434 11 11
|
||||||
icons/Pin 173 616 14 14
|
icons/NotificationInfo 489 434 11 11
|
||||||
icons/Plus 827 655 16 16
|
icons/NotificationWarning 501 434 11 11
|
||||||
icons/Power 844 655 16 16
|
icons/Ocelot 746 632 16 16
|
||||||
icons/RMB 230 616 11 14
|
icons/Paste 763 632 16 16
|
||||||
icons/Restart 861 655 16 16
|
icons/Pause 780 632 16 16
|
||||||
icons/Save 878 655 16 16
|
icons/Pin 408 434 14 14
|
||||||
icons/SaveAs 895 655 16 16
|
icons/Play 797 632 16 16
|
||||||
icons/Server 912 655 16 16
|
icons/Plus 814 632 16 16
|
||||||
icons/SettingsSound 134 616 12 17
|
icons/Power 831 632 16 16
|
||||||
icons/SettingsSystem 147 616 12 17
|
icons/RMB 465 434 11 14
|
||||||
icons/SettingsUI 160 616 12 17
|
icons/Restart 848 632 16 16
|
||||||
icons/Tier0 929 655 16 16
|
icons/Save 865 632 16 16
|
||||||
icons/Tier1 946 655 16 16
|
icons/SaveAs 882 632 16 16
|
||||||
icons/Tier2 963 655 16 16
|
icons/Server 899 632 16 16
|
||||||
icons/Tiers 980 655 16 16
|
icons/SettingsKeymap 356 434 12 17
|
||||||
icons/Unpin 188 616 14 14
|
icons/SettingsSound 369 434 12 17
|
||||||
icons/WaveLFSR 799 584 24 10
|
icons/SettingsSystem 382 434 12 17
|
||||||
icons/WaveNoise 824 584 24 10
|
icons/SettingsUI 395 434 12 17
|
||||||
icons/WaveSawtooth 849 584 24 10
|
icons/SideAny 513 434 11 11
|
||||||
icons/WaveSine 874 584 24 10
|
icons/SideDown 525 434 11 11
|
||||||
icons/WaveSquare 899 584 24 10
|
icons/SideEast 537 434 11 11
|
||||||
icons/WaveTriangle 924 584 24 10
|
icons/SideNone 549 434 11 11
|
||||||
icons/Window 997 655 16 16
|
icons/SideNorth 561 434 11 11
|
||||||
icons/WireArrowLeft 281 305 4 8
|
icons/SideSouth 573 434 11 11
|
||||||
icons/WireArrowRight 286 305 4 8
|
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
|
||||||
items/APU0 49 655 16 96
|
items/APU0 49 655 16 96
|
||||||
items/APU1 66 655 16 96
|
items/APU1 66 655 16 96
|
||||||
items/APU2 83 655 16 96
|
items/APU2 83 655 16 96
|
||||||
items/CPU0 201 540 16 16
|
items/CPU0 1001 632 16 16
|
||||||
items/CPU1 218 540 16 16
|
items/CPU1 358 674 16 16
|
||||||
items/CPU2 235 540 16 16
|
items/CPU2 375 674 16 16
|
||||||
items/CardBase 252 540 16 16
|
items/CardBase 392 674 16 16
|
||||||
items/CircuitBoard 269 540 16 16
|
items/CircuitBoard 409 674 16 16
|
||||||
items/ComponentBus0 286 540 16 16
|
items/ComponentBus0 426 674 16 16
|
||||||
items/ComponentBus1 303 540 16 16
|
items/ComponentBus1 443 674 16 16
|
||||||
items/ComponentBus2 320 540 16 16
|
items/ComponentBus2 460 674 16 16
|
||||||
items/ComponentBus3 337 540 16 16
|
items/ComponentBus3 477 674 16 16
|
||||||
items/DataCard0 49 526 16 128
|
items/DataCard0 49 526 16 128
|
||||||
items/DataCard1 66 526 16 128
|
items/DataCard1 66 526 16 128
|
||||||
items/DataCard2 83 526 16 128
|
items/DataCard2 83 526 16 128
|
||||||
items/DebugCard 354 540 16 16
|
items/DebugCard 494 674 16 16
|
||||||
items/DiskDriveMountable 371 540 16 16
|
items/DiskDriveMountable 511 674 16 16
|
||||||
items/EEPROM 388 540 16 16
|
items/EEPROM 528 674 16 16
|
||||||
items/FloppyDisk_dyeBlack 405 540 16 16
|
items/FloppyDisk_dyeBlack 545 674 16 16
|
||||||
items/FloppyDisk_dyeBlue 422 540 16 16
|
items/FloppyDisk_dyeBlue 562 674 16 16
|
||||||
items/FloppyDisk_dyeBrown 439 540 16 16
|
items/FloppyDisk_dyeBrown 579 674 16 16
|
||||||
items/FloppyDisk_dyeCyan 456 540 16 16
|
items/FloppyDisk_dyeCyan 596 674 16 16
|
||||||
items/FloppyDisk_dyeGray 473 540 16 16
|
items/FloppyDisk_dyeGray 613 674 16 16
|
||||||
items/FloppyDisk_dyeGreen 490 540 16 16
|
items/FloppyDisk_dyeGreen 630 674 16 16
|
||||||
items/FloppyDisk_dyeLightBlue 507 540 16 16
|
items/FloppyDisk_dyeLightBlue 647 674 16 16
|
||||||
items/FloppyDisk_dyeLightGray 524 540 16 16
|
items/FloppyDisk_dyeLightGray 664 674 16 16
|
||||||
items/FloppyDisk_dyeLime 541 540 16 16
|
items/FloppyDisk_dyeLime 681 674 16 16
|
||||||
items/FloppyDisk_dyeMagenta 558 540 16 16
|
items/FloppyDisk_dyeMagenta 698 674 16 16
|
||||||
items/FloppyDisk_dyeOrange 575 540 16 16
|
items/FloppyDisk_dyeOrange 715 674 16 16
|
||||||
items/FloppyDisk_dyePink 592 540 16 16
|
items/FloppyDisk_dyePink 732 674 16 16
|
||||||
items/FloppyDisk_dyePurple 609 540 16 16
|
items/FloppyDisk_dyePurple 749 674 16 16
|
||||||
items/FloppyDisk_dyeRed 626 540 16 16
|
items/FloppyDisk_dyeRed 766 674 16 16
|
||||||
items/FloppyDisk_dyeWhite 643 540 16 16
|
items/FloppyDisk_dyeWhite 783 674 16 16
|
||||||
items/FloppyDisk_dyeYellow 660 540 16 16
|
items/FloppyDisk_dyeYellow 800 674 16 16
|
||||||
items/GraphicsCard0 677 540 16 16
|
items/GraphicsCard0 817 674 16 16
|
||||||
items/GraphicsCard1 694 540 16 16
|
items/GraphicsCard1 834 674 16 16
|
||||||
items/GraphicsCard2 711 540 16 16
|
items/GraphicsCard2 851 674 16 16
|
||||||
items/HardDiskDrive0 728 540 16 16
|
items/HardDiskDrive0 868 674 16 16
|
||||||
items/HardDiskDrive1 745 540 16 16
|
items/HardDiskDrive1 885 674 16 16
|
||||||
items/HardDiskDrive2 762 540 16 16
|
items/HardDiskDrive2 902 674 16 16
|
||||||
items/InternetCard 117 707 16 32
|
items/InternetCard 143 567 16 32
|
||||||
items/LinkedCard 100 655 16 96
|
items/LinkedCard 100 655 16 96
|
||||||
items/Memory0 779 540 16 16
|
items/Memory0 919 674 16 16
|
||||||
items/Memory1 796 540 16 16
|
items/Memory1 936 674 16 16
|
||||||
items/Memory2 813 540 16 16
|
items/Memory2 953 674 16 16
|
||||||
items/Memory3 830 540 16 16
|
items/Memory3 970 674 16 16
|
||||||
items/Memory4 847 540 16 16
|
items/Memory4 987 674 16 16
|
||||||
items/Memory5 864 540 16 16
|
items/Memory5 1004 674 16 16
|
||||||
items/Memory6 881 540 16 16
|
items/Memory6 134 707 16 16
|
||||||
items/NetworkCard 898 540 16 16
|
items/NetworkCard 151 707 16 16
|
||||||
items/OcelotCard 100 526 16 128
|
items/OcelotCard 100 526 16 128
|
||||||
items/RedstoneCard0 915 540 16 16
|
items/RedstoneCard0 168 707 16 16
|
||||||
items/RedstoneCard1 932 540 16 16
|
items/RedstoneCard1 185 707 16 16
|
||||||
items/SelfDestructingCard 134 707 16 32
|
items/SelfDestructingCard 160 567 16 32
|
||||||
items/Server0 949 540 16 16
|
items/Server0 202 707 16 16
|
||||||
items/Server1 966 540 16 16
|
items/Server1 219 707 16 16
|
||||||
items/Server2 983 540 16 16
|
items/Server2 236 707 16 16
|
||||||
items/Server3 1000 540 16 16
|
items/Server3 253 707 16 16
|
||||||
items/SoundCard 117 526 16 128
|
items/SoundCard 117 526 16 128
|
||||||
items/TapeCopper 134 567 16 16
|
items/TapeCopper 270 707 16 16
|
||||||
items/TapeDiamond 151 567 16 16
|
items/TapeDiamond 287 707 16 16
|
||||||
items/TapeGold 168 567 16 16
|
items/TapeGold 304 707 16 16
|
||||||
items/TapeGreg 185 567 16 16
|
items/TapeGreg 321 707 16 16
|
||||||
items/TapeIg 202 567 16 16
|
items/TapeIg 338 707 16 16
|
||||||
items/TapeIron 219 567 16 16
|
items/TapeIron 355 707 16 16
|
||||||
items/TapeNetherStar 236 567 16 16
|
items/TapeNetherStar 372 707 16 16
|
||||||
items/TapeSteel 253 567 16 16
|
items/TapeSteel 389 707 16 16
|
||||||
items/WirelessNetworkCard0 270 567 16 16
|
items/WirelessNetworkCard0 406 707 16 16
|
||||||
items/WirelessNetworkCard1 287 567 16 16
|
items/WirelessNetworkCard1 423 707 16 16
|
||||||
light-panel/BookmarkLeft 780 584 18 14
|
light-panel/BookmarkLeft 269 540 18 14
|
||||||
light-panel/BookmarkRight 395 674 20 14
|
light-panel/BookmarkRight 197 600 20 14
|
||||||
light-panel/BorderB 292 314 4 4
|
light-panel/BorderB 296 344 4 4
|
||||||
light-panel/BorderL 382 314 4 2
|
light-panel/BorderL 284 353 4 2
|
||||||
light-panel/BorderR 297 314 4 4
|
light-panel/BorderR 301 344 4 4
|
||||||
light-panel/BorderT 302 314 4 4
|
light-panel/BorderT 306 344 4 4
|
||||||
light-panel/CornerBL 307 314 4 4
|
light-panel/CornerBL 311 344 4 4
|
||||||
light-panel/CornerBR 312 314 4 4
|
light-panel/CornerBR 316 344 4 4
|
||||||
light-panel/CornerTL 317 314 4 4
|
light-panel/CornerTL 321 344 4 4
|
||||||
light-panel/CornerTR 322 314 4 4
|
light-panel/CornerTR 326 344 4 4
|
||||||
light-panel/Fill 285 325 2 2
|
light-panel/Fill 410 344 2 2
|
||||||
light-panel/Vent 356 434 2 38
|
light-panel/Vent 279 305 2 38
|
||||||
nodes/Cable 377 445 8 8
|
nodes/Cable 300 316 8 8
|
||||||
nodes/Camera 304 567 16 16
|
nodes/Camera 440 707 16 16
|
||||||
nodes/Chest 203 616 14 14
|
nodes/Chest 438 434 14 14
|
||||||
nodes/HologramProjector0 321 567 16 16
|
nodes/HologramProjector0 457 707 16 16
|
||||||
nodes/HologramProjector1 338 567 16 16
|
nodes/HologramProjector1 474 707 16 16
|
||||||
nodes/IronNoteBlock 355 567 16 16
|
nodes/IronNoteBlock 491 707 16 16
|
||||||
nodes/Lamp 372 567 16 16
|
nodes/Lamp 508 707 16 16
|
||||||
nodes/LampFrame 389 567 16 16
|
nodes/LampFrame 525 707 16 16
|
||||||
nodes/LampGlow 49 305 128 128
|
nodes/LampGlow 49 305 128 128
|
||||||
nodes/NewNode 406 567 16 16
|
nodes/NewNode 542 707 16 16
|
||||||
nodes/NoteBlock 423 567 16 16
|
nodes/NoteBlock 559 707 16 16
|
||||||
nodes/OpenFMRadio 440 567 16 16
|
nodes/OpenFMRadio 576 707 16 16
|
||||||
nodes/Relay 457 567 16 16
|
nodes/Relay 593 707 16 16
|
||||||
nodes/TapeDrive 474 567 16 16
|
nodes/TapeDrive 610 707 16 16
|
||||||
nodes/computer/Default 491 567 16 16
|
nodes/computer/Default 627 707 16 16
|
||||||
nodes/computer/DiskActivity 508 567 16 16
|
nodes/computer/DiskActivity 644 707 16 16
|
||||||
nodes/computer/Error 525 567 16 16
|
nodes/computer/Error 661 707 16 16
|
||||||
nodes/computer/On 542 567 16 16
|
nodes/computer/On 678 707 16 16
|
||||||
nodes/disk-drive/Default 559 567 16 16
|
nodes/disk-drive/Default 695 707 16 16
|
||||||
nodes/disk-drive/DiskActivity 576 567 16 16
|
nodes/disk-drive/DiskActivity 712 707 16 16
|
||||||
nodes/disk-drive/Floppy 593 567 16 16
|
nodes/disk-drive/Floppy 729 707 16 16
|
||||||
nodes/holidays/Christmas 117 674 32 32
|
nodes/holidays/Christmas 134 674 32 32
|
||||||
nodes/holidays/Halloween 150 674 32 32
|
nodes/holidays/Halloween 167 674 32 32
|
||||||
nodes/holidays/Valentines 183 674 32 32
|
nodes/holidays/Valentines 200 674 32 32
|
||||||
nodes/microcontroller/Default 610 567 16 16
|
nodes/microcontroller/Default 746 707 16 16
|
||||||
nodes/microcontroller/Error 627 567 16 16
|
nodes/microcontroller/Error 763 707 16 16
|
||||||
nodes/microcontroller/On 644 567 16 16
|
nodes/microcontroller/On 780 707 16 16
|
||||||
nodes/rack/Default 661 567 16 16
|
nodes/ocelot-block/Default 117 655 16 80
|
||||||
nodes/rack/Empty 678 567 16 16
|
nodes/ocelot-block/Rx 797 707 16 16
|
||||||
nodes/rack/drive/0/Default 695 567 16 16
|
nodes/ocelot-block/Tx 814 707 16 16
|
||||||
nodes/rack/drive/0/DiskActivity 712 567 16 16
|
nodes/rack/Default 831 707 16 16
|
||||||
nodes/rack/drive/0/Floppy 729 567 16 16
|
nodes/rack/Empty 848 707 16 16
|
||||||
nodes/rack/drive/1/Default 746 567 16 16
|
nodes/rack/drive/0/Default 865 707 16 16
|
||||||
nodes/rack/drive/1/DiskActivity 763 567 16 16
|
nodes/rack/drive/0/DiskActivity 882 707 16 16
|
||||||
nodes/rack/drive/1/Floppy 780 567 16 16
|
nodes/rack/drive/0/Floppy 899 707 16 16
|
||||||
nodes/rack/drive/2/Default 797 567 16 16
|
nodes/rack/drive/1/Default 916 707 16 16
|
||||||
nodes/rack/drive/2/DiskActivity 814 567 16 16
|
nodes/rack/drive/1/DiskActivity 933 707 16 16
|
||||||
nodes/rack/drive/2/Floppy 831 567 16 16
|
nodes/rack/drive/1/Floppy 950 707 16 16
|
||||||
nodes/rack/drive/3/Default 848 567 16 16
|
nodes/rack/drive/2/Default 967 707 16 16
|
||||||
nodes/rack/drive/3/DiskActivity 865 567 16 16
|
nodes/rack/drive/2/DiskActivity 984 707 16 16
|
||||||
nodes/rack/drive/3/Floppy 882 567 16 16
|
nodes/rack/drive/2/Floppy 1001 707 16 16
|
||||||
nodes/rack/drive/Floppy 899 567 16 16
|
nodes/rack/drive/3/Default 266 655 16 16
|
||||||
nodes/rack/server/0/Default 916 567 16 16
|
nodes/rack/drive/3/DiskActivity 283 655 16 16
|
||||||
nodes/rack/server/0/DiskActivity 933 567 16 16
|
nodes/rack/drive/3/Floppy 300 655 16 16
|
||||||
nodes/rack/server/0/Error 950 567 16 16
|
nodes/rack/drive/Floppy 317 655 16 16
|
||||||
nodes/rack/server/0/NetworkActivity 967 567 16 16
|
nodes/rack/server/0/Default 334 655 16 16
|
||||||
nodes/rack/server/0/On 984 567 16 16
|
nodes/rack/server/0/DiskActivity 351 655 16 16
|
||||||
nodes/rack/server/1/Default 1001 567 16 16
|
nodes/rack/server/0/Error 368 655 16 16
|
||||||
nodes/rack/server/1/DiskActivity 134 584 16 16
|
nodes/rack/server/0/NetworkActivity 385 655 16 16
|
||||||
nodes/rack/server/1/Error 151 584 16 16
|
nodes/rack/server/0/On 402 655 16 16
|
||||||
nodes/rack/server/1/NetworkActivity 168 584 16 16
|
nodes/rack/server/1/Default 419 655 16 16
|
||||||
nodes/rack/server/1/On 185 584 16 16
|
nodes/rack/server/1/DiskActivity 436 655 16 16
|
||||||
nodes/rack/server/2/Default 202 584 16 16
|
nodes/rack/server/1/Error 453 655 16 16
|
||||||
nodes/rack/server/2/DiskActivity 219 584 16 16
|
nodes/rack/server/1/NetworkActivity 470 655 16 16
|
||||||
nodes/rack/server/2/Error 236 584 16 16
|
nodes/rack/server/1/On 487 655 16 16
|
||||||
nodes/rack/server/2/NetworkActivity 253 584 16 16
|
nodes/rack/server/2/Default 504 655 16 16
|
||||||
nodes/rack/server/2/On 270 584 16 16
|
nodes/rack/server/2/DiskActivity 521 655 16 16
|
||||||
nodes/rack/server/3/Default 287 584 16 16
|
nodes/rack/server/2/Error 538 655 16 16
|
||||||
nodes/rack/server/3/DiskActivity 304 584 16 16
|
nodes/rack/server/2/NetworkActivity 555 655 16 16
|
||||||
nodes/rack/server/3/Error 321 584 16 16
|
nodes/rack/server/2/On 572 655 16 16
|
||||||
nodes/rack/server/3/NetworkActivity 338 584 16 16
|
nodes/rack/server/3/Default 589 655 16 16
|
||||||
nodes/rack/server/3/On 355 584 16 16
|
nodes/rack/server/3/DiskActivity 606 655 16 16
|
||||||
nodes/raid/0/DiskActivity 372 584 16 16
|
nodes/rack/server/3/Error 623 655 16 16
|
||||||
nodes/raid/0/Error 389 584 16 16
|
nodes/rack/server/3/NetworkActivity 640 655 16 16
|
||||||
nodes/raid/1/DiskActivity 406 584 16 16
|
nodes/rack/server/3/On 657 655 16 16
|
||||||
nodes/raid/1/Error 423 584 16 16
|
nodes/raid/0/DiskActivity 674 655 16 16
|
||||||
nodes/raid/2/DiskActivity 440 584 16 16
|
nodes/raid/0/Error 691 655 16 16
|
||||||
nodes/raid/2/Error 457 584 16 16
|
nodes/raid/1/DiskActivity 708 655 16 16
|
||||||
nodes/raid/Default 474 584 16 16
|
nodes/raid/1/Error 725 655 16 16
|
||||||
nodes/screen/BottomLeft 491 584 16 16
|
nodes/raid/2/DiskActivity 742 655 16 16
|
||||||
nodes/screen/BottomMiddle 508 584 16 16
|
nodes/raid/2/Error 759 655 16 16
|
||||||
nodes/screen/BottomRight 525 584 16 16
|
nodes/raid/Default 776 655 16 16
|
||||||
nodes/screen/ColumnBottom 542 584 16 16
|
nodes/screen/BottomLeft 793 655 16 16
|
||||||
nodes/screen/ColumnMiddle 559 584 16 16
|
nodes/screen/BottomMiddle 810 655 16 16
|
||||||
nodes/screen/ColumnTop 576 584 16 16
|
nodes/screen/BottomRight 827 655 16 16
|
||||||
nodes/screen/Middle 593 584 16 16
|
nodes/screen/ColumnBottom 844 655 16 16
|
||||||
nodes/screen/MiddleLeft 610 584 16 16
|
nodes/screen/ColumnMiddle 861 655 16 16
|
||||||
nodes/screen/MiddleRight 627 584 16 16
|
nodes/screen/ColumnTop 878 655 16 16
|
||||||
nodes/screen/PowerOnOverlay 644 584 16 16
|
nodes/screen/Middle 895 655 16 16
|
||||||
nodes/screen/RowLeft 661 584 16 16
|
nodes/screen/MiddleLeft 912 655 16 16
|
||||||
nodes/screen/RowMiddle 678 584 16 16
|
nodes/screen/MiddleRight 929 655 16 16
|
||||||
nodes/screen/RowRight 695 584 16 16
|
nodes/screen/PowerOnOverlay 946 655 16 16
|
||||||
nodes/screen/Standalone 712 584 16 16
|
nodes/screen/RowLeft 963 655 16 16
|
||||||
nodes/screen/TopLeft 729 584 16 16
|
nodes/screen/RowMiddle 980 655 16 16
|
||||||
nodes/screen/TopMiddle 746 584 16 16
|
nodes/screen/RowRight 997 655 16 16
|
||||||
nodes/screen/TopRight 763 584 16 16
|
nodes/screen/Standalone 201 540 16 16
|
||||||
panel/BorderB 327 314 4 4
|
nodes/screen/TopLeft 218 540 16 16
|
||||||
panel/BorderL 387 314 4 2
|
nodes/screen/TopMiddle 235 540 16 16
|
||||||
panel/BorderR 332 314 4 4
|
nodes/screen/TopRight 252 540 16 16
|
||||||
panel/BorderT 337 314 4 4
|
panel/BorderB 331 344 4 4
|
||||||
panel/CornerBL 342 314 4 4
|
panel/BorderL 289 353 4 2
|
||||||
panel/CornerBR 347 314 4 4
|
panel/BorderR 336 344 4 4
|
||||||
panel/CornerTL 352 314 4 4
|
panel/BorderT 341 344 4 4
|
||||||
panel/CornerTR 357 314 4 4
|
panel/CornerBL 346 344 4 4
|
||||||
panel/Fill 288 325 2 2
|
panel/CornerBR 351 344 4 4
|
||||||
particles/Note 377 434 7 10
|
panel/CornerTL 356 344 4 4
|
||||||
screen/BorderB 284 314 2 8
|
panel/CornerTR 361 344 4 4
|
||||||
screen/BorderT 281 314 2 10
|
panel/Fill 413 344 2 2
|
||||||
screen/CornerBL 386 445 8 8
|
particles/Note 300 305 7 10
|
||||||
screen/CornerBR 395 445 8 8
|
particles/Smoke 134 567 8 64
|
||||||
screen/CornerTL 359 434 8 10
|
screen/InnerBorderB 281 360 2 4
|
||||||
screen/CornerTR 368 434 8 10
|
screen/InnerBorderT 284 360 2 4
|
||||||
window/BorderDark 281 325 1 4
|
screen/InnerCornerBL 366 344 4 4
|
||||||
window/BorderLight 283 325 1 4
|
screen/InnerCornerBR 371 344 4 4
|
||||||
window/CornerBL 362 314 4 4
|
screen/InnerCornerTL 376 344 4 4
|
||||||
window/CornerBR 367 314 4 4
|
screen/InnerCornerTR 381 344 4 4
|
||||||
window/CornerTL 372 314 4 4
|
screen/OuterBorderT 281 353 2 6
|
||||||
window/CornerTR 377 314 4 4
|
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
|
||||||
window/OpenFMRadio 474 0 232 105
|
window/OpenFMRadio 474 0 232 105
|
||||||
window/case/Motherboard 123 434 79 70
|
window/case/Motherboard 123 434 79 70
|
||||||
window/rack/Lines 49 434 73 91
|
window/rack/Lines 49 434 73 91
|
||||||
window/rack/Motherboard 178 305 100 78
|
window/rack/Motherboard 178 305 100 78
|
||||||
window/rack/NetworkBack 299 319 1 2
|
window/rack/NetworkBack 293 365 1 2
|
||||||
window/rack/NetworkBottom 301 319 1 2
|
window/rack/NetworkBottom 295 365 1 2
|
||||||
window/rack/NetworkConnector 303 319 1 2
|
window/rack/NetworkConnector 297 365 1 2
|
||||||
window/rack/NetworkLeft 305 319 1 2
|
window/rack/NetworkLeft 299 365 1 2
|
||||||
window/rack/NetworkRight 307 319 1 2
|
window/rack/NetworkRight 301 365 1 2
|
||||||
window/rack/NetworkTop 309 319 1 2
|
window/rack/NetworkTop 303 365 1 2
|
||||||
window/rack/NodeBack 392 314 5 1
|
window/rack/NodeBack 287 360 5 1
|
||||||
window/rack/NodeBottom 398 314 5 1
|
window/rack/NodeBottom 293 360 5 1
|
||||||
window/rack/NodeLeft 404 314 5 1
|
window/rack/NodeLeft 299 360 5 1
|
||||||
window/rack/NodeRight 410 314 5 1
|
window/rack/NodeRight 305 360 5 1
|
||||||
window/rack/NodeTop 416 314 5 1
|
window/rack/NodeTop 311 360 5 1
|
||||||
window/rack/SideBack 287 319 1 3
|
window/rack/SideBack 281 365 1 3
|
||||||
window/rack/SideBottom 289 319 1 3
|
window/rack/SideBottom 283 365 1 3
|
||||||
window/rack/SideConnector 291 319 1 3
|
window/rack/SideConnector 285 365 1 3
|
||||||
window/rack/SideLeft 293 319 1 3
|
window/rack/SideLeft 287 365 1 3
|
||||||
window/rack/SideRight 295 319 1 3
|
window/rack/SideRight 289 365 1 3
|
||||||
window/rack/SideTop 297 319 1 3
|
window/rack/SideTop 291 365 1 3
|
||||||
window/raid/Slots 134 540 66 26
|
window/raid/Slots 134 540 66 26
|
||||||
window/tape/Back 315 707 20 15
|
window/tape/Back 341 567 20 15
|
||||||
window/tape/BackPressed 336 707 20 15
|
window/tape/BackPressed 362 567 20 15
|
||||||
window/tape/Forward 357 707 20 15
|
window/tape/Forward 383 567 20 15
|
||||||
window/tape/ForwardPressed 378 707 20 15
|
window/tape/ForwardPressed 404 567 20 15
|
||||||
window/tape/Play 399 707 20 15
|
window/tape/Play 425 567 20 15
|
||||||
window/tape/PlayPressed 420 707 20 15
|
window/tape/PlayPressed 446 567 20 15
|
||||||
window/tape/Screen 134 526 146 13
|
window/tape/Screen 134 526 146 13
|
||||||
window/tape/Stop 441 707 20 15
|
window/tape/Stop 467 567 20 15
|
||||||
window/tape/StopPressed 462 707 20 15
|
window/tape/StopPressed 488 567 20 15
|
||||||
|
|||||||
@ -46,11 +46,31 @@ ocelot {
|
|||||||
# Otherwise, Ocelot doesn't bother asking and saves the workspace right away if it knows where.
|
# Otherwise, Ocelot doesn't bother asking and saves the workspace right away if it knows where.
|
||||||
saveOnExit: true
|
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.
|
# If enabled, Ocelot loads the most recently opened workspace automatically on startup.
|
||||||
openLastWorkspace: true
|
openLastWorkspace: true
|
||||||
|
|
||||||
# If true, screen nodes will render mini-previews of their content in realtime
|
# If true, screen nodes will render mini-previews of their content in realtime
|
||||||
# Otherwise, content will be shown only in windows
|
# Otherwise, content will be shown only in windows
|
||||||
renderScreenDataOnNodes: false
|
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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
#version 140
|
#version 150
|
||||||
|
|
||||||
in vec3 inPos;
|
in vec3 inPos;
|
||||||
in vec3 inNormal;
|
in vec3 inNormal;
|
||||||
|
|||||||
BIN
src/main/resources/ocelot/desktop/sounds/interface/shutter.ogg
Normal file
@ -28,7 +28,7 @@ class ColorScheme extends Logging {
|
|||||||
val bytes = java.lang.Long.parseLong(value.substring(1), 16)
|
val bytes = java.lang.Long.parseLong(value.substring(1), 16)
|
||||||
val color = if (value.length == 9) {
|
val color = if (value.length == 9) {
|
||||||
val rgb = bytes >> 8
|
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
|
} else IntColor(bytes.toInt).toRGBANorm
|
||||||
entries.addOne((key, color))
|
entries.addOne((key, color))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package ocelot.desktop
|
package ocelot.desktop
|
||||||
|
|
||||||
import buildinfo.BuildInfo
|
import buildinfo.BuildInfo
|
||||||
|
import ocelot.desktop.geometry.Size2D
|
||||||
import ocelot.desktop.inventory.Items
|
import ocelot.desktop.inventory.Items
|
||||||
import ocelot.desktop.ui.UiHandler
|
import ocelot.desktop.ui.UiHandler
|
||||||
import ocelot.desktop.ui.swing.SplashScreen
|
import ocelot.desktop.ui.swing.SplashScreen
|
||||||
@ -29,13 +30,11 @@ import scala.io.Source
|
|||||||
import scala.jdk.CollectionConverters._
|
import scala.jdk.CollectionConverters._
|
||||||
import scala.util.{Failure, Success, Try, Using}
|
import scala.util.{Failure, Success, Try, Using}
|
||||||
|
|
||||||
object OcelotDesktop
|
// LoggingConfiguration configures Log4j appenders & loggers, it should come before Logging in inheritance hierarchy
|
||||||
// This configures Log4j appenders & loggers, it should come before Logging in inheritance hierarchy
|
object OcelotDesktop extends LoggingConfiguration with Logging {
|
||||||
extends LoggingConfiguration
|
|
||||||
with Logging
|
|
||||||
{
|
|
||||||
System.setProperty("awt.useSystemAAFontSettings", "on")
|
System.setProperty("awt.useSystemAAFontSettings", "on")
|
||||||
System.setProperty("swing.aatext", "true")
|
System.setProperty("swing.aatext", "true")
|
||||||
|
System.setProperty("LWJGL_WM_CLASS", "Ocelot Desktop")
|
||||||
|
|
||||||
// Required to make all subsequent swing components look "native"
|
// Required to make all subsequent swing components look "native"
|
||||||
try UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName)
|
try UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName)
|
||||||
@ -49,6 +48,16 @@ object OcelotDesktop
|
|||||||
val tpsCounter = new FPSCalculator
|
val tpsCounter = new FPSCalculator
|
||||||
val ticker = new Ticker
|
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
|
private val TickerIntervalHistorySize = 5
|
||||||
val tickerIntervalHistory = new mutable.Queue[Duration](TickerIntervalHistorySize)
|
val tickerIntervalHistory = new mutable.Queue[Duration](TickerIntervalHistorySize)
|
||||||
|
|
||||||
@ -68,11 +77,10 @@ object OcelotDesktop
|
|||||||
splashScreen.setStatus("Loading configuration...", 0.10f)
|
splashScreen.setStatus("Loading configuration...", 0.10f)
|
||||||
val customConfigPath = args.get(CommandLine.ConfigPath).flatten
|
val customConfigPath = args.get(CommandLine.ConfigPath).flatten
|
||||||
|
|
||||||
val desktopConfigPath: Path =
|
val desktopConfigPath: Path = {
|
||||||
if (customConfigPath.isDefined) {
|
if (customConfigPath.isDefined) {
|
||||||
Paths.get(customConfigPath.get)
|
Paths.get(customConfigPath.get)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// TODO: migration for old locations of ocelot.conf, can be safely removed later
|
// 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!
|
// TODO: uncomment this line and delete everything below it when you're ready!
|
||||||
// OcelotPaths.desktopConfig
|
// OcelotPaths.desktopConfig
|
||||||
@ -81,23 +89,24 @@ object OcelotDesktop
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (!Files.exists(newConfigPath)) {
|
if (!Files.exists(newConfigPath)) {
|
||||||
val oldConfigPath =
|
val oldConfigPath = {
|
||||||
if (SystemUtils.IS_OS_WINDOWS)
|
if (SystemUtils.IS_OS_WINDOWS)
|
||||||
Paths.get(OcelotPaths.windowsAppDataDirectoryName, "Ocelot", "ocelot.conf")
|
Paths.get(OcelotPaths.windowsAppDataDirectoryName, "Ocelot", "ocelot.conf")
|
||||||
else
|
else
|
||||||
Paths.get(OcelotPaths.linuxHomeDirectoryName, ".config", "ocelot", "ocelot.conf")
|
Paths.get(OcelotPaths.linuxHomeDirectoryName, ".config", "ocelot", "ocelot.conf")
|
||||||
|
}
|
||||||
|
|
||||||
if (Files.exists(oldConfigPath))
|
if (Files.exists(oldConfigPath))
|
||||||
Files.move(oldConfigPath, newConfigPath)
|
Files.move(oldConfigPath, newConfigPath)
|
||||||
}
|
}
|
||||||
}
|
} catch {
|
||||||
catch {
|
|
||||||
case _: Throwable =>
|
case _: Throwable =>
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: end of upper todo <3
|
// TODO: end of upper todo <3
|
||||||
newConfigPath
|
newConfigPath
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Settings.load(desktopConfigPath)
|
Settings.load(desktopConfigPath)
|
||||||
|
|
||||||
@ -123,8 +132,7 @@ object OcelotDesktop
|
|||||||
val loadRecentWorkspace = Settings.get.recentWorkspace.isDefined && Settings.get.openLastWorkspace
|
val loadRecentWorkspace = Settings.get.recentWorkspace.isDefined && Settings.get.openLastWorkspace
|
||||||
|
|
||||||
root = new RootWidget(!loadRecentWorkspace)
|
root = new RootWidget(!loadRecentWorkspace)
|
||||||
root.width = Display.getWidth
|
root.size = Size2D(Display.getWidth / Settings.get.scaleFactor, Display.getHeight / Settings.get.scaleFactor)
|
||||||
root.height = Display.getHeight
|
|
||||||
UiHandler.setRoot(root)
|
UiHandler.setRoot(root)
|
||||||
|
|
||||||
splashScreen.setStatus("Loading workspace...", 0.90f)
|
splashScreen.setStatus("Loading workspace...", 0.90f)
|
||||||
@ -156,22 +164,30 @@ object OcelotDesktop
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val updateThread = new Thread(() => try {
|
val updateThread = new Thread(
|
||||||
|
() =>
|
||||||
|
try {
|
||||||
val currentThread = Thread.currentThread()
|
val currentThread = Thread.currentThread()
|
||||||
|
|
||||||
while (!currentThread.isInterrupted) {
|
while (!currentThread.isInterrupted) {
|
||||||
|
if (!emulationPaused) {
|
||||||
Profiler.measure("tick") {
|
Profiler.measure("tick") {
|
||||||
withTickLockAcquired {
|
withTickLockAcquired {
|
||||||
workspace.update()
|
workspace.update()
|
||||||
|
updateThreadTasks.run()
|
||||||
|
|
||||||
tpsCounter.tick()
|
tpsCounter.tick()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ticker.waitNext()
|
ticker.waitNext(!emulationPaused)
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
case _: InterruptedException => // ignore
|
case _: InterruptedException => // ignore
|
||||||
}, "update-thread")
|
},
|
||||||
|
"update-thread",
|
||||||
|
)
|
||||||
|
|
||||||
updateThread.start()
|
updateThread.start()
|
||||||
splashScreen.dispose()
|
splashScreen.dispose()
|
||||||
@ -181,7 +197,8 @@ object OcelotDesktop
|
|||||||
logger.info("Cleaning up")
|
logger.info("Cleaning up")
|
||||||
updateThread.interrupt()
|
updateThread.interrupt()
|
||||||
|
|
||||||
try updateThread.join() catch {
|
try updateThread.join()
|
||||||
|
catch {
|
||||||
case _: InterruptedException =>
|
case _: InterruptedException =>
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,7 +248,29 @@ object OcelotDesktop
|
|||||||
root.workspaceView.load(frontendNBT)
|
root.workspaceView.load(frontendNBT)
|
||||||
if (frontendNBT.hasKey("players")) {
|
if (frontendNBT.hasKey("players")) {
|
||||||
players.clear()
|
players.clear()
|
||||||
players.addAll(frontendNBT.getTagList("players", NBT.TAG_STRING).map((player: NBTTagString) => User(player.getString)))
|
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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,31 +280,37 @@ object OcelotDesktop
|
|||||||
|
|
||||||
private def savePath_=(path: Option[Path]): Unit = {
|
private def savePath_=(path: Option[Path]): Unit = {
|
||||||
_savePath = path
|
_savePath = path
|
||||||
UiHandler.requestWindowTitle(UiHandler.baseWindowTitle + path.map(" - " + _).getOrElse(""))
|
|
||||||
|
UiHandler.windowTitleSuffix = path.map(_.toString)
|
||||||
}
|
}
|
||||||
|
|
||||||
private val tmpPath = Files.createTempDirectory("ocelot-save")
|
private val tmpPath = Files.createTempDirectory("ocelot-save")
|
||||||
|
|
||||||
Runtime.getRuntime.addShutdownHook(new Thread(() => {
|
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?") {
|
def newWorkspace(): Unit = showCloseConfirmationDialog("Save workspace before opening a new one?") {
|
||||||
root.workspaceView.newWorkspace()
|
root.workspaceView.newWorkspace()
|
||||||
savePath = None
|
savePath = None
|
||||||
Settings.get.recentWorkspace = None
|
Settings.get.recentWorkspace = None
|
||||||
|
resetAutosave()
|
||||||
}
|
}
|
||||||
|
|
||||||
private def saveTo(outputPath: Path): Unit = {
|
private def saveTo(outputPath: Path): Unit = {
|
||||||
val oldPath = workspace.path
|
val oldPath = workspace.path
|
||||||
|
|
||||||
if (oldPath != outputPath) {
|
if (oldPath != outputPath) {
|
||||||
val (oldFiles, newFiles) =
|
val (oldFiles, newFiles) = {
|
||||||
Using.resources(Files.list(oldPath), Files.list(outputPath)) { (oldDirStream, newDirStream) =>
|
Using.resources(Files.list(oldPath), Files.list(outputPath)) { (oldDirStream, newDirStream) =>
|
||||||
val oldFiles = oldDirStream.iterator.asScala.toArray
|
val oldFiles = oldDirStream.iterator.asScala.toArray
|
||||||
val newFiles = newDirStream.iterator.asScala.toArray
|
val newFiles = newDirStream.iterator.asScala.toArray
|
||||||
(oldFiles, newFiles)
|
(oldFiles, newFiles)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val toRemove = newFiles.intersect(oldFiles)
|
val toRemove = newFiles.intersect(oldFiles)
|
||||||
|
|
||||||
@ -283,8 +328,7 @@ object OcelotDesktop
|
|||||||
|
|
||||||
if (Files.isDirectory(path)) {
|
if (Files.isDirectory(path)) {
|
||||||
FileUtils.copyDirectory(oldFile, newFile)
|
FileUtils.copyDirectory(oldFile, newFile)
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
FileUtils.copyFile(oldFile, newFile)
|
FileUtils.copyFile(oldFile, newFile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -327,11 +371,12 @@ object OcelotDesktop
|
|||||||
|
|
||||||
def saveAs(): Unit = showSaveDialog()
|
def saveAs(): Unit = showSaveDialog()
|
||||||
|
|
||||||
def showOpenDialog(): Unit =
|
def showOpenDialog(): Unit = {
|
||||||
showFileChooserDialog(JFileChooser.OPEN_DIALOG, JFileChooser.DIRECTORIES_ONLY) {
|
showFileChooserDialog(JFileChooser.OPEN_DIALOG, JFileChooser.DIRECTORIES_ONLY) {
|
||||||
case Some(dir) => load(dir)
|
case Some(dir) => load(dir)
|
||||||
case None => Success(())
|
case None => Success(())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
def load(dir: File): Try[Unit] = {
|
def load(dir: File): Try[Unit] = {
|
||||||
val path = Paths.get(dir.getCanonicalPath, "workspace.nbt")
|
val path = Paths.get(dir.getCanonicalPath, "workspace.nbt")
|
||||||
@ -348,6 +393,9 @@ object OcelotDesktop
|
|||||||
Settings.get.recentWorkspace = Some(dir.getCanonicalPath)
|
Settings.get.recentWorkspace = Some(dir.getCanonicalPath)
|
||||||
workspace.path = dir.toPath
|
workspace.path = dir.toPath
|
||||||
loadWorld(nbt)
|
loadWorld(nbt)
|
||||||
|
|
||||||
|
resetAutosave()
|
||||||
|
logger.info(s"Workspace successfully loaded from: $path")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else Failure(new FileNotFoundException("Specified directory does not contain 'workspace.nbt'"))
|
} else Failure(new FileNotFoundException("Specified directory does not contain 'workspace.nbt'"))
|
||||||
@ -374,13 +422,16 @@ object OcelotDesktop
|
|||||||
val selectedFile =
|
val selectedFile =
|
||||||
Option.when(chooser.showDialog(null, null) == JFileChooser.APPROVE_OPTION)(chooser.getSelectedFile)
|
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 {
|
result match {
|
||||||
case f @ Failure(_) => showFailureMessage(f)
|
case f @ Failure(_) => showFailureMessage(f)
|
||||||
case Success(_) =>
|
case Success(_) =>
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}).start()
|
}).start()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,13 +442,13 @@ object OcelotDesktop
|
|||||||
|
|
||||||
new NotificationDialog(
|
new NotificationDialog(
|
||||||
s"Something went wrong!\n($exception)\nCheck the log file for a full stacktrace.",
|
s"Something went wrong!\n($exception)\nCheck the log file for a full stacktrace.",
|
||||||
NotificationType.Error
|
NotificationType.Error,
|
||||||
).addCloseButton().show()
|
).addCloseButton().show()
|
||||||
}
|
}
|
||||||
|
|
||||||
def showAddPlayerDialog(): Unit = new InputDialog(
|
def showAddPlayerDialog(): Unit = new InputDialog(
|
||||||
"Add new player",
|
"Add new player",
|
||||||
text => OcelotDesktop.selectPlayer(text)
|
text => OcelotDesktop.selectPlayer(text),
|
||||||
).show()
|
).show()
|
||||||
|
|
||||||
def player: User = if (players.nonEmpty) players.head else User("myself")
|
def player: User = if (players.nonEmpty) players.head else User("myself")
|
||||||
@ -424,19 +475,23 @@ object OcelotDesktop
|
|||||||
}
|
}
|
||||||
|
|
||||||
var workspace: Workspace = _
|
var workspace: Workspace = _
|
||||||
|
val updateThreadTasks = new TaskQueue()
|
||||||
|
|
||||||
private def createWorkspace(): Unit = {
|
private def createWorkspace(): Unit = {
|
||||||
workspace = new Workspace(tmpPath)
|
workspace = new Workspace(tmpPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
private def prepareSavePath(path: Path)(continuation: => Unit): Unit = {
|
private def prepareSavePath(path: Path)(continuation: => Unit): Unit = {
|
||||||
val nonEmpty = try Using.resource(Files.list(path))(_.iterator.asScala.nonEmpty) catch {
|
val nonEmpty = {
|
||||||
|
try Using.resource(Files.list(path))(_.iterator.asScala.nonEmpty)
|
||||||
|
catch {
|
||||||
case _: FileNotFoundException | _: NoSuchFileException =>
|
case _: FileNotFoundException | _: NoSuchFileException =>
|
||||||
logger.info(s"Save path $path does not exist: creating a new directory")
|
logger.info(s"Save path $path does not exist: creating a new directory")
|
||||||
Files.createDirectory(path)
|
Files.createDirectory(path)
|
||||||
|
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (nonEmpty) {
|
if (nonEmpty) {
|
||||||
new NotificationDialog(
|
new NotificationDialog(
|
||||||
@ -444,7 +499,7 @@ object OcelotDesktop
|
|||||||
|Files in the save directory will be included in the workspace.
|
|Files in the save directory will be included in the workspace.
|
||||||
|They may be overwritten, causing loss of data.
|
|They may be overwritten, causing loss of data.
|
||||||
|Proceed with saving anyway?""".stripMargin,
|
|Proceed with saving anyway?""".stripMargin,
|
||||||
NotificationType.Warning
|
NotificationType.Warning,
|
||||||
) {
|
) {
|
||||||
addButton("Cancel") {
|
addButton("Cancel") {
|
||||||
close()
|
close()
|
||||||
@ -457,7 +512,7 @@ object OcelotDesktop
|
|||||||
} else continuation
|
} else continuation
|
||||||
}
|
}
|
||||||
|
|
||||||
private def showSaveDialog(continuation: => Unit): Unit =
|
private def showSaveDialog(continuation: => Unit): Unit = {
|
||||||
showFileChooserDialog(JFileChooser.SAVE_DIALOG, JFileChooser.DIRECTORIES_ONLY) { dir =>
|
showFileChooserDialog(JFileChooser.SAVE_DIALOG, JFileChooser.DIRECTORIES_ONLY) { dir =>
|
||||||
Try {
|
Try {
|
||||||
if (dir.nonEmpty) {
|
if (dir.nonEmpty) {
|
||||||
@ -471,6 +526,9 @@ object OcelotDesktop
|
|||||||
|
|
||||||
save(continuation)
|
save(continuation)
|
||||||
Settings.get.recentWorkspace = dir.map(_.getCanonicalPath)
|
Settings.get.recentWorkspace = dir.map(_.getCanonicalPath)
|
||||||
|
|
||||||
|
resetAutosave()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,11 +5,9 @@ import ocelot.desktop.Settings.ExtendedConfig
|
|||||||
import ocelot.desktop.util.{Logging, SettingsData}
|
import ocelot.desktop.util.{Logging, SettingsData}
|
||||||
import org.apache.commons.lang3.SystemUtils
|
import org.apache.commons.lang3.SystemUtils
|
||||||
|
|
||||||
import java.io.InputStream
|
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.nio.file.{Files, Path}
|
import java.nio.file.{Files, Path}
|
||||||
import java.util
|
import java.util
|
||||||
import scala.io.{Codec, Source}
|
|
||||||
|
|
||||||
class Settings(val config: Config) extends SettingsData {
|
class Settings(val config: Config) extends SettingsData {
|
||||||
// TODO: refactor this mess (having to declare every field 3 times is extremely error-prone)
|
// TODO: refactor this mess (having to declare every field 3 times is extremely error-prone)
|
||||||
@ -44,12 +42,24 @@ class Settings(val config: Config) extends SettingsData {
|
|||||||
windowSize.y -= 16
|
windowSize.y -= 16
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (config.hasPath("ocelot.keymap")) {
|
||||||
|
keymap.load(config.getConfig("ocelot.keymap"))
|
||||||
|
}
|
||||||
|
|
||||||
recentWorkspace = config.getOptionalString("ocelot.workspace.recent")
|
recentWorkspace = config.getOptionalString("ocelot.workspace.recent")
|
||||||
pinNewWindows = config.getBooleanOrElse("ocelot.workspace.pinNewWindows", default = true)
|
pinNewWindows = config.getBooleanOrElse("ocelot.workspace.pinNewWindows", default = true)
|
||||||
unfocusedWindowTransparency = config.getDoubleOrElse("ocelot.workspace.unfocusedWindowTransparency", 0.5)
|
unfocusedWindowTransparency = config.getDoubleOrElse("ocelot.workspace.unfocusedWindowTransparency", 0.5).toFloat
|
||||||
|
unfocusedWindowHide = config.getBooleanOrElse("ocelot.workspace.unfocusedWindowHide", default = false)
|
||||||
saveOnExit = config.getBooleanOrElse("ocelot.workspace.saveOnExit", default = true)
|
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)
|
openLastWorkspace = config.getBooleanOrElse("ocelot.workspace.openLastWorkspace", default = true)
|
||||||
renderScreenDataOnNodes = config.getBooleanOrElse("ocelot.workspace.renderScreenDataOnNodes", default = false)
|
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)
|
||||||
}
|
}
|
||||||
|
|
||||||
object Settings extends Logging {
|
object Settings extends Logging {
|
||||||
@ -66,23 +76,31 @@ object Settings extends Logging {
|
|||||||
if (config.hasPath(path)) config.getBoolean(path)
|
if (config.hasPath(path)) config.getBoolean(path)
|
||||||
else default
|
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 =
|
def getDoubleOrElse(path: String, default: Double): Double =
|
||||||
if (config.hasPath(path)) config.getDouble(path)
|
if (config.hasPath(path)) config.getDouble(path)
|
||||||
else default
|
else default
|
||||||
|
|
||||||
def withValuePreserveOrigin(path: String, value: Any): Config =
|
def withValuePreserveOrigin(path: String, value: Any): Config = {
|
||||||
config.withValue(path,
|
config.withValue(
|
||||||
|
path,
|
||||||
if (config.hasPath(path))
|
if (config.hasPath(path))
|
||||||
ConfigValueFactory.fromAnyRef(value).withOrigin(config.getValue(path).origin())
|
ConfigValueFactory.fromAnyRef(value).withOrigin(config.getValue(path).origin())
|
||||||
else ConfigValueFactory.fromAnyRef(value)
|
else ConfigValueFactory.fromAnyRef(value),
|
||||||
)
|
)
|
||||||
|
}
|
||||||
|
|
||||||
def withValuePreserveOrigin(path: String, value: Option[Any]): Config =
|
def withValuePreserveOrigin(path: String, value: Option[Any]): Config = {
|
||||||
config.withValue(path,
|
config.withValue(
|
||||||
|
path,
|
||||||
if (config.hasPath(path))
|
if (config.hasPath(path))
|
||||||
ConfigValueFactory.fromAnyRef(value.orNull).withOrigin(config.getValue(path).origin())
|
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 = {
|
def withValue(path: String, value: Int2D): Config = {
|
||||||
config.withValue(path, ConfigValueFactory.fromIterable(util.Arrays.asList(value.x, value.y)))
|
config.withValue(path, ConfigValueFactory.fromIterable(util.Arrays.asList(value.x, value.y)))
|
||||||
@ -90,12 +108,16 @@ object Settings extends Logging {
|
|||||||
|
|
||||||
def withValue(path: String, value: Option[Any]): Config =
|
def withValue(path: String, value: Option[Any]): Config =
|
||||||
config.withValue(path, ConfigValueFactory.fromAnyRef(value.orNull))
|
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) {
|
class Int2D(var x: Int, var y: Int) {
|
||||||
var isSet: Boolean = false
|
var isSet: Boolean = false
|
||||||
|
|
||||||
def this() = this(0, 0)
|
def this() = this(0, 0)
|
||||||
|
|
||||||
def this(list: util.List[Integer]) = {
|
def this(list: util.List[Integer]) = {
|
||||||
this()
|
this()
|
||||||
if (list.size() == 2) {
|
if (list.size() == 2) {
|
||||||
@ -116,41 +138,18 @@ object Settings extends Logging {
|
|||||||
def get: Settings = settings
|
def get: Settings = settings
|
||||||
|
|
||||||
def load(path: Path): Unit = {
|
def load(path: Path): Unit = {
|
||||||
import java.lang.System.{lineSeparator => EOL}
|
|
||||||
|
|
||||||
if (Files.exists(path)) {
|
if (Files.exists(path)) {
|
||||||
var stream: InputStream = null
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
stream = Files.newInputStream(path)
|
settings = new Settings(ConfigFactory.parseFile(path.toFile))
|
||||||
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")
|
logger.info(s"Loaded Ocelot Desktop configuration from: $path")
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
} catch {
|
||||||
catch {
|
case t: Throwable => logger.error(s"Failed to parse $path!", t)
|
||||||
case _: Throwable =>
|
|
||||||
logger.info(s"Failed to parse $path, using default Ocelot Desktop configuration.")
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
if (stream != null)
|
|
||||||
stream.close()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val defaults = {
|
logger.info(s"Using default Ocelot Desktop configuration...")
|
||||||
val in = getClass.getResourceAsStream("/ocelot/desktop/ocelot.conf")
|
settings = new Settings(ConfigFactory.parseResources(OcelotDesktop.getClass, "/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 = {
|
def save(path: Path): Unit = {
|
||||||
@ -172,12 +171,19 @@ object Settings extends Logging {
|
|||||||
.withValuePreserveOrigin("ocelot.window.fullscreen", settings.windowFullscreen)
|
.withValuePreserveOrigin("ocelot.window.fullscreen", settings.windowFullscreen)
|
||||||
.withValuePreserveOrigin("ocelot.window.disableVsync", settings.disableVsync)
|
.withValuePreserveOrigin("ocelot.window.disableVsync", settings.disableVsync)
|
||||||
.withValuePreserveOrigin("ocelot.window.debugLwjgl", settings.debugLwjgl)
|
.withValuePreserveOrigin("ocelot.window.debugLwjgl", settings.debugLwjgl)
|
||||||
|
.withValue("ocelot.keymap", settings.keymap.save())
|
||||||
.withValue("ocelot.workspace.recent", settings.recentWorkspace)
|
.withValue("ocelot.workspace.recent", settings.recentWorkspace)
|
||||||
.withValuePreserveOrigin("ocelot.workspace.pinNewWindows", settings.pinNewWindows)
|
.withValuePreserveOrigin("ocelot.workspace.pinNewWindows", settings.pinNewWindows)
|
||||||
.withValuePreserveOrigin("ocelot.workspace.unfocusedWindowTransparency", settings.unfocusedWindowTransparency)
|
.withValuePreserveOrigin("ocelot.workspace.unfocusedWindowTransparency", settings.unfocusedWindowTransparency)
|
||||||
|
.withValuePreserveOrigin("ocelot.workspace.unfocusedWindowHide", settings.unfocusedWindowHide)
|
||||||
.withValuePreserveOrigin("ocelot.workspace.saveOnExit", settings.saveOnExit)
|
.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.openLastWorkspace", settings.openLastWorkspace)
|
||||||
.withValuePreserveOrigin("ocelot.workspace.renderScreenDataOnNodes", settings.renderScreenDataOnNodes)
|
.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))
|
if (!Files.exists(path.getParent))
|
||||||
Files.createDirectory(path.getParent)
|
Files.createDirectory(path.getParent)
|
||||||
|
|||||||
@ -21,7 +21,7 @@ object AL10W extends Logging {
|
|||||||
val exc = OpenAlException(func, errName, err)
|
val exc = OpenAlException(func, errName, err)
|
||||||
|
|
||||||
if (Settings.get.logAudioErrorStacktrace) {
|
if (Settings.get.logAudioErrorStacktrace) {
|
||||||
logger.error(exc)
|
logger.error(exc.getMessage, exc)
|
||||||
} else {
|
} else {
|
||||||
logger.error(exc.getMessage)
|
logger.error(exc.getMessage)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,9 +15,7 @@ object Audio extends Logging {
|
|||||||
private val sources = new mutable.HashMap[SoundSource, Int]
|
private val sources = new mutable.HashMap[SoundSource, Int]
|
||||||
private var _disabled = true
|
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 = {
|
def init(): Unit = {
|
||||||
try {
|
try {
|
||||||
AL.create()
|
AL.create()
|
||||||
@ -32,14 +30,10 @@ object Audio extends Logging {
|
|||||||
|
|
||||||
def isDisabled: Boolean = _disabled
|
def isDisabled: Boolean = _disabled
|
||||||
|
|
||||||
def numSources: Int = synchronized {
|
|
||||||
sources.size
|
|
||||||
}
|
|
||||||
|
|
||||||
def newStream(
|
def newStream(
|
||||||
soundCategory: SoundCategory.Value,
|
soundCategory: SoundCategory.Value,
|
||||||
pitch: Float = 1f,
|
pitch: Float = 1f,
|
||||||
volume: Float = 1f
|
volume: Float = 1f,
|
||||||
): (SoundStream, SoundSource) = {
|
): (SoundStream, SoundSource) = {
|
||||||
var source: SoundSource = null
|
var source: SoundSource = null
|
||||||
|
|
||||||
@ -83,7 +77,7 @@ object Audio extends Logging {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
source = SoundSource.fromStream(stream, soundCategory, looping = false, pitch, volume)
|
source = SoundSource.fromStream(stream, soundCategory, looping = false, pitch, volume).build()
|
||||||
(stream, source)
|
(stream, source)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +114,7 @@ object Audio extends Logging {
|
|||||||
tx.onFailure { AL10W.alDeleteSources(sourceId) }
|
tx.onFailure { AL10W.alDeleteSources(sourceId) }
|
||||||
|
|
||||||
source.kind match {
|
source.kind match {
|
||||||
case SoundSource.KindSoundBuffer(buffer) =>
|
case SoundSource.Kind.Buffer(buffer) =>
|
||||||
buffer.bufferId match {
|
buffer.bufferId match {
|
||||||
case Some(bufferId) =>
|
case Some(bufferId) =>
|
||||||
AL10W.alSourcei(sourceId, AL10.AL_BUFFER, bufferId)
|
AL10W.alSourcei(sourceId, AL10.AL_BUFFER, bufferId)
|
||||||
@ -130,14 +124,14 @@ object Audio extends Logging {
|
|||||||
tx.abort()
|
tx.abort()
|
||||||
}
|
}
|
||||||
|
|
||||||
case SoundSource.KindSoundSamples(samples) =>
|
case SoundSource.Kind.Samples(samples) =>
|
||||||
Transaction.run { innerTx =>
|
Transaction.run { innerTx =>
|
||||||
val bufferId = samples.genBuffer().getOrElse { tx.abort() }
|
val bufferId = samples.genBuffer().getOrElse { tx.abort() }
|
||||||
innerTx.onFailure { AL10W.alDeleteBuffers(bufferId) }
|
innerTx.onFailure { AL10W.alDeleteBuffers(bufferId) }
|
||||||
AL10W.alSourceQueueBuffers(sourceId, bufferId)
|
AL10W.alSourceQueueBuffers(sourceId, bufferId)
|
||||||
}
|
}
|
||||||
|
|
||||||
case SoundSource.KindStream(_) =>
|
case SoundSource.Kind.Stream(_) =>
|
||||||
}
|
}
|
||||||
|
|
||||||
AL10W.alSourcef(sourceId, AL10.AL_PITCH, source.pitch)
|
AL10W.alSourcef(sourceId, AL10.AL_PITCH, source.pitch)
|
||||||
@ -188,7 +182,7 @@ object Audio extends Logging {
|
|||||||
AL10W.alSourcef(
|
AL10W.alSourcef(
|
||||||
sourceId,
|
sourceId,
|
||||||
AL10.AL_GAIN,
|
AL10.AL_GAIN,
|
||||||
source.volume * SoundCategory.getSettingsValue(source.soundCategory) * Settings.get.volumeMaster
|
source.volume * SoundCategory.getSettingsValue(source.soundCategory) * Settings.get.volumeMaster,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,4 +242,12 @@ object Audio extends Logging {
|
|||||||
AL10W.alDeleteBuffers(buf.get(i))
|
AL10W.alDeleteBuffers(buf.get(i))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def removeAllSources(): Unit = synchronized {
|
||||||
|
for (sourceId <- sources.values) {
|
||||||
|
deleteSource(sourceId)
|
||||||
|
}
|
||||||
|
|
||||||
|
sources.clear()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,7 @@ package ocelot.desktop.audio
|
|||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
object BeepGenerator {
|
object BeepGenerator {
|
||||||
def newBeep(pattern: String, frequency: Short, duration: Short): SoundSource = {
|
def newBeep(pattern: String, frequency: Short, duration: Short): SoundSource.Factory = {
|
||||||
val sampleCounts = pattern.toCharArray
|
val sampleCounts = pattern.toCharArray
|
||||||
.map(ch => if (ch == '.') duration else 2 * duration)
|
.map(ch => if (ch == '.') duration else 2 * duration)
|
||||||
.map(_ * Audio.sampleRate / 1000)
|
.map(_ * Audio.sampleRate / 1000)
|
||||||
@ -19,8 +19,8 @@ object BeepGenerator {
|
|||||||
val value = (math.signum(math.sin(angle)) * 8192).toShort
|
val value = (math.signum(math.sin(angle)) * 8192).toShort
|
||||||
offset += step
|
offset += step
|
||||||
if (offset > 1) offset -= 1
|
if (offset > 1) offset -= 1
|
||||||
data.put((value & 0xFF).toByte)
|
data.put((value & 0xff).toByte)
|
||||||
data.put(((value >> 8) & 0xFF).toByte)
|
data.put(((value >> 8) & 0xff).toByte)
|
||||||
}
|
}
|
||||||
if (data.hasRemaining) {
|
if (data.hasRemaining) {
|
||||||
for (_ <- 0 until pauseSampleCount) {
|
for (_ <- 0 until pauseSampleCount) {
|
||||||
|
|||||||
@ -0,0 +1,6 @@
|
|||||||
|
package ocelot.desktop.audio
|
||||||
|
|
||||||
|
trait ClickSoundSourceFactory {
|
||||||
|
def press: SoundSource.Factory
|
||||||
|
def release: SoundSource.Factory
|
||||||
|
}
|
||||||
@ -8,7 +8,8 @@ import java.nio.ByteBuffer
|
|||||||
|
|
||||||
object OggDecoder {
|
object OggDecoder {
|
||||||
def decode(input: InputStream): SoundSamples = {
|
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 rate = stream.getIdentificationHeader.getSampleRate
|
||||||
val channels = stream.getIdentificationHeader.getChannels
|
val channels = stream.getIdentificationHeader.getChannels
|
||||||
|
|||||||
@ -3,12 +3,10 @@ package ocelot.desktop.audio
|
|||||||
import scala.util.control
|
import scala.util.control
|
||||||
import scala.util.control.Exception.Catch
|
import scala.util.control.Exception.Catch
|
||||||
|
|
||||||
case class OpenAlException(func: String, errName: String, code: Int)
|
case class OpenAlException(func: String, errName: String, code: Int) extends Exception(s"OpenAL error: $func: $errName")
|
||||||
extends Exception(s"OpenAL error: $func: $errName")
|
|
||||||
|
|
||||||
object OpenAlException {
|
object OpenAlException {
|
||||||
def defaulting[T](default: => T): Catch[T] = control.Exception.failAsValue(classOf[OpenAlException])(default)
|
def defaulting[T](default: => T): Catch[T] = control.Exception.failAsValue(classOf[OpenAlException])(default)
|
||||||
|
|
||||||
def ignoring: Catch[Unit] = defaulting(())
|
def ignoring: Catch[Unit] = defaulting(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import scala.collection.mutable.ArrayBuffer
|
|||||||
|
|
||||||
object SoundBuffers extends Resource {
|
object SoundBuffers extends Resource {
|
||||||
lazy val MachineComputerRunning: SoundBuffer = load("/ocelot/desktop/sounds/machine/computer_running.ogg")
|
lazy val MachineComputerRunning: SoundBuffer = load("/ocelot/desktop/sounds/machine/computer_running.ogg")
|
||||||
|
|
||||||
lazy val MachineFloppyAccess: Array[SoundBuffer] = Array(
|
lazy val MachineFloppyAccess: Array[SoundBuffer] = Array(
|
||||||
load("/ocelot/desktop/sounds/machine/floppy_access1.ogg"),
|
load("/ocelot/desktop/sounds/machine/floppy_access1.ogg"),
|
||||||
load("/ocelot/desktop/sounds/machine/floppy_access2.ogg"),
|
load("/ocelot/desktop/sounds/machine/floppy_access2.ogg"),
|
||||||
@ -14,8 +15,10 @@ object SoundBuffers extends Resource {
|
|||||||
load("/ocelot/desktop/sounds/machine/floppy_access5.ogg"),
|
load("/ocelot/desktop/sounds/machine/floppy_access5.ogg"),
|
||||||
load("/ocelot/desktop/sounds/machine/floppy_access6.ogg"),
|
load("/ocelot/desktop/sounds/machine/floppy_access6.ogg"),
|
||||||
)
|
)
|
||||||
|
|
||||||
lazy val MachineFloppyEject: SoundBuffer = load("/ocelot/desktop/sounds/machine/floppy_eject.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 MachineFloppyInsert: SoundBuffer = load("/ocelot/desktop/sounds/machine/floppy_insert.ogg")
|
||||||
|
|
||||||
lazy val MachineHDDAccess: Array[SoundBuffer] = Array(
|
lazy val MachineHDDAccess: Array[SoundBuffer] = Array(
|
||||||
load("/ocelot/desktop/sounds/machine/hdd_access1.ogg"),
|
load("/ocelot/desktop/sounds/machine/hdd_access1.ogg"),
|
||||||
load("/ocelot/desktop/sounds/machine/hdd_access2.ogg"),
|
load("/ocelot/desktop/sounds/machine/hdd_access2.ogg"),
|
||||||
@ -25,20 +28,27 @@ object SoundBuffers extends Resource {
|
|||||||
load("/ocelot/desktop/sounds/machine/hdd_access6.ogg"),
|
load("/ocelot/desktop/sounds/machine/hdd_access6.ogg"),
|
||||||
)
|
)
|
||||||
|
|
||||||
lazy val MachineTapeButton: SoundBuffer = load("/ocelot/desktop/sounds/machine/tape_button.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 MachineTapeEject: SoundBuffer = load("/ocelot/desktop/sounds/machine/tape_eject.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 MachineTapeInsert: SoundBuffer = load("/ocelot/desktop/sounds/machine/tape_insert.ogg")
|
||||||
lazy val MachineTapeRewind: SoundBuffer = load("/ocelot/desktop/sounds/machine/tape_rewind.ogg")
|
lazy val MachineTapeRewind: SoundBuffer = load("/ocelot/desktop/sounds/machine/tape_rewind.ogg")
|
||||||
|
|
||||||
lazy val InterfaceClick: SoundBuffer = load("/ocelot/desktop/sounds/interface/click.ogg")
|
lazy val InterfaceClickPress: SoundBuffer = load("/ocelot/desktop/sounds/interface/click_press.ogg")
|
||||||
lazy val InterfaceTick: SoundBuffer = load("/ocelot/desktop/sounds/interface/tick.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 MinecraftClick: SoundBuffer = load("/ocelot/desktop/sounds/minecraft/click.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 MinecraftExplosion: SoundBuffer = load("/ocelot/desktop/sounds/minecraft/explosion.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(
|
lazy val NoteBlock: Map[String, SoundBuffer] = List(
|
||||||
"banjo", "basedrum", "bass", "bell", "bit", "chime", "cow_bell", "didgeridoo", "flute", "guitar",
|
"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 => {
|
).map(name => {
|
||||||
(name, load(s"/ocelot/desktop/sounds/minecraft/note_block/$name.ogg"))
|
(name, load(s"/ocelot/desktop/sounds/minecraft/note_block/$name.ogg"))
|
||||||
}).toMap
|
}).toMap
|
||||||
|
|||||||
@ -11,13 +11,13 @@ class SoundSource(
|
|||||||
val looping: Boolean,
|
val looping: Boolean,
|
||||||
val pitch: Float,
|
val pitch: Float,
|
||||||
var volume: Float,
|
var volume: Float,
|
||||||
var position: Vector3D = Vector3D(0, 0, 0)
|
var position: Vector3D = Vector3D(0, 0, 0),
|
||||||
) {
|
) {
|
||||||
def duration: Option[Duration] = kind match {
|
def duration: Option[Duration] = kind match {
|
||||||
case SoundSource.KindSoundBuffer(buffer) =>
|
case SoundSource.Kind.Buffer(buffer) =>
|
||||||
Some(Duration(buffer.numSamples.toFloat / buffer.sampleRate, TimeUnit.SECONDS))
|
Some(Duration(buffer.numSamples.toFloat / buffer.sampleRate, TimeUnit.SECONDS))
|
||||||
|
|
||||||
case SoundSource.KindSoundSamples(SoundSamples(buffer, rate, format)) =>
|
case SoundSource.Kind.Samples(SoundSamples(buffer, rate, format)) =>
|
||||||
val bps = format match {
|
val bps = format match {
|
||||||
case SoundSamples.Format.Stereo16 => 2
|
case SoundSamples.Format.Stereo16 => 2
|
||||||
case SoundSamples.Format.Mono8 => 1
|
case SoundSamples.Format.Mono8 => 1
|
||||||
@ -26,7 +26,7 @@ class SoundSource(
|
|||||||
|
|
||||||
Some(Duration(buffer.limit().toFloat / (rate * bps), TimeUnit.SECONDS))
|
Some(Duration(buffer.limit().toFloat / (rate * bps), TimeUnit.SECONDS))
|
||||||
|
|
||||||
case SoundSource.KindStream(_) =>
|
case SoundSource.Kind.Stream(_) =>
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,15 +34,15 @@ class SoundSource(
|
|||||||
Audio.getSourceStatus(this)
|
Audio.getSourceStatus(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
def isPlaying: Boolean = {
|
def playing: Boolean = {
|
||||||
status == SoundSource.Status.Playing
|
status == SoundSource.Status.Playing
|
||||||
}
|
}
|
||||||
|
|
||||||
def isPaused: Boolean = {
|
def paused: Boolean = {
|
||||||
status == SoundSource.Status.Paused
|
status == SoundSource.Status.Paused
|
||||||
}
|
}
|
||||||
|
|
||||||
def isStopped: Boolean = {
|
def stopped: Boolean = {
|
||||||
status == SoundSource.Status.Stopped
|
status == SoundSource.Status.Stopped
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,26 +62,52 @@ class SoundSource(
|
|||||||
object SoundSource {
|
object SoundSource {
|
||||||
sealed trait Kind
|
sealed trait Kind
|
||||||
|
|
||||||
case class KindSoundBuffer(buffer: SoundBuffer) extends Kind
|
object Kind {
|
||||||
|
case class Buffer(buffer: SoundBuffer) extends Kind
|
||||||
case class KindSoundSamples(samples: SoundSamples) extends Kind
|
case class Samples(samples: SoundSamples) extends Kind
|
||||||
|
case class Stream(stream: SoundStream) 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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def fromSamples(samples: SoundSamples, soundCategory: SoundCategory.Value,
|
class Factory(
|
||||||
looping: Boolean = false, pitch: Float = 1f, volume: Float = 1f): SoundSource = {
|
val kind: Kind,
|
||||||
new SoundSource(SoundSource.KindSoundSamples(samples), soundCategory, looping, pitch, volume)
|
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 fromStream(stream: SoundStream, soundCategory: SoundCategory.Value,
|
def fromBuffer(
|
||||||
looping: Boolean = false, pitch: Float = 1f, volume: Float = 1f): SoundSource = {
|
buffer: SoundBuffer,
|
||||||
new SoundSource(SoundSource.KindStream(stream), soundCategory, looping, pitch, volume)
|
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)
|
||||||
|
|
||||||
object Status extends Enumeration {
|
object Status extends Enumeration {
|
||||||
val Playing, Paused, Stopped = Value
|
val Playing, Paused, Stopped = Value
|
||||||
@ -89,13 +115,40 @@ object SoundSource {
|
|||||||
|
|
||||||
// ----------------------------------------------------------------
|
// ----------------------------------------------------------------
|
||||||
|
|
||||||
lazy val InterfaceClick: SoundSource = SoundSource.fromBuffer(SoundBuffers.InterfaceClick, SoundCategory.Interface)
|
object InterfaceClick extends ClickSoundSourceFactory {
|
||||||
lazy val InterfaceClickLow: SoundSource = SoundSource.fromBuffer(SoundBuffers.InterfaceClick, SoundCategory.Interface, pitch = 0.8f)
|
override lazy val press: Factory = SoundSource.fromBuffer(SoundBuffers.InterfaceClickPress, SoundCategory.Interface)
|
||||||
lazy val InterfaceTick: SoundSource = SoundSource.fromBuffer(SoundBuffers.InterfaceTick, SoundCategory.Interface)
|
|
||||||
|
|
||||||
lazy val MinecraftClick: SoundSource = SoundSource.fromBuffer(SoundBuffers.MinecraftClick, SoundCategory.Interface)
|
override lazy val release: Factory =
|
||||||
lazy val MinecraftExplosion: SoundSource = SoundSource.fromBuffer(SoundBuffers.MinecraftExplosion, SoundCategory.Environment)
|
SoundSource.fromBuffer(SoundBuffers.InterfaceClickRelease, SoundCategory.Interface)
|
||||||
|
}
|
||||||
lazy val MachineFloppyInsert: SoundSource = SoundSource.fromBuffer(SoundBuffers.MachineFloppyInsert, SoundCategory.Environment)
|
|
||||||
lazy val MachineFloppyEject: SoundSource = SoundSource.fromBuffer(SoundBuffers.MachineFloppyEject, 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)
|
||||||
}
|
}
|
||||||
@ -6,8 +6,8 @@ case class IntColor(color: Int) extends Color {
|
|||||||
override def toRGBA: RGBAColor = {
|
override def toRGBA: RGBAColor = {
|
||||||
RGBAColor(
|
RGBAColor(
|
||||||
(color >> 16).toShort,
|
(color >> 16).toShort,
|
||||||
((color >> 8) & 0xFF).toShort,
|
((color >> 8) & 0xff).toShort,
|
||||||
(color & 0xFF).toShort,
|
(color & 0xff).toShort,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -14,7 +14,7 @@ case class RGBAColor(r: Short, g: Short, b: Short, a: Short = 255) extends Color
|
|||||||
r.toFloat / 255f,
|
r.toFloat / 255f,
|
||||||
g.toFloat / 255f,
|
g.toFloat / 255f,
|
||||||
b.toFloat / 255f,
|
b.toFloat / 255f,
|
||||||
a.toFloat / 255f
|
a.toFloat / 255f,
|
||||||
)
|
)
|
||||||
|
|
||||||
override def toHSVA: HSVAColor = toRGBANorm.toHSVA
|
override def toHSVA: HSVAColor = toRGBANorm.toHSVA
|
||||||
|
|||||||
@ -1,22 +1,25 @@
|
|||||||
package ocelot.desktop.color
|
package ocelot.desktop.color
|
||||||
|
|
||||||
|
import ocelot.desktop.util.NumberUtils.ExtendedFloat
|
||||||
import ocelot.desktop.geometry.Vector3D
|
import ocelot.desktop.geometry.Vector3D
|
||||||
|
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
|
|
||||||
case class RGBAColorNorm(r: Float, g: Float, b: Float, a: Float = 1f) extends Color {
|
case class RGBAColorNorm(r: Float, g: Float, b: Float, a: Float = 1f) extends Color {
|
||||||
assert(0 <= r && r <= 1.0f, "Invalid RED channel")
|
require(0 <= r && r <= 1.0f, "Invalid RED channel")
|
||||||
assert(0 <= g && g <= 1.0f, "Invalid GREEN channel")
|
require(0 <= g && g <= 1.0f, "Invalid GREEN channel")
|
||||||
assert(0 <= b && b <= 1.0f, "Invalid BLUE channel")
|
require(0 <= b && b <= 1.0f, "Invalid BLUE channel")
|
||||||
assert(0 <= a && a <= 1.0f, "Invalid ALPHA channel")
|
require(0 <= a && a <= 1.0f, "Invalid ALPHA channel")
|
||||||
|
|
||||||
def components: Array[Float] = Array(r, g, b, a)
|
def components: Array[Float] = Array(r, g, b, a)
|
||||||
|
|
||||||
def rgbVector: Vector3D = Vector3D(r, g, b)
|
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))
|
def mapA(f: Float => Float): RGBAColorNorm = copy(a = f(a))
|
||||||
|
|
||||||
final private def componentToLinear(x: Float): Float = {
|
private final def componentToLinear(x: Float): Float = {
|
||||||
if (x <= 0.0404482362771082)
|
if (x <= 0.0404482362771082)
|
||||||
x / 12.92f
|
x / 12.92f
|
||||||
else
|
else
|
||||||
@ -58,6 +61,15 @@ case class RGBAColorNorm(r: Float, g: Float, b: Float, a: Float = 1f) extends Co
|
|||||||
HSVAColor(hue, saturation, value, a)
|
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)
|
def withAlpha(alpha: Float): RGBAColorNorm = RGBAColorNorm(r, g, b, alpha)
|
||||||
|
|
||||||
// ʕ•ᴥ•ʔ
|
// ʕ•ᴥ•ʔ
|
||||||
|
|||||||
@ -43,7 +43,7 @@ class Camera extends Entity with GenericCamera with DeviceInfo {
|
|||||||
DeviceAttribute.Class -> DeviceClass.Multimedia,
|
DeviceAttribute.Class -> DeviceClass.Multimedia,
|
||||||
DeviceAttribute.Description -> "Dungeon Scanner 2.5D",
|
DeviceAttribute.Description -> "Dungeon Scanner 2.5D",
|
||||||
DeviceAttribute.Vendor -> Constants.DeviceInfo.DefaultVendor,
|
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 = {
|
override def load(nbt: NBTTagCompound, workspace: Workspace): Unit = {
|
||||||
|
|||||||
22
src/main/scala/ocelot/desktop/entity/OcelotBlock.scala
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
@ -1,18 +1,14 @@
|
|||||||
package ocelot.desktop.entity
|
package ocelot.desktop.entity
|
||||||
|
|
||||||
import ocelot.desktop.entity.OcelotCard.{LogDirection, LogEvent}
|
import ocelot.desktop.entity.traits.OcelotInterface
|
||||||
import totoro.ocelot.brain.Constants
|
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.{DeviceAttribute, DeviceClass}
|
||||||
import totoro.ocelot.brain.entity.traits.{DeviceInfo, Entity, Environment, Tiered, result}
|
import totoro.ocelot.brain.entity.traits.{DeviceInfo, Tiered}
|
||||||
import totoro.ocelot.brain.event.{EventBus, NodeEvent}
|
|
||||||
import totoro.ocelot.brain.network.{Network, Node, Visibility}
|
import totoro.ocelot.brain.network.{Network, Node, Visibility}
|
||||||
import totoro.ocelot.brain.util.Tier
|
import totoro.ocelot.brain.util.Tier
|
||||||
import totoro.ocelot.brain.util.Tier.Tier
|
import totoro.ocelot.brain.util.Tier.Tier
|
||||||
|
|
||||||
import java.time.Instant
|
class OcelotCard extends OcelotInterface with DeviceInfo with Tiered {
|
||||||
|
|
||||||
class OcelotCard extends Entity with Environment with DeviceInfo with Tiered {
|
|
||||||
override val node: Node = Network.newNode(this, Visibility.Neighbors)
|
override val node: Node = Network.newNode(this, Visibility.Neighbors)
|
||||||
.withComponent("ocelot", Visibility.Neighbors)
|
.withComponent("ocelot", Visibility.Neighbors)
|
||||||
.create()
|
.create()
|
||||||
@ -27,47 +23,4 @@ class OcelotCard extends Entity with Environment with DeviceInfo with Tiered {
|
|||||||
)
|
)
|
||||||
|
|
||||||
override def getDeviceInfo: Map[String, String] = deviceInfo
|
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,11 +19,12 @@ import javax.sound.sampled.AudioFormat.Encoding
|
|||||||
import javax.sound.sampled.{AudioFormat, AudioSystem}
|
import javax.sound.sampled.{AudioFormat, AudioSystem}
|
||||||
|
|
||||||
class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
|
class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
|
||||||
override val node: Component =
|
override val node: Component = {
|
||||||
Network
|
Network
|
||||||
.newNode(this, Visibility.Network)
|
.newNode(this, Visibility.Network)
|
||||||
.withComponent("openfm_radio", Visibility.Network)
|
.withComponent("openfm_radio", Visibility.Network)
|
||||||
.create()
|
.create()
|
||||||
|
}
|
||||||
|
|
||||||
// --------------------------- URL ---------------------------
|
// --------------------------- URL ---------------------------
|
||||||
|
|
||||||
@ -44,14 +45,18 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
|
|||||||
private def playSynchronously(): Unit = {
|
private def playSynchronously(): Unit = {
|
||||||
try {
|
try {
|
||||||
// Trying to parse URL and sending request to host
|
// Trying to parse URL and sending request to host
|
||||||
val connection =
|
val connection = {
|
||||||
new URI(url.get)
|
new URI(url.get)
|
||||||
.toURL
|
.toURL
|
||||||
.openConnection
|
.openConnection
|
||||||
.asInstanceOf[HttpURLConnection]
|
.asInstanceOf[HttpURLConnection]
|
||||||
|
}
|
||||||
|
|
||||||
connection.setRequestMethod("GET")
|
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.setRequestProperty("Content-Language", "en-US")
|
||||||
connection.setDoInput(true)
|
connection.setDoInput(true)
|
||||||
connection.setDoOutput(true)
|
connection.setDoOutput(true)
|
||||||
@ -65,21 +70,22 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
|
|||||||
2,
|
2,
|
||||||
4,
|
4,
|
||||||
44100,
|
44100,
|
||||||
false
|
false,
|
||||||
),
|
),
|
||||||
// Obtaining audio input stream from HTTP connection
|
// 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
|
// Keeping input stream format parameters here to offload the reading loop
|
||||||
val inputStreamFormat = inputStream.getFormat
|
val inputStreamFormat = inputStream.getFormat
|
||||||
val inputStreamSampleRate = inputStreamFormat.getSampleRate.toInt
|
val inputStreamSampleRate = inputStreamFormat.getSampleRate.toInt
|
||||||
|
|
||||||
val inputStreamSoundSampleFormat =
|
val inputStreamSoundSampleFormat = {
|
||||||
if (inputStreamFormat.getChannels > 1)
|
if (inputStreamFormat.getChannels > 1)
|
||||||
Format.Stereo16
|
Format.Stereo16
|
||||||
else
|
else
|
||||||
Format.Mono16
|
Format.Mono16
|
||||||
|
}
|
||||||
|
|
||||||
// Creating Ocelot output sound stream
|
// Creating Ocelot output sound stream
|
||||||
val (outputStream, outputSource) = Audio.newStream(SoundCategory.Records, volume = volume)
|
val (outputStream, outputSource) = Audio.newStream(SoundCategory.Records, volume = volume)
|
||||||
@ -99,7 +105,7 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
|
|||||||
.flip
|
.flip
|
||||||
.asInstanceOf[ByteBuffer],
|
.asInstanceOf[ByteBuffer],
|
||||||
inputStreamSampleRate,
|
inputStreamSampleRate,
|
||||||
inputStreamSoundSampleFormat
|
inputStreamSoundSampleFormat,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,8 +113,7 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.info("OpenFM input audio stream has reached EOF, closing thread")
|
logger.info("OpenFM input audio stream has reached EOF, closing thread")
|
||||||
}
|
} catch {
|
||||||
catch {
|
|
||||||
case _: InterruptedException =>
|
case _: InterruptedException =>
|
||||||
case e: Exception => logger.error("OpenFM playback exception", e)
|
case e: Exception => logger.error("OpenFM playback exception", e)
|
||||||
}
|
}
|
||||||
@ -147,7 +152,7 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def isPlaying: Boolean =
|
def isPlaying: Boolean =
|
||||||
playbackSoundSource.isDefined && playbackSoundSource.get.isPlaying || playbackThread.isDefined
|
playbackSoundSource.isDefined && playbackSoundSource.get.playing || playbackThread.isDefined
|
||||||
|
|
||||||
@Callback()
|
@Callback()
|
||||||
def start(context: Context, args: Arguments): Array[AnyRef] =
|
def start(context: Context, args: Arguments): Array[AnyRef] =
|
||||||
@ -168,6 +173,7 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
|
|||||||
|
|
||||||
private var _volume: Float = 1
|
private var _volume: Float = 1
|
||||||
def volume: Float = _volume
|
def volume: Float = _volume
|
||||||
|
|
||||||
def volume_=(value: Float): Unit = {
|
def volume_=(value: Float): Unit = {
|
||||||
_volume = value
|
_volume = value
|
||||||
|
|
||||||
@ -211,7 +217,7 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
|
|||||||
// --------------------------- Screen color/text ---------------------------
|
// --------------------------- Screen color/text ---------------------------
|
||||||
|
|
||||||
private val defaultScreenText = "OpenFM"
|
private val defaultScreenText = "OpenFM"
|
||||||
private val defaultScreenColor = IntColor(0x0AFF0A)
|
private val defaultScreenColor = IntColor(0x0aff0a)
|
||||||
|
|
||||||
var screenColor: IntColor = defaultScreenColor
|
var screenColor: IntColor = defaultScreenColor
|
||||||
private var _screenText: String = defaultScreenText
|
private var _screenText: String = defaultScreenText
|
||||||
@ -272,23 +278,26 @@ class OpenFMRadio extends Entity with Environment with DeviceInfo with Logging {
|
|||||||
if (nbt.hasKey("url"))
|
if (nbt.hasKey("url"))
|
||||||
url = Option(nbt.getString("url"))
|
url = Option(nbt.getString("url"))
|
||||||
|
|
||||||
screenColor =
|
screenColor = {
|
||||||
if (nbt.hasKey("screenColor"))
|
if (nbt.hasKey("screenColor"))
|
||||||
IntColor(nbt.getInteger("screenColor"))
|
IntColor(nbt.getInteger("screenColor"))
|
||||||
else
|
else
|
||||||
defaultScreenColor
|
defaultScreenColor
|
||||||
|
}
|
||||||
|
|
||||||
screenText =
|
screenText = {
|
||||||
if (nbt.hasKey("screenText"))
|
if (nbt.hasKey("screenText"))
|
||||||
nbt.getString("screenText")
|
nbt.getString("screenText")
|
||||||
else
|
else
|
||||||
defaultScreenText
|
defaultScreenText
|
||||||
|
}
|
||||||
|
|
||||||
volume =
|
volume = {
|
||||||
if (nbt.hasKey("volume"))
|
if (nbt.hasKey("volume"))
|
||||||
nbt.getDouble("volume").toFloat
|
nbt.getDouble("volume").toFloat
|
||||||
else
|
else
|
||||||
1
|
1
|
||||||
|
}
|
||||||
|
|
||||||
isListenRedstone = nbt.hasKey("isListenRedstone") && nbt.getBoolean("isListenRedstone")
|
isListenRedstone = nbt.hasKey("isListenRedstone") && nbt.getBoolean("isListenRedstone")
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,59 @@
|
|||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -27,25 +27,25 @@ case class Basis3D(x: Vector3D, y: Vector3D, z: Vector3D) {
|
|||||||
def *(rhs: Vector3D): Vector3D = Vector3D(
|
def *(rhs: Vector3D): Vector3D = Vector3D(
|
||||||
x.x * rhs.x + y.x * rhs.y + z.x * rhs.z,
|
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.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(
|
def *(rhs: Basis3D): Basis3D = Basis3D(
|
||||||
Vector3D(
|
Vector3D(
|
||||||
x.x * rhs.x.x + y.x * rhs.x.y + z.x * rhs.x.z,
|
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.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(
|
Vector3D(
|
||||||
x.x * rhs.y.x + y.x * rhs.y.y + z.x * rhs.y.z,
|
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.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(
|
Vector3D(
|
||||||
x.x * rhs.z.x + y.x * rhs.z.y + z.x * rhs.z.z,
|
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.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) -
|
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(
|
Vector3D(
|
||||||
(y.y * z.z - z.y * y.z) * s,
|
(y.y * z.z - z.y * y.z) * s,
|
||||||
(x.z * z.y - x.y * z.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(
|
Vector3D(
|
||||||
(y.z * z.x - y.x * z.z) * s,
|
(y.z * z.x - y.x * z.z) * s,
|
||||||
(x.x * z.z - x.z * z.x) * 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(
|
Vector3D(
|
||||||
(y.x * z.y - z.x * y.y) * s,
|
(y.x * z.y - z.x * y.y) * s,
|
||||||
(z.x * x.y - x.x * z.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,
|
||||||
)
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,9 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -3,11 +3,12 @@ package ocelot.desktop.geometry
|
|||||||
object ProjectionMatrix3D {
|
object ProjectionMatrix3D {
|
||||||
def perspective(aspect: Float, fovY: Float, zNear: Float, zFar: Float): ProjectionMatrix3D = {
|
def perspective(aspect: Float, fovY: Float, zNear: Float, zFar: Float): ProjectionMatrix3D = {
|
||||||
val f = (1.0 / math.tan(math.toRadians(fovY) / 2.0)).toFloat
|
val f = (1.0 / math.tan(math.toRadians(fovY) / 2.0)).toFloat
|
||||||
|
// format: off
|
||||||
ProjectionMatrix3D(
|
ProjectionMatrix3D(
|
||||||
f / aspect, 0, 0, 0,
|
f / aspect, 0, 0, 0,
|
||||||
0, f, 0, 0,
|
0, f, 0, 0,
|
||||||
0, 0, (zFar + zNear) / (zNear - zFar), (2 * zFar * zNear) / (zNear - zFar),
|
0, 0, (zFar + zNear) / (zNear - zFar), (2 * zFar * zNear) / (zNear - zFar),
|
||||||
0, 0, -1, 0
|
0, 0, -1, 0,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -15,7 +16,6 @@ object ProjectionMatrix3D {
|
|||||||
case class ProjectionMatrix3D(m11: Float, m12: Float, m13: Float, m14: Float,
|
case class ProjectionMatrix3D(m11: Float, m12: Float, m13: Float, m14: Float,
|
||||||
m21: Float, m22: Float, m23: Float, m24: Float,
|
m21: Float, m22: Float, m23: Float, m24: Float,
|
||||||
m31: Float, m32: Float, m33: Float, m34: 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)
|
def array: Array[Float] = Array(m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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,
|
y * rhs.z - z * rhs.y + x * rhs.w + w * rhs.x,
|
||||||
z * rhs.x - x * rhs.z + y * rhs.w + w * rhs.y,
|
z * rhs.x - x * rhs.z + y * rhs.w + w * rhs.y,
|
||||||
x * rhs.y - y * rhs.x + z * rhs.w + w * rhs.z,
|
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)
|
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(
|
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(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 * 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
|
def dot(that: Quaternion): Float = x * that.x + y * that.y + z * that.z + w * that.w
|
||||||
|
|||||||
@ -76,7 +76,9 @@ case class Rect2D(x: Float, y: Float, w: Float, h: Float) {
|
|||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
def inflate(addition: Float): Rect2D = Rect2D(x - addition, y - addition, w + addition * 2, h + addition * 2)
|
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 center: Vector2D = {
|
def center: Vector2D = {
|
||||||
min + (size * 0.5f).toVector
|
min + (size * 0.5f).toVector
|
||||||
@ -86,7 +88,8 @@ case class Rect2D(x: Float, y: Float, w: Float, h: Float) {
|
|||||||
Vector2D(x + w / 2f, y),
|
Vector2D(x + w / 2f, y),
|
||||||
Vector2D(x + w, y + h / 2f),
|
Vector2D(x + w, y + h / 2f),
|
||||||
Vector2D(x + w / 2f, y + h),
|
Vector2D(x + w / 2f, y + h),
|
||||||
Vector2D(x, y + h / 2f))
|
Vector2D(x, y + h / 2f),
|
||||||
|
)
|
||||||
|
|
||||||
def distanceTo(that: Rect2D): Float = {
|
def distanceTo(that: Rect2D): Float = {
|
||||||
((center - that.center).abs - (extent + that.extent)).max(Vector2D(0, 0)).length
|
((center - that.center).abs - (extent + that.extent)).max(Vector2D(0, 0)).length
|
||||||
|
|||||||
@ -32,7 +32,7 @@ case class Size2D(width: Float, height: Float) {
|
|||||||
def clamped(min: Size2D, max: Size2D): Size2D = {
|
def clamped(min: Size2D, max: Size2D): Size2D = {
|
||||||
Size2D(
|
Size2D(
|
||||||
math.min(max.width, math.max(min.width, width)),
|
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)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,27 +5,28 @@ import java.nio.ByteBuffer
|
|||||||
object Transform2D {
|
object Transform2D {
|
||||||
def identity: Transform2D = Transform2D(
|
def identity: Transform2D = Transform2D(
|
||||||
1, 0, 0,
|
1, 0, 0,
|
||||||
0, 1, 0
|
0, 1, 0,
|
||||||
)
|
)
|
||||||
|
|
||||||
def scale(x: Float, y: Float): Transform2D = Transform2D(
|
def scale(x: Float, y: Float): Transform2D = Transform2D(
|
||||||
x, 0, 0,
|
x, 0, 0,
|
||||||
0, y, 0
|
0, y, 0,
|
||||||
)
|
)
|
||||||
|
|
||||||
def scale(a: Float): Transform2D = Transform2D.scale(a, a)
|
def scale(a: Float): Transform2D = Transform2D.scale(a, a)
|
||||||
|
|
||||||
def translate(x: Float, y: Float): Transform2D = Transform2D(
|
def translate(x: Float, y: Float): Transform2D = Transform2D(
|
||||||
1, 0, x,
|
1, 0, x,
|
||||||
0, 1, y
|
0, 1, y,
|
||||||
)
|
)
|
||||||
|
|
||||||
def viewport(width: Float, height: Float): Transform2D =
|
def viewport(width: Float, height: Float): Transform2D =
|
||||||
Transform2D.translate(-1f, 1f) >> Transform2D.scale(2f / width, -2f / height)
|
Transform2D.translate(-1f, 1f) >> Transform2D.scale(2f / width, -2f / height)
|
||||||
|
|
||||||
def rotate(angle: Float): Transform2D = {
|
def rotate(angle: Float): Transform2D = {
|
||||||
val (s, c) = (math.sin(angle).asInstanceOf[Float], math.cos(angle).asInstanceOf[Float])
|
val (s, c) = (math.sin(angle).toFloat, math.cos(angle).toFloat)
|
||||||
|
|
||||||
|
// format: off
|
||||||
Transform2D(
|
Transform2D(
|
||||||
c, -s, 0,
|
c, -s, 0,
|
||||||
s, c, 0
|
s, c, 0
|
||||||
@ -36,13 +37,13 @@ object Transform2D {
|
|||||||
case class Transform2D(m11: Float, m12: Float, m13: Float, m21: Float, m22: Float, m23: Float) {
|
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)
|
def array: Array[Float] = Array(m11, m12, m13, m21, m22, m23)
|
||||||
|
|
||||||
// :|
|
// format: off
|
||||||
def >>(that: Transform2D): Transform2D = Transform2D(
|
def >>(that: Transform2D): Transform2D = Transform2D(
|
||||||
m11 * that.m11 + m12 * that.m21, m11 * that.m12 + m12 * that.m22, m11 * that.m13 + m12 * that.m23 + m13,
|
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,
|
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(
|
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.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
|
m11 * that.m21 + m21 * that.m22, m12 * that.m21 + m22 * that.m22, m13 * that.m21 + m23 * that.m22 + that.m23
|
||||||
@ -50,13 +51,14 @@ case class Transform2D(m11: Float, m12: Float, m13: Float, m21: Float, m22: Floa
|
|||||||
|
|
||||||
def *(that: Vector2D): Vector2D = Vector2D(
|
def *(that: Vector2D): Vector2D = Vector2D(
|
||||||
m11 * that.x + m12 * that.y + m13,
|
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]
|
f"""Transform2D [$m11%6.3f $m12%6.3f $m13%6.3f]
|
||||||
| [$m21%6.3f $m22%6.3f $m23%6.3f]
|
| [$m21%6.3f $m22%6.3f $m23%6.3f]
|
||||||
""".stripMargin
|
""".stripMargin
|
||||||
|
}
|
||||||
|
|
||||||
// (╯°□°)╯︵ ┻━┻
|
// (╯°□°)╯︵ ┻━┻
|
||||||
def put(buffer: ByteBuffer): Unit = {
|
def put(buffer: ByteBuffer): Unit = {
|
||||||
|
|||||||
@ -26,6 +26,7 @@ object Transform3D {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case class Transform3D(basis: Basis3D, origin: Vector3D) {
|
case class Transform3D(basis: Basis3D, origin: Vector3D) {
|
||||||
|
// format: off
|
||||||
def array: Array[Float] = Array(
|
def array: Array[Float] = Array(
|
||||||
basis.x.x, basis.y.x, basis.z.x, origin.x,
|
basis.x.x, basis.y.x, basis.z.x, origin.x,
|
||||||
basis.x.y, basis.y.y, basis.z.y, origin.y,
|
basis.x.y, basis.y.y, basis.z.y, origin.y,
|
||||||
@ -44,8 +45,8 @@ case class Transform3D(basis: Basis3D, origin: Vector3D) {
|
|||||||
override def toString: String = s"Transform3D [${basis.x}, ${basis.y}, ${basis.z}, $origin]"
|
override def toString: String = s"Transform3D [${basis.x}, ${basis.y}, ${basis.z}, $origin]"
|
||||||
|
|
||||||
def put(buffer: ByteBuffer): Unit = {
|
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.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.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.z); buffer.putFloat(basis.y.z); buffer.putFloat(basis.z.z); buffer.putFloat(origin.z)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,9 +36,6 @@ case class Vector2D(x: Float, y: Float) extends Persistable {
|
|||||||
|
|
||||||
def *(scalar: Float): Vector2D = Vector2D(x * scalar, y * scalar)
|
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 /(scalar: Float): Vector2D = Vector2D(x / scalar, y / scalar)
|
||||||
|
|
||||||
def snap(v: Float): Vector2D = Vector2D((x / v).floor * v, (y / v).floor * v)
|
def snap(v: Float): Vector2D = Vector2D((x / v).floor * v, (y / v).floor * v)
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
package ocelot.desktop.geometry
|
package ocelot.desktop.geometry
|
||||||
|
|
||||||
import ocelot.desktop.geometry.FloatUtils.ExtendedFloat
|
import ocelot.desktop.util.NumberUtils.ExtendedFloat
|
||||||
|
|
||||||
object Vector3D {
|
object Vector3D {
|
||||||
val Zero: Vector3D = Vector3D(0, 0, 0)
|
val Zero: Vector3D = Vector3D(0, 0, 0)
|
||||||
@ -41,11 +41,11 @@ case class Vector3D(x: Float, y: Float, z: Float) {
|
|||||||
def cross(that: Vector3D): Vector3D = Vector3D(
|
def cross(that: Vector3D): Vector3D = Vector3D(
|
||||||
y * that.z - z * that.y,
|
y * that.z - z * that.y,
|
||||||
z * that.x - x * that.z,
|
z * that.x - x * that.z,
|
||||||
x * that.y - y * that.x
|
x * that.y - y * that.x,
|
||||||
)
|
)
|
||||||
|
|
||||||
def angle(that: Vector3D): Float = {
|
def angle(that: Vector3D): Float = {
|
||||||
math.acos((dot(that) / length / that.length).clamp(-1, 1)).toFloat
|
math.acos((dot(that) / length / that.length).clamped(-1, 1)).toFloat
|
||||||
}
|
}
|
||||||
|
|
||||||
def lerp(that: Vector3D, alpha: Float): Vector3D = {
|
def lerp(that: Vector3D, alpha: Float): Vector3D = {
|
||||||
|
|||||||