Compare commits

...

599 Commits

Author SHA1 Message Date
Ryan Butler
e349440ce0 Fixed NPE in tracker ip addr (#269) 2022-11-14 18:02:04 +03:00
Erimel
d8a7fa978b Add third party notices with releases (#262) 2022-11-12 18:33:18 +03:00
Uriel
d9988cc658 Refactor code to Java 17 (#244) 2022-11-12 03:14:08 +03:00
lucas lelievre
f38360e4f1 Do not send position or rotation if not available (#265) 2022-11-09 20:18:46 +03:00
lucas lelievre
b149b87fa7 Pub sub implementation (#263) 2022-11-07 23:34:52 +03:00
Butterscotch!
3d2aa988a0 Better AutoBone body proportion error (#258) 2022-10-27 16:00:04 +03:00
lucas lelievre
f3d32da4cd Bring back device ip (#256) 2022-10-20 18:10:31 +03:00
Erimel
d5059ed48c Better filtering (#241) 2022-10-13 16:17:32 +03:00
unlogisch04
8b1e4cb21a Adding more Serial Commands to the Server (#245) 2022-10-05 19:36:08 +03:00
Erimel
6242afda42 fix crashing when converting zoom (#242) 2022-09-17 00:09:53 +03:00
Erimel
4f44e6c7c7 fix arms from controllers (#240) 2022-09-13 03:14:29 +03:00
Butterscotch!
6fec8c6d07 Autobone cleanup, logging improvements, and restoring functionality (#237) 2022-09-13 03:14:02 +03:00
Erimel
1348f9f5ef fix trackers not loading none/null (#239) 2022-09-13 03:13:45 +03:00
Eiren Rain
1b5a4bf369 Remove Slime Java Commons submodule, (#236) 2022-09-13 03:13:06 +03:00
Collin Kees
426f914f4d Leg tweaks (#223) 2022-08-30 19:50:15 +03:00
Eiren Rain
caa11f9dfc Bump version to v0.2.1 2022-08-24 23:04:09 +02:00
Butterscotch!
d1ed22d3d3 Add a recurring AutoBone recording for user debug info (#235) 2022-08-24 23:54:03 +03:00
Kitlith
67ea5edda6 Add "fast reset" user action to protobuf bridge. (#232) 2022-08-17 18:16:03 +03:00
lucas lelievre
8c6d5e2369 Fix NPE on config converter. (#234) 2022-08-16 22:38:00 +03:00
lucas lelievre
c5e05d2940 Fix Tracker config not being saved properly (#231) 2022-08-11 21:31:58 +03:00
lucas lelievre
8adf3fe591 Implement overlay toggle (#230) 2022-08-09 21:49:07 +03:00
Loler920a
101c403138 Make elbows track from HMD by default (#228) 2022-08-09 21:32:43 +03:00
lucas lelievre
5d1dbb27e1 Config refactor round 2 (#229) 2022-08-09 21:27:28 +03:00
Ryan Butler
390d6f3b51 Model settings (#226) 2022-08-04 20:34:33 +03:00
Erimel
29121913fd Add Skeleton settings to config and change some FK averaging logic. (#215) 2022-08-04 20:02:58 +03:00
lucas lelievre
8d90663799 Add set tracker name in the rpc (#222) 2022-07-30 00:38:15 +03:00
lucas lelievre
bc3cc4ce14 Send manufacturer through the protocol (#221) 2022-07-30 00:37:42 +03:00
lucas lelievre
b5159c9776 Catch flatbuffers errors (#218) 2022-07-30 00:37:11 +03:00
Erimel
9ee09319d7 Skeleton arms rework and cleaning up (#214) 2022-07-15 02:24:21 +03:00
lucas lelievre
7818582f3f Implement Tracker names (#210) 2022-07-15 02:06:22 +03:00
Erimel
0b7f79340b Set disconnected TrackerStatus (#211) 2022-07-04 16:35:08 +03:00
lucas lelievre
c22b5ce421 Implement devices in the protocol (#205) 2022-07-04 00:57:21 +03:00
Erimel
078b140966 Rename hmd bodypart to head (#209) 2022-07-04 00:54:29 +03:00
Erimel
c3e4e86c4e Fixing skeleton stuff (#206) 2022-07-04 00:53:29 +03:00
Erimel
d0b1a9c822 remove waist_tracker, only use hip_tracker (#208) 2022-07-04 00:50:05 +03:00
Erimel
e875cbbcbc Place tracker at origin when disabled (#207) 2022-07-01 02:14:17 +03:00
Eiren Rain
663170681a Bump version to 0.2.0 2022-06-28 21:09:27 +02:00
Butterscotch!
b1318567e3 AutoBone: Adjust bone offsets for directly and implement new contribution based error calculation (#204) 2022-06-28 22:00:57 +03:00
Eiren Rain
d1a02c52a9 Monor changes, added comments 2022-06-28 20:36:04 +02:00
lucas lelievre
b3efcd19db Remove realTracker checks for cleaner code (#203) 2022-06-28 21:33:43 +03:00
Erimel
66e4e4e0a0 Basic shoulder tracking support (#200) 2022-06-28 21:32:11 +03:00
Erimel
7c37fdb24e Use split offsets and rename foot_offset to foot_shift (#199) 2022-06-28 21:20:31 +03:00
Eiren Rain
ceb1cc77d8 Minor cleanup and comments from last PRs 2022-06-28 20:11:08 +02:00
Ryan Butler
8df4726d22 [Feature] Add support for bones in DataFeedUpdate (#196) 2022-06-28 20:57:09 +03:00
Ryan Butler
408df5273d Fix NPE in Websocket logging (#202) 2022-06-28 20:21:38 +03:00
Ryan Butler
7a4d087be2 Prevent logs in gitignore (#201) 2022-06-28 20:21:01 +03:00
Erimel
11a15ab5b1 Cleaning up the skeleton a bit (#197) 2022-06-21 17:22:53 +03:00
Eiren Rain
f6ffd63b05 Fix left arm tracker not resetting (#198) 2022-06-21 01:13:09 +03:00
lucas lelievre
0afe9d2ef8 Fix optional in the feeder and bring back none option in the gui (#194) 2022-06-11 20:15:27 +03:00
Erimel
c02b95346f Extended knee model + temporary fix for null/optional (#193) 2022-06-09 13:21:40 +03:00
Erimel
db33c561ab fix minor typo in firewall.bat (#192) 2022-06-06 21:08:58 +03:00
Eiren Rain
df2cfa1e57 Skeleton data (#189) 2022-06-05 01:04:07 +03:00
Ryan Butler
d40e2c2ce2 Refactored TrackerPosition (#181) 2022-06-01 17:08:56 +03:00
Butterscotch!
215f3b9f44 Implement AutoBone into the protocol (#187)
Implement event-based AutoBoneHandler & more
2022-06-01 16:59:17 +03:00
Ryan Butler
766cf9ae72 Update instructions for spotless on IntelliJ (#183) 2022-05-31 13:42:57 +03:00
Kamilake
26d808deb7 Handling exception thrown when closing WiFiWindow (#185) 2022-05-30 17:43:43 +03:00
Eiren Rain
f9259c976d Update commons version 2022-05-24 17:17:32 +03:00
Erimel
39c3285a0a Fix getting previous rotations (#180) 2022-05-24 01:09:00 +03:00
Ryan Butler
57f3624ee7 CI & Formatting improvements (#178)
* CI & Formatting improvements

+ Added spotlessCheck to CI
+ Now formatting .yaml files (with spaces)
* Bumped version numbers of actions

* Renamed .yml to .yaml in accordance with yaml.org/faq
2022-05-23 15:54:10 +03:00
Eiren Rain
5a4b656a74 WIP of skeleton data 2022-05-19 19:03:38 +03:00
Eiren Rain
bf7e13d923 Update spotless 2022-05-19 18:08:57 +03:00
Ryan Butler
dedec668e1 Added spotless and CONTRIBUTING.md (#172) 2022-05-19 17:51:46 +03:00
Eiren Rain
1e4013be98 This time it should work 2022-05-16 20:52:28 +03:00
Eiren Rain
76c3210788 Should be the last one for gradle or I give up... 2022-05-16 20:48:42 +03:00
Eiren Rain
4a99eff854 Fix gradle build (hopefully) 2022-05-16 20:46:18 +03:00
Eiren Rain
237dabce17 Update commons with formatted code 2022-05-16 20:41:42 +03:00
Eiren Rain
6f36f7624a Update commons 2022-05-16 20:36:26 +03:00
Eiren Rain
1dea626422 Eclipse formatting (#176)
* Make eclipse formatter better follow our conventions and format all of it
* Don't join wrapped lines to allow for hand-formatting
* Fix formatting of switch statements and add breaks in enum declarations
* Use new formatter by @thebutlah, adjust formatter more
2022-05-16 20:05:46 +03:00
Eiren Rain
c74cc539c9 Clarify README regarding licencing 2022-05-08 17:56:53 +03:00
Eiren Rain
910e32bb3e Merge pull request #166 from Futurabeast/clean-get-tracker-by-id
Change getTrackerById
2022-05-03 15:33:18 +03:00
Eiren Rain
6ad3c862ce Merge pull request #171 from TheButlah/fix_concurrent_modification_datafeed
Fix concurrent modification in DataFeedHandler
2022-05-03 15:27:43 +03:00
Eiren Rain
05819baf97 Merge pull request #173 from TheButlah/trackerpos_from_role
Computed trackers use TrackerRole to determine TrackerPosition, if null
2022-05-03 15:25:52 +03:00
Eiren Rain
d84ba2aef5 Merge pull request #174 from TheButlah/fix_more_fbs_bugs
Fix position and rotation getting mixed up in flatbuffer
2022-05-03 15:24:20 +03:00
Ryan Butler
ec9e7d94bc Fix position and rotation getting mixed up in flatbuffer 2022-05-03 03:24:22 -04:00
Ryan Butler
a800749b87 Fix concurrent modification in DataFeedHandler 2022-05-03 00:41:25 -04:00
Ryan Butler
76fe83452e Computed trackers use TrackerRole to determine TrackerPosition, if null 2022-05-02 23:45:57 -04:00
Eiren Rain
6d940503af Merge pull request #169 from TheButlah/gitignore_eclipse
.gitignore eclipse stuff
2022-05-02 23:17:21 +03:00
Ryan Butler
0ea5b58b50 .gitignoring most eclipse stuff 2022-05-02 13:38:50 -04:00
Eiren Rain
94c079815f Merge pull request #168 from TheButlah/fix_computed_tracker_num
Computed trackers should use their tracker id as their num
2022-05-02 13:47:43 +03:00
Eiren Rain
11ab7a6cb6 Merge pull request #167 from TheButlah/fix_invalid_fb_serialization
Fix invalid flatbuffer offsets
2022-05-02 13:47:15 +03:00
Ryan Butler
7f67135e69 Computed trackers should use their tracker id as their num 2022-05-02 02:58:24 -04:00
Ryan Butler
a1717bbf7a Fix invalid flatbuffer offsets 2022-05-02 02:09:33 -04:00
Eiren Rain
c99375364d Merge pull request #165 from deiteris/use-foreach
Refactor for to foreach
2022-05-01 16:02:24 +03:00
Eiren Rain
94460ed7ab Merge pull request #163 from Futurabeast/fix-index-error-datafeed
Fix iteration on the sensors, i used the key of the map instead of the index of the array
2022-05-01 01:07:18 +03:00
lucas lelievre
413332462f more readable version of it 2022-04-30 23:06:14 +02:00
lucas lelievre
18386d57d6 Better tracker id with no possible null pointer exception 2022-04-30 22:50:32 +02:00
lucas lelievre
f72045438e Change getTrackerById 2022-04-30 21:58:26 +02:00
Yury
17f74c693c Refactor for to foreach 2022-04-30 20:34:13 +03:00
lucas lelievre
3fb87a4688 Fix iteration on the sensors, i used the key of the map instead of the index of the array 2022-04-30 04:03:53 +02:00
Eiren Rain
9205f6cab7 Merge pull request #162 from Futurabeast/format-all
Format all files based on .editorconfig
2022-04-28 15:32:48 +03:00
lucas lelievre
d0ee364fb9 Fix conflicts with main 2022-04-28 14:30:40 +02:00
lucas lelievre
3a80ad0632 Change the formating to tabs 2022-04-28 03:44:24 +02:00
lucas lelievre
93c353bbc6 Fix crlf 2022-04-27 23:20:14 +02:00
Eiren Rain
9f883dccec Fix eclipse prefs 2022-04-27 23:42:16 +03:00
lucas lelievre
ad3b697517 New formatting rules 2022-04-27 22:40:03 +02:00
Eiren Rain
5f3097185e Merge pull request #161 from Futurabeast/fix-forward-to-front
Fix Converting FORWARD rotation to FRONT
2022-04-27 23:33:11 +03:00
lucas lelievre
7ac7f7d424 New formatting rules 2022-04-27 22:29:09 +02:00
lucas lelievre
ba58f4df6a Reformat the formatter 2022-04-27 22:25:28 +02:00
lucas lelievre
297bfddf6c Update src/main/java/dev/slimevr/NetworkProtocol.java
Co-authored-by: TheDevMinerTV <tobigames200@gmail.com>
2022-04-27 22:21:09 +02:00
lucas lelievre
e4d0aca744 Update src/main/java/dev/slimevr/bridge/BridgeThread.java
Co-authored-by: TheDevMinerTV <tobigames200@gmail.com>
2022-04-27 22:21:05 +02:00
lucas lelievre
8b5ac226c3 Update src/main/java/dev/slimevr/bridge/BridgeThread.java
Co-authored-by: TheDevMinerTV <tobigames200@gmail.com>
2022-04-27 22:21:00 +02:00
lucas lelievre
cfc264fa33 Update src/main/java/dev/slimevr/bridge/BridgeThread.java
Co-authored-by: TheDevMinerTV <tobigames200@gmail.com>
2022-04-27 22:20:49 +02:00
lucas lelievre
9e12958ca6 reformat all 2022-04-27 21:39:54 +02:00
lucas lelievre
8609fb9e97 Fix Converting FORWARD rotation to FRON 2022-04-27 21:30:54 +02:00
Eiren Rain
9d6dda8b17 Use https for solarxr module 2022-04-27 19:38:36 +03:00
Eiren Rain
92b64f0f12 Merge pull request #159 from Futurabeast/main
Headless Server / SolarXR Protocol implementation
2022-04-27 19:33:59 +03:00
lucas lelievre
677fa71ba1 Cancel change to .settings 2022-04-26 02:08:56 +02:00
lucas lelievre
9f3f34ce70 Reformat protocol 2022-04-26 02:07:45 +02:00
lucas lelievre
2d2b6588b3 Sync with main solarxr 2022-04-26 02:04:59 +02:00
lucas lelievre
44fa266697 remove slimevr_protocol from submodules 2022-04-25 00:39:37 +02:00
lucas lelievre
57f7ea6745 clean useless stuff 2022-04-25 00:38:50 +02:00
lucas lelievre
326c7e969a remove ignored files 2022-04-25 00:37:29 +02:00
lucas lelievre
23f9b3b276 Sync with lastest server 2022-04-25 00:21:32 +02:00
lucas lelievre
1e74deede3 Current progress 2022-04-24 19:53:56 +02:00
loucass003
2317ad94f5 Update submodule to solarxr-protocol 2022-04-24 01:20:55 +02:00
Eiren Rain
58d1f2de96 Merge pull request #158 from Louka3000/skeloff-fix
Fix hip when not using legs
2022-04-23 19:03:15 +03:00
Louka
de101b3576 Fix hip when not using legs
disables pelvis averaging if user isn't using knee trackers.
2022-04-20 21:26:36 -04:00
lucas lelievre
9fbfc43655 Clean packages 2022-04-16 16:40:30 +02:00
loucass003
e3e37023e8 More rpc stuff 2022-04-15 01:17:16 +02:00
Eiren Rain
63409b61ea Merge pull request #157 from mgschwan/main
Change variable naming of configPath
2022-04-14 17:15:20 +03:00
Michael Gschwandtner
c827e7725b Revert changed files 2022-04-14 16:10:35 +02:00
Michael Gschwandtner
0bcaed719b Merge branch 'SlimeVR:main' into main 2022-04-14 16:06:17 +02:00
Michael Gschwandtner
c77d73c460 Rename configPath variable 2022-04-14 16:04:37 +02:00
loucass003
9fc5d41e1b protocol rework Sets -> Map 2022-04-14 09:15:01 +02:00
Eiren Rain
ac0cb9e9cb Merge pull request #149 from Louka3000/hand-tracking
Hand tracking support + elbows without controllers
2022-04-12 20:22:32 +03:00
lucas lelievre
f961318035 update submodule 2022-04-12 09:09:29 +02:00
lucas lelievre
52fa670293 Datafeed almost done 2022-04-12 09:07:18 +02:00
Eiren Rain
d0c3e0ae8d Merge pull request #155 from mgschwan/main
Make VRServer config path variable
2022-04-12 01:57:10 +03:00
Louka
0b2491ead5 Properly handle elbows with hand tracking
Instead of moving the controller chains to the slimevr hand, now just make the computed elbows affected by the elbow from the slimevr hand chain.

Also refactored some stuff to replace "upper arm distance" by "elbow offset". Idek if it's useful but it may be so I'm keeping it.
2022-04-11 18:39:30 -04:00
Michael Gschwandtner
1723d19882 Make VRServer config path variable
Add a constructor that accepts a path to the config file
2022-04-12 00:37:46 +02:00
Louka
416f96fe44 Merge branch 'main' into hand-tracking 2022-04-11 17:41:32 -04:00
Eiren Rain
7b700b4a0c Merge pull request #154 from mgschwan/main
Make WebSocketVRBridge compatible with Android
2022-04-11 21:37:50 +03:00
Eiren Rain
bde578fb9d Merge pull request #151 from Louka3000/neck-tracking
Neck tracking support owo
2022-04-11 21:05:37 +03:00
Michael Gschwandtner
c41dc490ed Make WebSocketVRBridge compatible with Android
Change JSONObject.optFloat (does not exist on Android) to optDouble
2022-04-11 16:00:42 +02:00
Eiren Rain
6d9e816d19 Merge pull request #153 from Louka3000/filters-fix
Make filter dependent on tracker tps
2022-04-08 00:59:28 +03:00
Erimel
8f19afee88 Merge branch 'SlimeVR:main' into hand-tracking 2022-04-07 01:18:50 -04:00
Louka
784ec877d8 Make filter dependent on tracker tps
Why was this in the wrong place wth
2022-04-06 20:35:47 -04:00
Eiren Rain
48c509ef54 Merge pull request #152 from mgschwan/main
Add full reset command to WebSocket bridge
2022-04-06 15:43:56 +03:00
Eiren Rain
0a5f06816d Merge pull request #150 from Louka3000/patch-1
Null check on designation I forgor
2022-04-06 15:42:06 +03:00
Louka
ecbeaf10ed Remove unnecessary code
Also makes my code a whole lot clearer damn.
Now I can understand it myself!!! :P
2022-04-05 18:02:30 -04:00
Michael Gschwandtner
c8e4918b4e Merge branch 'SlimeVR:main' into main 2022-04-05 22:17:55 +02:00
Michael Gschwandtner
4ab5609dbd Add full reset command to WebSocket bridge 2022-04-05 22:11:40 +02:00
Louka
cfe8eb62e7 Neck tracking support owo
This is not a joke.
As I suspected, adding a neck tracker does help with reducing sliding. I tested it in VRChat myself.
Think of it like adding a fourth spine tracker.
Anyway, this can be useful to mocap suits and furries, pls merge ty owo.
2022-04-05 00:11:43 -04:00
Erimel
f2ceb84969 Null check on designation I forgor
sowwy, this completely breaks the server whenever a new tracker (never connected before) connects.  

;-;
2022-04-04 23:41:51 -04:00
Louka
039628d7e4 Hand tracking support + elbows without controllers
This extends the HMD skeleton to support arm tracking. We do end up with kinda duplicate bones tho.

If user has controllers: elbows go from controllers with tracker on lower arm and possibility for upper arm tracker.

If user has no controller (or no feeder app) and enables Elbow SteamVR trackers: elbows go from chest.

If user has no controllers and enables Hand SteamVR trackers: SteamVR trackers on the users hand appear using upper arm, forearm and hand trackers. Use chest tracker as well for more precision.

This also adds a lot of body proportions...
2022-04-04 22:21:18 -04:00
Eiren Rain
91fb4bc035 Merge pull request #146 from Louka3000/legs-fix
Fix ambiguous usage of "Legs"
2022-04-04 16:07:59 +03:00
Eiren Rain
d89ccc3401 Merge pull request #147 from TheDevMinerTV/fix/pingpongpacket
Fix "could not parse packet" warning for PingPongPackets
2022-04-04 16:02:21 +03:00
loucass003
7ab9a37989 Started implementing new protocol 2022-04-04 07:37:26 +02:00
TheDevMinerTV
cf7054b6da Fix "could not parse packet" warning for PingPongPackets 2022-04-04 00:29:22 +02:00
Louka
758f9e8f2f support old configs + rename in skeleton 2022-04-03 11:44:40 -04:00
Louka
fef21906f5 Fix ambiguous usage of "Legs"
- Leg in tracker roles is now knee
- Legs in SteamVR trackers to enable is now Feet
2022-04-03 11:18:44 -04:00
loucass003
37ce6a69c1 update submodule 2022-04-02 22:35:45 +02:00
loucass003
aaed8fbd49 Body proportions 2022-04-02 22:33:32 +02:00
loucass003
5fbac9d861 More data 2022-04-02 08:39:31 +02:00
lucas lelievre
c54298709f update submodules url 2022-04-01 05:32:25 +02:00
lucas lelievre
b7d1637b18 Add more tracker data 2022-04-01 04:57:51 +02:00
lucas lelievre
015fa551b7 slimevr_protocol submodules 2022-03-31 16:05:34 +02:00
lucas lelievre
a681f0e5b3 rm folder 2022-03-31 16:05:03 +02:00
lucas lelievre
281810dfbb begin websocket api 2022-03-31 16:00:13 +02:00
Eiren Rain
38b8e65d53 Merge pull request #137 from Louka3000/main
Basic Interpolation and Extrapolation
2022-03-28 16:56:05 +03:00
Louka
74ccaa6cf1 fix size/capacity of circle buffer 2022-03-28 00:07:43 -04:00
Louka
4d2083df27 Use circle buffer to get previous rotations
Should help with performance.
2022-03-27 23:58:23 -04:00
Louka
98068232a6 don't create as many quaternions 2022-03-27 06:33:36 -04:00
Eiren Rain
60711df671 Merge pull request #128 from ButterscotchV/autobone-work
Consolidate PoseFrames functionality and add documentation
2022-03-24 14:05:55 +02:00
Erimel
c6b7c11418 Merge branch 'SlimeVR:main' into main 2022-03-23 21:47:52 -04:00
Eiren Rain
ece8811293 Merge pull request #138 from Louka3000/issue61
Align spine yaw with HMD when no spine trackers are present
2022-03-24 02:10:03 +02:00
Louka
8c95c491b9 use GetYaw() 2022-03-23 18:53:43 -04:00
Erimel
45091565b4 Merge branch 'SlimeVR:main' into issue61 2022-03-23 18:42:36 -04:00
Erimel
7c0222189a Merge branch 'SlimeVR:main' into main 2022-03-23 18:42:26 -04:00
Eiren Rain
a84c735761 Update commons 2022-03-24 00:40:20 +02:00
Louka
946c5d4527 calculate hmd yaw better 2022-03-23 18:34:47 -04:00
Louka
2ac6208302 / 2 * 4 = *2 2022-03-23 18:05:58 -04:00
Louka
0cd65c825c Align spine yaw with HMD when no spine tracker
issue https://github.com/SlimeVR/SlimeVR-Server/issues/61
2022-03-23 18:04:09 -04:00
Eiren Rain
43d97a7b97 Merge pull request #136 from Louka3000/upper-arms
Upper arm tracking support for VRC shoulders
2022-03-23 18:38:02 +02:00
Louka
9923556212 removed bad code lol
Code I stole was bad and did unncessery things that made extrapolation wonky
2022-03-22 21:44:29 -04:00
Louka
0a65783637 Make bone
bone yes
2022-03-22 19:58:52 -04:00
Louka
b5bd7e0a26 changed default values for filters 2022-03-22 18:35:00 -04:00
Louka
82779d101b fixed a NullPointerException
When the type was null oops
2022-03-22 01:20:40 -04:00
Louka
9c18d349dd upper arms support
for vrchat's shoulder tracking.
Not recommended since that's 2 trackers PER ARM, but good to have the option.
2022-03-22 00:46:38 -04:00
Louka
8c74071e30 Small adjustments to filter
-renamed frame to tick
- increased max intensity
2022-03-19 13:33:54 -04:00
Louka
256e5079cf Tracker filters GUI and config
I am now master at GUI.
(never again).
Might have done some dumb things like giving IMUTracker VRServer idk, tell me if there's something to fix <3
2022-03-19 03:02:06 -04:00
Louka
4263e86189 Basic interpolation and extrapolation
only the logic. Need to make config and GUI
2022-03-18 22:36:06 -04:00
Eiren Rain
dadb0bb378 Merge pull request #135 from Louka3000/main
Prettier changing proportions
2022-03-15 14:56:26 +02:00
Erimel
8ba25ca840 Merge branch 'SlimeVR:main' into main 2022-03-14 18:12:01 -04:00
Louka
09ac04331f Prettier changing proportions
- Now removes the .0 if the value is whole (only see decimal if is .5)
- Add precision adjust button unselected by default to toggle between 1 and 0.5 adjustment.
2022-03-14 18:11:29 -04:00
Eiren Rain
c21caa76d5 Bump version number to 0.1.6 2022-03-14 20:02:10 +02:00
Eiren Rain
4073d8fc32 Merge pull request #134 from Louka3000/main
Change default proportions and allow half-decimal editing
2022-03-14 15:33:49 +02:00
Erimel
f8b7be8572 Merge branch 'SlimeVR:main' into main 2022-03-13 18:27:01 -04:00
Louka
d6ab811de0 Change default proportions and allow half-decimal editing
- Better initial and default body proportions for people that don't want to calibrate
- Now changes bone length to half decimal (0.5). Rounds up to the nearest 0.5.
2022-03-13 18:25:43 -04:00
Eiren Rain
9fa6722f2b Merge pull request #133 from Blixtdraken/main
Changed the scroll speed for the ui
2022-03-13 18:21:35 +02:00
Blixtdraken
c51204e9cd Changed the scroll speed for the ui. 2022-03-13 17:12:40 +01:00
Eiren Rain
74794d8610 Merge pull request #132 from Louka3000/main
Elbow tracking support OwO
2022-03-12 12:56:49 +02:00
Louka
16978f5acd change default elbow distance
25cm to 22cm
2022-03-11 21:15:49 -05:00
Louka
71e24d0cb9 oops Mighty <3
fix FORWARD being renamed to FRONT
2022-03-10 19:47:28 -05:00
Louka
9a45f99b0e Vertical controllet offset 2022-03-10 18:45:36 -05:00
Louka
7f829f56a3 Fix reset and fix pipe creation order
owo
2022-03-10 18:35:51 -05:00
Erimel
347531f4fe Merge branch 'SlimeVR:main' into main 2022-03-10 18:21:08 -05:00
Eiren Rain
a1c33a0852 Merge pull request #131 from MightyGood/patch-1
Change FORWARD to FRONT for tracker mounting
2022-03-10 17:55:01 +02:00
MightyGood
9cd441654e Change FORWARD to FRONT for tracker mounting
Front fits better with the other options, and should cause less confusion about what the drop-down items mean.
2022-03-10 09:46:24 -06:00
Eiren Rain
88c866a735 Merge pull request #130 from kitlith/feeder_app_protobuf
Switch feeder app from old protocol to new protocol.
2022-03-10 16:00:55 +02:00
Louka
b11492c3f3 minor cleanup
removed skeleton offset for arms; useless
2022-03-10 01:07:35 -05:00
Erimel
52e30b6323 Merge pull request #3 from kitlith/feeder_app_protobuf
Switch feeder app from old protocol to new protocol.
2022-03-10 00:37:47 -05:00
Louka
1ebad806f9 Fix reset and wrist
oh wow, it works.
2022-03-09 19:24:35 -05:00
Louka
51129d3b5d Wrists
doesn't work as intended but does something
2022-03-09 01:27:13 -05:00
Louka
379e1cdcf0 Get all nodes better
for SkeletonList
2022-03-09 01:08:26 -05:00
Louka
0a8f76cfd4 Initial elbows commit
testing
2022-03-08 00:13:49 -05:00
Kitlith
a382698c32 Switch feeder app from old protocol to new protocol. 2022-03-07 20:56:22 -08:00
Butterscotch!
d43002952c Consolidate PoseFrames functionality & add documentation 2022-02-17 22:51:15 -05:00
Eiren Rain
718f1d02c6 Merge pull request #127 from ColdIce1605/ignore-runtime-created-files
Ignore runtime created files
2022-02-17 08:40:52 +02:00
ColdIce
542de22550 Revert "restart branch"
This reverts commit e18ce338e9.
2022-02-16 21:16:22 -06:00
ColdIce
327d458f00 update .gitignore 2022-02-16 21:14:49 -06:00
Eiren Rain
5f206dd12e Merge pull request #125 from Louka3000/patch-2
waist tracker will default to hip before chest
2022-02-16 12:33:09 +02:00
ColdIce
e18ce338e9 restart branch 2022-02-16 01:25:15 -06:00
Erimel
51e6255e9d waist tracker will default to hip before chest
If user has chest + waist but uses chest + hip in the server, chest would be chest+waist and waist would be hip.
This can be fixed by a better explanation of “waist vs hip” in the new GUI, or just defaulting waist to hip instead of chest when no waist tracker is found.
2022-02-15 14:48:21 -05:00
Eiren Rain
7fa7e6c2cc Merge pull request #124 from ColdIce1605/platform-package-fix
Platform Package Fix
2022-02-15 10:19:34 +02:00
ColdIce
710d154817 remove files 2022-02-15 02:17:55 -06:00
ColdIce
f354a10a81 fix .idea related things
sadly can't get rid of the one Magneto change do to LF :P
2022-02-15 02:17:14 -06:00
ColdIce
8bb8135f41 Move windows related code to its own package
Ah no more git history issues
2022-02-15 02:12:20 -06:00
Eiren Rain
e7b9968519 Better tracker timeout handling 2022-02-15 09:59:27 +02:00
Eiren Rain
bfc58d51f2 Merge pull request #122 from Kamilake/patch-1
Change the minimum compatible Java version
2022-02-11 07:14:10 +02:00
Eiren Rain
c158022da5 Merge pull request #123 from Kamilake/patch-2
Add a hide password option
2022-02-11 05:56:55 +02:00
Kamilake
9e010b0026 Add a hide password option 2022-02-11 11:44:18 +09:00
Kamilake
a085b09e07 Change the minimum compatible Java version 2022-02-11 09:36:11 +09:00
Kamilake
37da4ab7fe Change the minimum compatible Java version 2022-02-11 09:22:28 +09:00
Eiren Rain
c5945d784b Merge pull request #121 from ButterscotchV/spell-fix
Fix spelling mistakes
2022-02-09 21:09:33 +02:00
Butterscotch!
16ca08446b Fix spelling mistakes 2022-02-09 04:44:05 -05:00
Eiren Rain
b487350714 Update version to 0.1.5 2022-02-07 20:31:19 +02:00
Eiren Rain
753b12b49e Merge pull request #120 from ButterscotchV/bvh-recording
Add simple BVH recording button
2022-02-07 20:01:45 +02:00
Butterscotch!
0d90cf9c20 Add simple BVH recording button 2022-02-07 01:40:11 -05:00
Eiren Rain
658fd2916d Merge pull request #118 from deiteris/main
Switch to latest gradle and update dependencies
2022-02-05 17:43:00 +02:00
Yury
ed4ea675fb Keep JavaOSC package and remove slf4j package 2022-02-05 18:39:10 +03:00
Yury
2746fd7a67 Remove guava 2022-02-05 18:37:25 +03:00
Eiren Rain
a6b92c60b0 Update commons 2022-02-05 17:33:40 +02:00
Eiren Rain
d4d36a65ec Merge pull request #119 from ButterscotchV/bvh-fix
Fix BVH local angle calculations and abstract PoseStreamer
2022-02-05 17:25:34 +02:00
Butterscotch!
97df8ee12f Simplify PoseFrameStreamer constructor 2022-02-05 07:21:14 -05:00
Butterscotch!
2ab637b4e8 Improve PoseFrameStreamer functionality 2022-02-05 07:08:45 -05:00
Butterscotch!
9a821b051f Abstract PoseStreamer 2022-02-05 07:08:44 -05:00
Butterscotch!
e2f09fc93d Add fake root method & fix local angle calculation 2022-02-05 06:01:07 -05:00
Butterscotch!
891d8e0468 Fix BVH angles 2022-02-05 05:04:31 -05:00
Yury
494e31e41f Switch to latest gradle and update dependencies 2022-02-05 13:02:46 +03:00
Eiren Rain
b369ae6a2a Fix battery reading compatibility with owoTrack 2022-02-02 18:39:53 +02:00
Eiren Rain
5c22ef0192 Fix backwards compatibility with extensions on old firmware 2022-01-29 00:45:28 +02:00
Eiren Rain
d99cbb9c85 Fix backwards compatibility with old firmware and owoTrack 2022-01-28 02:53:08 +02:00
Eiren Rain
4f14f01830 Merge pull request #114 from SlimeVR/test
Network refactoring
2022-01-27 21:51:56 +02:00
Eiren Rain
930b5c701a Cleanup UDP responses 2022-01-27 21:38:49 +02:00
Eiren Rain
bd9e2c47a3 Merge branch 'main' into test 2022-01-27 21:30:39 +02:00
Eiren Rain
53ca2cf881 Move UDP packets and parsing to own calsses
Cleanup UDP networking significantly
2022-01-27 21:30:25 +02:00
Eiren Rain
55e17e7625 Minor network fixes 2022-01-27 19:52:30 +02:00
Eiren Rain
13b37aa2a9 Improved debug, added checkbox to display sensors debug info 2022-01-27 19:31:13 +02:00
Eiren Rain
fe4dde69ea Merge pull request #113 from Louka3000/patch-1
fix link setup
2022-01-27 02:21:07 +02:00
Erimel
0268a5a3ec fix link setup 2022-01-26 19:13:52 -05:00
Eiren Rain
4bddb529d4 Fix ping not working 2022-01-25 22:00:02 +02:00
Eiren Rain
435f5d1751 Don't create new trackers if tracker's IP changed while server is running, hand over old trackers to the new connection
Implements #70
2022-01-25 21:46:50 +02:00
Eiren Rain
af8ce60dbe Fix signal strength reading 2022-01-20 20:57:03 +02:00
Eiren Rain
25f53232cd Fix packet number reading 2022-01-20 20:27:23 +02:00
Eiren Rain
012cb518b3 Fix merge issues, track packets order, improve logging in UDP server 2022-01-20 18:03:53 +02:00
Eiren Rain
2d1ffbc5b0 Merge branch 'main' into test
# Conflicts:
#	src/main/java/dev/slimevr/vr/trackers/TrackersUDPServer.java
2022-01-20 17:54:56 +02:00
Eiren Rain
c88a6802a9 Minor debug stuff 2022-01-20 17:50:08 +02:00
Eiren Rain
f5d608ac6a Merge pull request #111 from ButterscotchV/autobone-fix
Fix AutoBone overwriting configs & improve code documentation
2022-01-20 14:19:46 +03:00
Butterscotch!
5d49bbfb29 Fix AutoBone overwriting configs & improve code documentation 2022-01-19 19:20:47 -05:00
Eiren Rain
5ce520a316 Merge pull request #110 from deiteris/main
Fix decimal places for battery voltage
2022-01-20 00:44:49 +03:00
Yury
98c2c6e202 Merge branch 'main' of https://github.com/deiteris/SlimeVR-Server 2022-01-20 00:25:12 +03:00
Yury
a2fc809d71 Fix decimal places for battery voltage 2022-01-20 00:24:47 +03:00
Eiren Rain
eb302aaef1 Merge pull request #107 from Louka3000/main
Updated default body proportions (again
2022-01-16 02:12:36 +03:00
Louka
3b354f103a Update SkeletonConfigValue.java 2022-01-15 18:00:50 -05:00
Eiren Rain
03c24a5d39 Merge pull request #106 from deiteris/main
Fix log in firewall_uninstall.bat
2022-01-13 01:11:38 +03:00
Yury
a8f13bb570 Fix log in firewall_uninstall.bat 2022-01-13 00:56:08 +03:00
Eiren Rain
f8e35e0a72 Merge pull request #105 from deiteris/main
Show battery level reported by tracker
2022-01-13 00:32:10 +03:00
Yury
27c153f5d3 Show battery level reported by tracker 2022-01-12 19:30:51 +03:00
Eiren Rain
82fdedfa14 Minor changes 2022-01-10 12:54:25 +02:00
Eiren Rain
f5bfbb13e2 Added contributions notice to the README 2022-01-10 12:50:02 +02:00
Eiren Rain
80de578334 Fix missing refactoring changes 2022-01-09 12:27:32 +02:00
Eiren Rain
3b0acbe406 Create new trackers only when sensor info packet received 2022-01-09 12:25:57 +02:00
Eiren Rain
1062361612 Merge pull request #99 from deiteris/main
Add RSSI to trackers
2022-01-09 13:17:57 +03:00
Eiren Rain
7d81fe6f92 Merge pull request #102 from Louka3000/main
Skeleton offset
2022-01-09 11:13:42 +03:00
Eiren Rain
0285eca613 Merge pull request #103 from ColdIce1605/fix-typos
Fix typos
2022-01-08 09:03:10 +03:00
James R
b0aea9ba89 fix typos 2022-01-07 20:54:34 -06:00
Louka
b98eafb66f Remove vertical foot offset
Only keep skeleton offset
2022-01-07 09:12:06 -05:00
Louka
566df6793c fixed foot rotation (I broke it)
I still think there's a better way to do this horizontal foot offset thing
2022-01-06 23:43:26 -05:00
Louka
4949e0a7f3 Skeleton offset and vertical foot offset 2022-01-06 22:30:20 -05:00
Yury
572dcdf1bb Add RSSI to trackers 2022-01-02 23:25:56 +03:00
Eiren Rain
80ce825494 Merge pull request #97 from carl-anders/keybindings-wrap-even-more-no-more-panics-plz
Keybindings: Catch even more errors, and manually check that operating system is Windows
2021-12-27 02:23:13 +03:00
Carl Andersson
bdc3b1971c Fix accidental inversion of check 2021-12-26 21:49:46 +01:00
Carl Andersson
cee400a4c6 Simplify OS & error checks 2021-12-26 21:48:52 +01:00
Carl Andersson
e58706d212 Keybindings: Catch even more errors, and manually check that operating system is Windows 2021-12-26 06:05:27 +01:00
Eiren Rain
ad03caa064 Bump version to 0.1.3 2021-12-24 21:26:03 +02:00
Eiren Rain
e2d6189547 Code formatting 2021-12-24 21:25:40 +02:00
Eiren Rain
7b15d242f7 Move rest of the classes to dev.slimev package, comment out wrong unit-test, minor cleanups 2021-12-24 21:18:58 +02:00
Eiren Rain
b81458d034 Merge pull request #95 from ButterscotchVanilla/skeleton-refactor
Skeleton refactor
2021-12-24 22:09:57 +03:00
Eiren Rain
0595422f69 Merge pull request #96 from deiteris/main
Send empty packets when there are no active trackers
2021-12-18 20:46:43 +03:00
Yury
8e58adb279 Send empty packets when there are no active trackers 2021-12-18 16:33:49 +03:00
ButterscotchVanilla
a5e4b4d8e2 Fix all config "Reset" buttons being timed 2021-12-16 21:33:48 -05:00
ButterscotchVanilla
be2c010b5a Remove comment, this was fixed 2021-12-16 19:14:37 -05:00
ButterscotchVanilla
e107326fee Fix merge conflict 2021-12-16 18:54:20 -05:00
Eiren Rain
4da54f6dec Merge pull request #94 from Louka3000/main
Changed default body proportions
2021-12-16 18:15:42 +03:00
ButterscotchVanilla
1a3a955e10 Change StringBuilder.isEmpty() to length check instead for Java 8 compatibility 2021-12-16 02:26:02 -05:00
ButterscotchVanilla
3f304f7275 Optimize offset calculation for bulk config set & finish AutoBone implementation 2021-12-16 02:06:55 -05:00
ButterscotchVanilla
0690d742c7 Properly save SkeletonConfig values 2021-12-16 01:38:16 -05:00
ButterscotchVanilla
43bbd4b4dd Update config defaults from @Louka3000 2021-12-16 00:27:19 -05:00
ButterscotchVanilla
77fa27a698 Remove String skeleton config & fix null exception with SkeletonConfig 2021-12-15 23:00:08 -05:00
Louka
8991e4f9f8 Changed default body proportions 2021-12-15 20:43:13 -05:00
ButterscotchVanilla
c9740651ba Remove other HumanSkeleton implementations 2021-12-08 23:35:21 -05:00
ButterscotchVanilla
473550ba07 Move skeleton namespace 2021-12-08 23:12:43 -05:00
ButterscotchVanilla
a7cbe91e73 Make SimpleSkeleton compatible with HumanSkeleton 2021-12-08 23:10:54 -05:00
ButterscotchVanilla
40281f68b9 Use config enum instead of strings for AutoBone 2021-12-08 22:00:43 -05:00
ButterscotchVanilla
d3049751ba Update AutoBone to use SkeletonConfig 2021-12-08 21:26:32 -05:00
ButterscotchVanilla
68164756c2 Add more ways to set SkeletonConfig values 2021-12-08 19:53:07 -05:00
ButterscotchVanilla
91c0ddef28 Add new SkeletonConfig class for configuring bone lengths and toggles 2021-12-08 17:57:53 -05:00
Eiren Rain
0641ca1b7b Bump version to 0.1.2 2021-12-07 14:26:20 +02:00
ButterscotchVanilla
e3d9eb6ac9 Add new config for offset slide error & disable dist scaling 2021-12-06 19:37:47 -05:00
ButterscotchVanilla
7eec89bd53 AutoBone: Add offset slide error 2021-12-04 20:59:42 -05:00
ButterscotchVanilla
289a7f8313 AutoBone: Scale distances by height difference 2021-12-04 02:55:18 -05:00
ButterscotchVanilla
f49b2556ae Add full tracker functionality to SimpleSkeleton 2021-12-04 00:45:37 -05:00
Eiren Rain
318c43077c Merge pull request #89 from ButterscotchVanilla/bvh-standard
Add functional BVH recording
2021-12-02 23:54:10 +03:00
Eiren Rain
5691b68166 Merge pull request #90 from ButterscotchVanilla/patch-1
Update GitHub Actions workflow
2021-12-02 23:52:31 +03:00
Butterscotch!
0ea44f988c Update GitHub Actions workflow 2021-12-02 15:45:11 -05:00
ButterscotchVanilla
59e2f796eb Add reference to ViRe in a comment 2021-12-02 12:14:45 -05:00
ButterscotchVanilla
f1a75a98d0 Add extra Spine node for rotation 2021-12-02 03:37:13 -05:00
ButterscotchVanilla
76ac3fcf55 Fix angle calculations 2021-12-01 23:50:47 -05:00
ButterscotchVanilla
6cc3c8e84b Add attempted Euler conversion 2021-12-01 23:19:00 -05:00
ButterscotchVanilla
36907c3244 Add closeOutput for specific stream 2021-12-01 20:21:55 -05:00
ButterscotchVanilla
dfeb02c1a7 Add local rotation calculations to TransformNodeWrapper 2021-12-01 20:17:36 -05:00
ButterscotchVanilla
6adf5f4090 Move node hierarchy wrapping to TransformNodeWrapper 2021-12-01 20:17:36 -05:00
ButterscotchVanilla
e44ce3fb0b Add getParent to TransformNode and add StdBVHFileStream 2021-12-01 20:17:36 -05:00
ButterscotchVanilla
76ab69e44e Wrap TransformNodes for different PoseStream hierarchy requirements 2021-12-01 20:17:35 -05:00
Eiren Rain
57d009df5c Fix bug with wrong trackers being read in skeleton if no leg trackers are attached 2021-12-02 02:11:01 +02:00
Eiren Rain
b4d07b0b7e Merge pull request #88 from deiteris/main
Improve bat script error checking
2021-12-01 16:46:07 +03:00
Yury
da3afa6f8e Improve bat script error checking 2021-12-01 11:43:35 +03:00
Eiren Rain
ec1c491e93 Merge pull request #87 from Louka3000/main
autobone hip-only doesn't affect waist distance anymore
2021-12-01 10:27:45 +03:00
Louka
baccb556e8 autobone hip-only doesn't affect waist distance anymore
- Now autobone checks if user has BOTH waist and hip tracker to add the waist distance value.
- Also renamed certain variable, replacing "hip" by "torso"
2021-11-29 20:08:08 -05:00
Eiren Rain
eedfa61d74 Merge pull request #85 from deiteris/main
Prevent path change when running as admin
2021-11-24 08:39:44 +03:00
Eiren Rain
2a9225178f Merge pull request #84 from Louka3000/main
Changed body proportions: Torso, Chest, Waist
2021-11-23 23:36:31 +03:00
Louka
259190e478 Changed body proportions: Torso, Chest, Waist
Body proportions have been changed: - Torso length is now the base value, replacing waist length in earlier versions.
- Hip length is now waist distance. Waist distance is only used when using a hip tracker
- Chest distance/length is the same. It is only used when using a chest tracker
- Autobone support with any mix-n-match configuration :)
- Virtual Waist changed its name to "Hip offset". It still behaves the same.
2021-11-23 15:11:16 -05:00
Yury
24a0c3b136 Prevent path change when running as admin 2021-11-23 12:11:09 +03:00
Louka
f46f2bc913 Tries to get the waist first for the rotation node for Autobone
When you want the rotation node, it should give the node that has the rotation that affects the bone you want
2021-11-22 22:48:43 -05:00
Eiren Rain
77f048c48e Merge pull request #83 from Louka3000/hip-tracker
Hip tracker support
2021-11-23 04:00:14 +03:00
Louka
4055d51758 Autobone support
Added basic autobone support for the hip tracker, fixed "allTracekrs" and changed initial values of upper body.
2021-11-21 18:14:19 -05:00
Louka
21eff5e1ba Better reset and initial values
owo
2021-11-20 21:32:47 -05:00
Louka
34174b442f Virtual waist affected by kneebuf 2021-11-20 20:49:32 -05:00
Louka
77d37ab2a7 Cleanup and support for hip tracker alone/
why would you want hip alone instead of waist? idk
2021-11-20 16:35:22 -05:00
Louka
350fdbce9d Can use chest tracker alone
hip tracker will default to the waist tracker's position (waist or chest) if no hip tracker is found
2021-11-20 15:03:33 -05:00
Louka
e19cec4d3e Initial commit
added hip tracking support.
Independent from waist.
Has a hip length value going from waist to hip.
Legs depend on hip instead of waist
should work normally without hip tracker.
Should work just fine with a virtual waist offset.
2021-11-20 00:04:33 -05:00
Eiren Rain
e56d7665ed Merge pull request #81 from carl-anders/softer-keybinding-failure-mode
Keybinding: If JIntellitype fails to load, still allow server to run
2021-11-12 02:59:46 +02:00
Carl Andersson
a0e23bfbe9 Keybinding: Be even better at catching errors from JIntellitype 2021-11-12 00:35:41 +01:00
Carl Andersson
b7dc33f79e Keybinding: If JIntellitype fails to load, still allow server to run 2021-11-11 06:31:35 +01:00
Eiren Rain
d8c31eec81 Merge pull request #80 from deiteris/main
Display information message in case Java is not installed
2021-11-08 20:32:42 +02:00
Yury
e84ee760b1 Display information message in case Java is not installed 2021-11-08 21:25:26 +03:00
Eiren Rain
6ba1cc6bdb Merge pull request #79 from deiteris/main
Add forgotten imports
2021-11-08 17:22:31 +02:00
Yury
1b5e534592 Add forgotten imports 2021-11-08 18:01:43 +03:00
Eiren Rain
1a3e21007b Merge pull request #78 from deiteris/main
Prevent server from running if required ports are busy
2021-11-06 20:26:58 +02:00
Yury
55e11ffb5c Prevent server from running if required ports are busy 2021-11-06 21:22:24 +03:00
Eiren Rain
957a040996 Bump version to v0.1.1 2021-10-23 21:00:31 +03:00
Eiren Rain
460a42b135 Merge pull request #67 from ButterscotchVanilla/mocap-stuff
Add base internal mocap functionality
2021-10-22 17:43:47 +03:00
Eiren Rain
8c356b401c Merge pull request #71 from Louka3000/main
Foot Offset and Updated Icons
2021-10-22 17:42:19 +03:00
ButterscotchVanilla
820d06f008 Move PoseStreamer files 2021-10-21 22:48:38 -04:00
Louka
3fa22fa716 Merge branch 'SlimeVR:main' into main 2021-10-20 21:17:09 -04:00
Louka
333daa9cfb Updated Icons
Replaced the old Icons with the new ones. Also made the 16x16 icon pixel.
2021-10-20 21:15:23 -04:00
Eiren Rain
0e61460cdb Bump version to 0.1.0 Test 1 2021-10-21 00:57:49 +03:00
Eiren Rain
268da18bb5 Properly handle tracker error state for tracker extensions, and timeout for all trackers 2021-10-21 00:57:13 +03:00
Eiren Rain
3fec29cbc6 Minor GUI cleanup 2021-10-20 03:49:57 +03:00
Louka
4f3e79a4ac Foot Offset
Added horizontal backwards/forwards foot offset feature.
2021-10-17 01:03:31 -04:00
Eiren Rain
8b3699024b Merge branch 'main' of https://github.com/SlimeVR/SlimeVR-Server into main 2021-10-17 02:08:40 +03:00
Eiren Rain
dd071f2538 Properly use tracker's IP if no MAC recieved 2021-10-17 02:07:30 +03:00
Eiren Rain
ebe74dac17 Merge pull request #69 from ButterscotchVanilla/java-popup
Add a simple error log and popup for unsupported Java versions
2021-10-14 16:35:45 +03:00
ButterscotchVanilla
909a51892f Add a simple error log and popup for unsupported Java versions 2021-10-14 04:31:20 -04:00
ButterscotchVanilla
35c26bec0f Fix Pose Recorder and Streamer synchronization and reduce if nesting 2021-10-13 07:05:29 -04:00
ButterscotchVanilla
8f05284792 Rename PoseFileStream and remove unexpected buffer 2021-10-13 03:05:39 -04:00
ButterscotchVanilla
e1d3af0734 Fix formatting 2021-10-13 02:38:30 -04:00
ButterscotchVanilla
c3fc5607ba Explain the editorconfig change in a comment 2021-10-13 01:57:07 -04:00
ButterscotchVanilla
a326d76f6a Add basic BVH file streamer 2021-10-13 01:57:07 -04:00
ButterscotchVanilla
472fcab821 Add a basic PoseStreamer implementation for streaming mocap data 2021-10-13 01:57:06 -04:00
ButterscotchVanilla
e7f81eb1aa Rename PoseFrame to PoseFrames 2021-10-13 01:57:06 -04:00
ButterscotchVanilla
31375855a0 Fix PoseRecorder frame timing 2021-10-13 01:56:04 -04:00
Eiren Rain
385890d0f0 Bundle LICENSE in the build 2021-10-05 14:58:15 +03:00
Eiren Rain
c9b4b5f18f Added explanation of licensing terms 2021-10-05 14:57:29 +03:00
Eiren Rain
c49af7fb33 Add discorvery port to firewall and add firewall uninstall script 2021-10-05 14:23:16 +03:00
Eiren Rain
4f042de2f4 Minor refactoring 2021-10-05 14:18:13 +03:00
Eiren Rain
f3e2b2ca40 Merge pull request #64 from SlimeVR/ipc-upgrade
IPC upgrade
2021-10-05 14:17:08 +03:00
Eiren Rain
a690447391 Fix thread safety 2021-10-05 14:15:47 +03:00
Eiren Rain
01593352ab Bump version to 0.1.0 2021-10-05 14:13:09 +03:00
Eiren Rain
0e4618529d Merge branch 'main' into ipc-upgrade 2021-10-05 14:11:04 +03:00
Eiren Rain
57c97cd5e1 Move eclipse project to java 11 2021-10-05 13:51:57 +03:00
Eiren Rain
8606c0daa3 Change how SteamVR trackers are selected 2021-10-05 13:51:44 +03:00
Eiren Rain
e94551d4f7 Merge pull request #63 from carl-anders/keyboard-keybindings
Keyboard keybindings: Actually do the correct type of reset
2021-10-03 21:26:33 +03:00
Carl Andersson
ffcd4f32ed Keyboard keybindings: Actually do the correct type of reset 2021-10-03 20:21:21 +02:00
Eiren Rain
2248f577df Merge pull request #62 from adigyran/main
Updated github CI to use 11th java
2021-10-03 18:33:01 +03:00
adigyran
8a57553986 updated gradle CI to use 11th java 2021-10-03 18:31:36 +03:00
Eiren Rain
bb01ce776b Merge branch 'main' into ipc-upgrade
# Conflicts:
#	src/main/java/io/eiren/vr/trackers/TrackersUDPServer.java
2021-10-03 13:18:14 +03:00
Eiren Rain
631870846c Fix classpath for new gradle build 2021-10-03 13:13:33 +03:00
Eiren Rain
a45abb7992 Update README.md 2021-10-02 19:48:54 +03:00
Eiren Rain
c7aaffa5e6 Merge pull request #58 from adigyran/main
slime commons as submodule
2021-10-02 19:46:34 +03:00
adigyran
7def0d0b4e gitlab ci with submodule 2021-10-02 19:44:14 +03:00
adigyran
c035135fb7 slime commons as submodule 2021-10-02 18:43:51 +03:00
Eiren Rain
15ffdeeeb8 Merge pull request #57 from carl-anders/keyboard-keybindings
Keyboard keybindings
2021-10-02 17:47:18 +03:00
Eiren Rain
74f6902a1b Fix NPE in WebSocket bridge 2021-10-02 17:45:56 +03:00
Carl Andersson
b2ae71333a Remove unused json dependency 2021-10-02 16:31:45 +02:00
Carl Andersson
fc88269f2d Add support for keyboard keybindings for reset and quick reset 2021-10-02 15:30:53 +02:00
Eiren Rain
a191fcf803 Bump version to Test 6 2021-10-01 17:39:20 +03:00
Eiren Rain
37b109bd73 Make UDP server support any number of sensors on a single tracker 2021-10-01 17:39:03 +03:00
Eiren Rain
27b2a77f48 Display descriptive tracker names instead of mac addresses in the GUI 2021-10-01 17:30:37 +03:00
Eiren Rain
0f34dd0967 Add back the bridge for SteamVR input, make it also reconnect automatically 2021-10-01 17:30:17 +03:00
Eiren Rain
10fc717500 Fix new bridge 2021-10-01 14:49:01 +03:00
Eiren Rain
250068c6c2 Reset trackers on bridge disconnect 2021-10-01 13:32:44 +03:00
Eiren Rain
488838752b Implement new named pipe bridge and test it lightly 2021-10-01 12:27:04 +03:00
Eiren Rain
dd0f4deae3 Merge pull request #54 from JimWails/main
Fix Spelling mistakes
2021-10-01 04:28:01 +03:00
JimWails
2df4106c92 Fix Spelling mistakes
Change upd:// to udp://
2021-09-30 23:48:42 +08:00
Eiren Rain
ed58076c68 Rework new bridge, don't use internal trackers
Update messages, added more enums and such; some refactoring
2021-09-30 14:42:37 +03:00
Eiren Rain
a4b300198d More work on IPC, minor bridges refactoring 2021-09-29 21:51:14 +03:00
Eiren Rain
6980023c5a Merge branch 'main' into ipc-upgrade 2021-09-29 20:37:35 +03:00
Eiren Rain
9f4d956345 Don't start SteamVR bridge not on Windows for now 2021-09-29 17:16:28 +03:00
Eiren Rain
ce4a90dc55 Early implementation of WebSocket VR Bridge 2021-09-24 01:53:10 +03:00
Eiren Rain
82ba193bb4 Minor GUI cleanup 2021-09-23 22:55:45 +03:00
Eiren Rain
a3a004536d Make GUI greatly less annoying and stretchy 2021-09-23 22:46:17 +03:00
Eiren Rain
bb1d7e06c2 Minor GUI update 2021-09-23 21:42:11 +03:00
Eiren Rain
3689e6723c IPC upgrade WIP 2021-09-23 21:18:42 +03:00
Eiren Rain
ef504c40b6 Use tracker mac address to save tracker configs 2021-09-22 22:37:45 +03:00
Eiren Rain
5e4a128d25 Update firewall script 2021-09-21 20:22:48 +03:00
Eiren Rain
67d93d87b5 Added protobuf and generated messages class 2021-09-18 23:44:51 +03:00
Eiren Rain
56b8b58606 Bump version to 0.0.19 2021-09-18 16:51:49 +03:00
Eiren Rain
97bc9343c1 Code formatting, move some packages 2021-09-18 16:50:54 +03:00
Eiren Rain
18cea30f72 Merge pull request #49 from ButterscotchVanilla/autobone-positions
AutoBone PoseFrame file format rework and other related fixes
2021-09-18 03:04:29 +03:00
Eiren Rain
d5c048600e Fix SteamVR input bridge not setting tracker status properly 2021-09-18 02:58:22 +03:00
ButterscotchVanilla
6d103d4ff9 Get all trackers directly in setPoseFromFrame 2021-09-17 19:18:24 -04:00
Eiren Rain
7008197760 Merge pull request #48 from kitlith/target_java8_release
Fixes some compatibility issues when compiling with Java9+ jdk.
2021-09-17 15:32:21 +03:00
Kitlith
da66f33edc Fixes some compatibility issues when compiling with Java9+ jdk.
i.e.:
java.lang.NoSuchMethodError:
java.nio.ByteBuffer.rewind()Ljava/nio/ByteBuffer;
2021-09-16 16:36:00 -07:00
ButterscotchVanilla
4109d1c825 Add pelvis averaging for SimpleSkeleton and fix neck rotation 2021-09-16 00:30:22 -04:00
ButterscotchVanilla
a300663a9e Spelling fix and check for null in TrackerUtils 2021-09-14 10:36:35 -04:00
ButterscotchVanilla
cb33dac3b9 Handle getRotation and getPosition responses properly 2021-09-14 09:14:16 -04:00
ButterscotchVanilla
582bac8050 Check recording for chest tracker when loading AutoBone configs 2021-09-14 09:08:22 -04:00
ButterscotchVanilla
5e1c45bc09 Record individual trackers with PoseFrame and optimize iterations 2021-09-14 08:50:08 -04:00
ButterscotchVanilla
b3073e6938 Handle busy status and add better exception messages 2021-09-14 04:20:43 -04:00
ButterscotchVanilla
63e259689f Handle computed trackers better and handle tracker status in data collection 2021-09-14 04:01:10 -04:00
ButterscotchVanilla
d92ea0a39e Small clean-up and ignore computed trackers in PoseFrame by default 2021-09-14 02:52:06 -04:00
ButterscotchVanilla
81bbb4008b Only allow loading tracker configs if the tracker is user editable 2021-09-14 01:45:35 -04:00
ButterscotchVanilla
45ad0698b1 Allow multiple TrackerFrames with the same designation in PoseFrame, make TrackerFrame extend Tracker 2021-09-14 01:06:36 -04:00
ButterscotchVanilla
bc542a7bb1 Don't put null designations in the trackers 2021-09-12 13:19:33 -04:00
ButterscotchVanilla
efb065f558 Fix TrackerBodyPosition.getByDesignation capitalization 2021-09-12 13:19:33 -04:00
ButterscotchVanilla
00e63db029 Use HashMap directly 2021-09-12 13:19:32 -04:00
ButterscotchVanilla
f6a2926033 Remove comment and useless if statement 2021-09-12 13:19:32 -04:00
ButterscotchVanilla
5b0f8afa4e Change namespaces, change PoseRecorder format, use TrackerBodyPosition for designation 2021-09-12 13:19:32 -04:00
Eiren Rain
c5b4421eae Fix tracker info not updating for IMU trackers 2021-09-12 19:58:03 +03:00
Eiren Rain
4d3f04e227 Bump version to 0.0.18 Test 3 2021-09-12 12:11:20 +03:00
Eiren Rain
75ad29a68d Can select role for SteamVR trackers
Trackers now have info if they report position or rotation
Extended pelvis model is always on
2021-09-12 12:10:59 +03:00
Eiren Rain
62e1e65dda Merge pull request #46 from ButterscotchVanilla/pelvis-fix
Fix leg averaging for pelvis and add waist tracker averaging
2021-09-12 12:00:11 +03:00
ButterscotchVanilla
02f64314b8 Fix leg averaging for pelvis and add waist tracker averaging 2021-09-12 04:49:54 -04:00
Eiren Rain
12d7f191ee Fix NPE, added bat scripts to the build 2021-09-04 09:25:16 +03:00
Eiren Rain
37135e1c8e Merge pull request #45 from ButterscotchVanilla/main
AutoBone: Move hardcoded values to variables
2021-09-03 06:54:33 +03:00
ButterscotchVanilla
85a0c25d0e AutoBone: Move hardcoded values to variables 2021-09-02 22:17:19 -04:00
Eiren Rain
1f081392df Always have a skeleton with legs, can work with any trackers, fill in empty trackers with static or previous 2021-09-02 11:41:54 +03:00
Eiren Rain
c02f9b827d Merge branch 'main' of https://github.com/SlimeVR/SlimeVR-Server into main 2021-09-02 11:33:46 +03:00
Eiren Rain
7e95c9f999 Remember window size and position between restarts
Added window and taskbar icons
2021-09-02 11:33:27 +03:00
Eiren Rain
4836b025e9 Merge pull request #41 from JimWails/main
Add support for ch910xx
2021-08-29 17:08:04 +03:00
JimWails
9a76838602 Add support for ch910xx
Already test on ESP8266 which use CH9102X driver
2021-08-28 22:27:30 +08:00
Eiren Rain
6c27186ce9 Make GUI less garbage (still gabage, but less) 2021-08-26 11:57:35 +03:00
Eiren Rain
74c25c2ca3 Don't use source port to id trackers 2021-08-23 16:39:43 +03:00
Eiren Rain
91ee6ff6c0 Merge pull request #35 from adigyran/patch-6
Update README.md
2021-08-22 15:59:17 +03:00
Yury
05ba866bef Update README.md
new build command
2021-08-22 15:55:38 +03:00
Eiren Rain
af3aab86dc Don't crash on pipe errors, just log them 2021-08-22 15:03:11 +03:00
Eiren Rain
4370defb69 Merge pull request #34 from adigyran/main
Gradle shadow plugin for dependency resolving
2021-08-22 14:50:20 +03:00
Eiren Rain
a105879c9a Supress config file not found error to create less confusion 2021-08-22 14:46:55 +03:00
Eiren Rain
9383be678c Don't parse some packets when paired with owoTrack #33 2021-08-22 14:46:35 +03:00
Eiren Rain
7c8a394147 Handle HMD pipe better 2021-08-22 14:43:27 +03:00
Eiren Rain
ffc8a9dae4 Remove new spine model from the main branch 2021-08-22 14:36:35 +03:00
adigyran
bb4a65882d Gradle shadow plugin. Based on Kitlith change of build.gradle. Using now library for Slime Commons dependency resolving. Changed gradle.yml accordingly. It produces slimevr.jar file 2021-08-22 13:44:26 +03:00
Yury
5ebbb907e7 Update gradle.yml
fix resolving
2021-08-22 13:39:20 +03:00
Yury
2ba66d7f91 Update .github/workflows/gradle.yml
Co-authored-by: Butterscotch! <bscotchvanilla@gmail.com>
2021-08-22 13:38:28 +03:00
Yury
7f8fe9e4f4 Update .github/workflows/gradle.yml
Co-authored-by: Butterscotch! <bscotchvanilla@gmail.com>
2021-08-22 13:38:22 +03:00
Yury
12292070ce Update .github/workflows/gradle.yml
Co-authored-by: Butterscotch! <bscotchvanilla@gmail.com>
2021-08-22 13:34:46 +03:00
adigyran
8bc2b72ab0 readme fix for new command 2021-08-22 13:32:20 +03:00
adigyran
208ae6b6d6 reformat code 2021-08-22 13:21:20 +03:00
adigyran
ba8121a8a7 Gradle shadow plugin. Based on Kitlith change of build.gradle. Using now library for Slime Commons dependency resolving. Changed gradle.yml accordingly. It produces slimevr.jar file 2021-08-22 13:15:22 +03:00
Eiren Rain
c8da0427f9 Added simple test extended spine model, SlimeVR/SlimeVR-Server#31 2021-08-20 17:05:15 +03:00
Eiren Rain
fed13e8fda Change name of steamvr trackers to legs instead of feet to make it less confusing 2021-08-20 15:32:53 +03:00
Eiren Rain
e3b977c636 Merge pull request #29 from ButterscotchVanilla/main
Add AutoBone, a skeleton auto-configuration algorithm
2021-08-20 15:30:33 +03:00
ButterscotchVanilla
337912f3d0 AutoBone: Optimize PoseRecorder tick 2021-08-19 22:46:41 -04:00
ButterscotchVanilla
3b61f88343 AutoBone: Add save recording button & enable buttons as needed 2021-08-19 18:14:57 -04:00
Eiren Rain
5f6a6ba1c5 Actually start steamvr input bridge 2021-08-20 00:11:25 +03:00
Eiren Rain
bb29844101 Recieve tracker role from SteamVR for input trackers 2021-08-19 23:03:07 +03:00
Eiren Rain
5600d95684 Add OCCLUDED status to the tracker status for future usage 2021-08-19 15:48:51 +03:00
Eiren Rain
45ba341ccf Added new pipe to read basic tracking data from feeder app 2021-08-19 15:43:49 +03:00
ButterscotchVanilla
7992526d2d AutoBone: Rename PoseRecordIO to PoseFrameIO and separate recording load into a method 2021-08-19 04:29:14 -04:00
ButterscotchVanilla
9a6cb23659 AutoBone: Add support for absolute positions 2021-08-19 04:11:23 -04:00
ButterscotchVanilla
bc132b7757 AutoBone: Thow NullPointerException for missing frames 2021-08-19 02:38:18 -04:00
ButterscotchVanilla
b05d726ad0 AutoBone: Return recording ASAP and check if it's empty 2021-08-19 01:54:28 -04:00
ButterscotchVanilla
a7a612aa9b AutoBone: Add recording cancellation, always check if the recording is done and not submitted 2021-08-19 00:52:09 -04:00
ButterscotchVanilla
32a29c8bc7 AutoBone: Add new dedicated AutoBone window 2021-08-18 23:59:12 -04:00
ButterscotchVanilla
23a3babf33 AutoBone: Fix recording 2021-08-17 16:28:13 -04:00
ButterscotchVanilla
3d90f0b284 AutoBone: Add proportion error 2021-08-17 04:12:21 -04:00
ButterscotchVanilla
1e6448c61f AutoBone: Let's pretend this didn't get committed 2021-08-17 00:10:08 -04:00
ButterscotchVanilla
a1f709ca12 AutoBone: Add unused configs to staticConfigs and split error function 2021-08-16 23:55:20 -04:00
ButterscotchVanilla
a8ca2fd6e6 AutoBone: Use abs dist for foot offset error, use total length again, and remove hips 2021-08-16 19:37:13 -04:00
ButterscotchVanilla
f835eeecdd AutoBone: Use error derivative and add more foot offsets 2021-08-16 18:14:17 -04:00
ButterscotchVanilla
70f5228d1c AutoBone: Remove head offset, remove totalLength 2021-08-16 18:14:17 -04:00
ButterscotchVanilla
89e2ea610a AutoBone: Automatically update node positions 2021-08-16 18:14:16 -04:00
ButterscotchVanilla
6b68a983a5 AutoBone: Restructure processFrames and remove unused code 2021-08-16 18:14:16 -04:00
ButterscotchVanilla
4a2878b92e AutoBone: Separate pose recorder from AutoBone & save multiple recordings 2021-08-16 18:14:16 -04:00
ButterscotchVanilla
4f8165c8e1 Set gradle compiler encoding to UTF-8 2021-08-16 18:14:15 -04:00
ButterscotchVanilla
855d15cec5 AutoBone: Fix configs not updating when AutoBone is run 2021-08-16 18:14:15 -04:00
ButterscotchVanilla
e1d17f61c4 AutoBone: Properly handle ratio output 2021-08-16 18:14:15 -04:00
ButterscotchVanilla
380ae27762 AutoBone: Support no chest tracker 2021-08-16 18:14:15 -04:00
ButterscotchVanilla
4775dcd57a AutoBone: Add more configs, fix recording reading 2021-08-16 18:14:14 -04:00
ButterscotchVanilla
807ccc69ce AutoBone: Print file name before processing frames 2021-08-16 18:14:14 -04:00
ButterscotchVanilla
aaee64ce02 AutoBone: Add stabilization, more fine-tuning as usual 2021-08-16 18:14:13 -04:00
ButterscotchVanilla
294141e223 AutoBone: Oops 2021-08-16 18:14:13 -04:00
ButterscotchVanilla
e3b125f244 AutoBone: Add bulk recording loading, add height diff stat 2021-08-16 18:14:13 -04:00
ButterscotchVanilla
7fd3297fed AutoBone: Fix error function, add error derivative, consider positive and negative equally, etc 2021-08-16 18:14:12 -04:00
ButterscotchVanilla
a2f54f67a3 AutoBone: Update GUI values after adjustment 2021-08-16 18:14:12 -04:00
Butterscotch!
d77724a911 Change CI to build on any branch 2021-08-16 18:14:12 -04:00
ButterscotchVanilla
1dc05ba196 AutoBone: Save configs without needing to have a skeleton 2021-08-16 18:14:11 -04:00
ButterscotchVanilla
cd7d4d102b AutoBone: Add config input for recording and adjustment values 2021-08-16 18:14:11 -04:00
ButterscotchVanilla
0ba2450152 AutoBone: Fine-tune chest-waist and leg-waist ratios 2021-08-16 18:14:10 -04:00
ButterscotchVanilla
eee7d67591 AutoBone: Allow manual target height value 2021-08-16 18:14:10 -04:00
ButterscotchVanilla
760dbfa5b9 AutoBone: Load multiple recordings, fine-tune values and extract ratios, fix restricted values from getting stuck 2021-08-16 18:14:10 -04:00
ButterscotchVanilla
a52384de2e AutoBone: This decreases error magically? Fine-tune leg to body ratio range 2021-08-16 18:14:09 -04:00
ButterscotchVanilla
0dab8f0c94 AutoBone: Fix grammar to be clearer 2021-08-16 18:14:09 -04:00
ButterscotchVanilla
629984c792 AutoBone: Remove feet from skeleton, read from AutoBone configs, and make skeletons local 2021-08-16 18:14:09 -04:00
ButterscotchVanilla
707e4c6dde AutoBone: Auto-detect height, add more restrains, fine-tuning adjustment values 2021-08-16 18:14:08 -04:00
ButterscotchVanilla
efbe409399 AutoBone: Only allow one AutoBone thread 2021-08-16 18:14:08 -04:00
ButterscotchVanilla
faf0be6c53 AutoBone: Fine-tune algorithm and error function, apply results to skeleton 2021-08-16 18:14:08 -04:00
ButterscotchVanilla
1a078993f3 AutoBone: Simplify length adjustment code 2021-08-16 18:14:07 -04:00
ButterscotchVanilla
e0ac3bb853 AutoBone: Make PoseRecordIO static and add height to algorithm error 2021-08-16 18:14:07 -04:00
ButterscotchVanilla
c6cd13d9cd AutoBone: Add data distance controls to control amount of context between poses 2021-08-16 18:14:07 -04:00
ButterscotchVanilla
ef88e2e4a9 AutoBone: Modify error function, add average error logs, and add tuning variables 2021-08-16 18:14:06 -04:00
ButterscotchVanilla
d9bcc39ee6 AutoBone: Disable feet 2021-08-16 18:14:06 -04:00
ButterscotchVanilla
84f4a47df1 AutoBone: Load recordings from "ABRecording_Load.abf" 2021-08-16 18:14:06 -04:00
ButterscotchVanilla
1408a5c357 AutoBone: Use skeleton properly and update on tick & add a timer for sampling 2021-08-16 18:14:05 -04:00
ButterscotchVanilla
202b15e8a8 Specify Java 8 compatibility 2021-08-16 18:14:05 -04:00
ButterscotchVanilla
110554a180 AutoBone: Add recording export to process 2021-08-16 18:14:04 -04:00
ButterscotchVanilla
90e3715426 AutoBone: Add serialization/deserialization of recording 2021-08-16 18:14:04 -04:00
ButterscotchVanilla
644fee2d1f AutoBone: Make auto-adjustment wait for recording to finish 2021-08-16 18:14:04 -04:00
ButterscotchVanilla
c163effe60 AutoBone: Add test button 2021-08-16 18:14:03 -04:00
ButterscotchVanilla
0a39c746a3 AutoBone: Add frame recording interval 2021-08-16 18:14:03 -04:00
ButterscotchVanilla
2f46b3ff58 AutoBone: Move configs to HashMap and finish implementing adjustment 2021-08-16 18:14:02 -04:00
ButterscotchVanilla
d35760d3a2 AutoBone: Add basic PoseFrame recording and start processing loop 2021-08-16 18:14:02 -04:00
ButterscotchVanilla
19a1101b43 AutoBone: Add AutoBone, PoseFrame, and finish implementing SimpleSkeleton 2021-08-16 18:14:01 -04:00
ButterscotchVanilla
8b209eaf27 AutoBone: Add node HashMap 2021-08-16 18:14:01 -04:00
ButterscotchVanilla
fc6f7d3004 AutoBone: Add config setting/saving 2021-08-16 18:14:01 -04:00
ButterscotchVanilla
1abab9f92d AutoBone: Add basic skeleton initialization 2021-08-16 18:14:01 -04:00
Eiren Rain
c3b50983e3 Make GUI updates less frequent and save some CPU usage 2021-08-14 18:48:45 +03:00
Eiren Rain
a0857090a0 Minor changes 2021-08-13 22:05:03 +03:00
Eiren Rain
1ce9be3ed3 Merge pull request #23 from ButterscotchVanilla/slime-ci-patch
Separate CI test and build into jobs
2021-08-12 12:18:58 +03:00
Butterscotch!
11d461380d Separate CI test and build into jobs 2021-08-12 05:14:16 -04:00
Eiren Rain
6c0eb07c0b Merge pull request #21 from ButterscotchVanilla/editorconfig
Create .editorconfig
2021-08-10 17:43:56 +03:00
ButterscotchVanilla
fb9ae3e78c Create .editorconfig 2021-08-10 10:38:58 -04:00
Eiren Rain
52f59fbfb3 Fixed typo 2021-08-10 16:51:42 +03:00
Eiren Rain
4a59017269 Added important comment 2021-08-10 09:20:12 +03:00
Eiren Rain
5c6d02de30 Parse handshake properly 2021-08-10 08:58:57 +03:00
Eiren Rain
83b0e78b9e Recieve correction and tap data, currently not used
Fix some typos
2021-08-10 08:31:03 +03:00
Eiren Rain
ac192e2416 Display magentometer calibration separately 2021-08-10 07:26:32 +03:00
Eiren Rain
52932d63d3 Display data from new protocol and firmware version, including calibration statis and magentometer correction 2021-08-09 17:46:00 +03:00
Eiren Rain
6a45e5d32c Another attempt at new knee model
Waist rotation now takes into account knee model
2021-08-08 08:04:28 +03:00
Eiren Rain
6f09598243 Added toggle for extended pelvis model
Extended aknle model is added but not used
Added 3-tracker rudementary support for people that want to hurt themselves
Fixed typos
2021-08-05 07:50:49 +03:00
Eiren Rain
467e79d1c0 Added Fast Reset button that resets only Yaw of the trackers 2021-08-04 07:48:54 +03:00
Eiren Rain
fa66c94ec3 Implemented new pelvis model as average between legs rotation
SlimeVR/SlimeVR-Server#9
2021-08-04 01:03:58 +03:00
Eiren Rain
2b4ce4b920 Fix version number 2021-08-02 23:08:22 +03:00
Eiren Rain
4e7585b87e Added support for different SteamVR trackers configuration 2021-08-02 23:07:25 +03:00
Eiren Rain
de13db4627 Fix right foot wasn't resetting 2021-07-31 03:05:08 +03:00
Eiren Rain
ca8ceb428b Set version to 0.0.12 2021-07-26 02:01:18 +03:00
Eiren Rain
c18597387a Merge pull request #20 from adigyran/patch-5
Update build.gradle
2021-07-26 02:00:25 +03:00
Yury
962504b788 Update build.gradle
useJUnitPlatform
2021-07-26 01:27:15 +03:00
Eiren Rain
8d1886d045 Display raw tracker data in degrees not in quats 2021-07-26 00:55:01 +03:00
Eiren Rain
1c5167bb7c Another tracker adjustment fix, doesn't pass all tests, but works better 2021-07-26 00:48:34 +03:00
Eiren Rain
e248cca4e7 Adjustmed trackers pass all tests 2021-07-25 23:04:34 +03:00
Eiren Rain
89ee97872d Streams go brrrr in unit tests 2021-07-25 22:45:54 +03:00
Eiren Rain
b22a2368d4 Refactor tests, generate tests for each angle dynamically, separate 3 test types 2021-07-25 22:19:04 +03:00
Eiren Rain
9ecfc57e44 Use JUnit 5 framework for testing 2021-07-25 20:57:11 +03:00
Eiren Rain
cd141258c5 Merge pull request #19 from adigyran/patch-4
Update build.gradle
2021-07-23 17:51:47 +03:00
Yury
5dc027a9e2 Update build.gradle
fix gradle dependencies
2021-07-23 17:46:29 +03:00
Eiren Rain
3e55b0e417 Merge pull request #18 from adigyran/patch-3
Update README.md
2021-07-22 12:36:20 +03:00
Eiren Rain
9ca6b21c61 Merge pull request #17 from ButterscotchVanilla/main
Automatically detect and set the Slime Java Commons subproject location
2021-07-22 12:36:01 +03:00
Yury
8ec528d4a0 Update README.md
formatting, thanks Butterscotch for some changes
2021-07-22 02:56:25 +03:00
Yury
961946bd29 Update README.md
formatting
2021-07-22 02:53:35 +03:00
Yury
da5fc860cf Update README.md
formatting
2021-07-22 02:50:54 +03:00
Yury
fdd39c4010 Update README.md
How to build instructions, this is for ButterscotchVanilla's PR  #17
2021-07-22 02:46:39 +03:00
ButterscotchVanilla
900e96a3a6 Announce subproject location before setting it 2021-07-21 17:39:45 -04:00
ButterscotchVanilla
6a9f42f126 Auto-detect Slime Java Commons subproject location 2021-07-21 17:35:09 -04:00
ButterscotchVanilla
72ea196359 Update gradle.yml 2021-07-21 17:11:01 -04:00
ButterscotchVanilla
90a8abeed2 Add comments to build.gradle and add path to subproject 2021-07-21 17:07:16 -04:00
Eiren Rain
34fcbfa96f Minor fixes 2021-07-21 22:21:39 +03:00
Eiren Rain
0f360cf892 WiFi window should be able to use CP2102 usb to uart too 2021-07-21 22:15:03 +03:00
Eiren Rain
22d4196bed Implement setting WiFi credentials via GUI 2021-07-21 22:06:35 +03:00
Eiren Rain
fb9860d51d Improve unit testing for adjusted trackers, not properly tests reference yaw != 0 2021-07-21 18:13:00 +03:00
Eiren Rain
c8ba9d62aa Added unit tests for adjusted trackers rotation, another rotation correction alghorithm
Added version number to the title to reduce confusion
2021-07-21 17:38:43 +03:00
Eiren Rain
4a66128af5 Merge pull request #15 from adigyran/patch-1
Update README.md
2021-07-20 15:54:27 +03:00
Yury
f061410205 Update README.md 2021-07-20 15:51:53 +03:00
Eiren Rain
1250e77771 More correct (but still not completely correct) tracker adjustments 2021-07-19 18:35:08 +03:00
Eiren Rain
a20334d026 Fixed trackers ping not being displayed 2021-07-13 17:25:15 +03:00
Eiren Rain
c2b4d30047 Removed leg yaw averaging, it was causing leg gimbal lock 2021-07-08 16:46:55 +03:00
Eiren Rain
755ab592f1 Rework legs proportions to legs length and knee height 2021-07-08 06:36:23 +03:00
Eiren Rain
9e0147ed27 Implemented body proportions reset buttons to reset things to their defaults based on user's height 2021-07-08 06:16:02 +03:00
Eiren Rain
0ab92322a8 Minor GUI fix 2021-07-08 06:15:21 +03:00
Eiren Rain
4e58df76fb Merge pull request #6 from ButterscotchVanilla/main
Add Slime Java Commons to Gradle build and create a GitHub Actions workflow
2021-07-08 02:50:33 +03:00
Butterscotch!
d3f81625ce Add testing to workflow 2021-07-07 19:39:08 -04:00
Butterscotch!
b97a92b682 Create GitHub Actions build script (#1)
* Create gradle.yml
2021-07-07 19:29:54 -04:00
ButterscotchVanilla
5b2918acb2 Build dependencies when packaging a server jar 2021-07-07 19:19:46 -04:00
ButterscotchVanilla
cbf37a7c9c Add Slime Java Commons as a dependency and add runnable jar task 2021-07-07 18:48:24 -04:00
Eiren Rain
f169cfd0c7 Merge pull request #5 from ButterscotchVanilla/main
Retain initial font size while zooming and display zoom level
2021-07-08 00:39:33 +03:00
ButterscotchVanilla
0e51b79775 Fix formatting 2021-07-07 10:27:42 -04:00
ButterscotchVanilla
1ee13c02d9 Retain initial font size while zooming and display zoom level 2021-07-07 10:19:55 -04:00
327 changed files with 62291 additions and 4099 deletions

View File

@@ -1,33 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="bin/main" path="src/main/java">
<attributes>
<attribute name="gradle_scope" value="main"/>
<attribute name="gradle_used_by_scope" value="main,test"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="bin/main" path="src/main/resources">
<attributes>
<attribute name="gradle_scope" value="main"/>
<attribute name="gradle_used_by_scope" value="main,test"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="bin/test" path="src/test/java">
<attributes>
<attribute name="gradle_scope" value="test"/>
<attribute name="gradle_used_by_scope" value="test"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="bin/test" path="src/test/resources">
<attributes>
<attribute name="gradle_scope" value="test"/>
<attribute name="gradle_used_by_scope" value="test"/>
<attribute name="test" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8/"/>
<classpathentry kind="con" path="org.eclipse.buildship.core.gradleclasspathcontainer"/>
<classpathentry combineaccessrules="false" kind="src" path="/Slime Java Commons"/>
<classpathentry kind="output" path="bin/default"/>
</classpath>

12
.gitattributes vendored
View File

@@ -1,6 +1,6 @@
#
# https://help.github.com/articles/dealing-with-line-endings/
#
# These are explicitly windows files and should use crlf
*.bat text eol=crlf
#
# https://help.github.com/articles/dealing-with-line-endings/
#
# These are explicitly windows files and should use crlf
*.bat text eol=crlf

61
.github/workflows/gradle.yaml vendored Normal file
View File

@@ -0,0 +1,61 @@
# This workflow will build a Java project with Gradle
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-gradle
name: SlimeVR Server
on: [push, pull_request]
jobs:
test:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: "17"
distribution: "adopt"
- name: Setup Gradle
uses: gradle/gradle-build-action@v2
- name: Check code formatting
run: ./gradlew spotlessCheck
- name: Test with Gradle
run: ./gradlew test
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
submodules: recursive
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
java-version: "17"
distribution: "adopt"
- name: Setup Gradle
uses: gradle/gradle-build-action@v2
- name: Build with Gradle
run: ./gradlew shadowJar
- name: Upload the Server JAR as a Build Artifact
uses: actions/upload-artifact@v3
with:
# Artifact name
name: "SlimeVR-Server" # optional, default is artifact
# A file, directory or wildcard pattern that describes what to upload
path: build/libs/*

28
.gitignore vendored
View File

@@ -6,7 +6,33 @@ build
/bin/
# Ignore .idea
.idea
# Syncthing ignore file
.stignore
MagnetoLib.dll
MagnetoLib.dll
vrconfig.yml
# BVH
BVH Recordings
# AutoBone
Recordings
AutoBone Recordings
Load AutoBone Recordings
# Logs
*.log.*
*.log.lck
*.log
logs/
# Ignore eclipse stuff
.project
.classpath
.settings
# VSCode stuff
/.vscode/settings.json

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "solarxr-protocol"]
path = solarxr-protocol
url = https://github.com/SlimeVR/SolarXR-Protocol.git

View File

@@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>SlimeVR Server</name>
<comment>SlimeVR Server</comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.buildship.core.gradleprojectbuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.buildship.core.gradleprojectnature</nature>
</natures>
</projectDescription>

View File

@@ -1,2 +0,0 @@
connection.project.dir=
eclipse.preferences.version=1

View File

@@ -1,2 +0,0 @@
eclipse.preferences.version=1
encoding/<project>=UTF-8

View File

@@ -1,2 +0,0 @@
eclipse.preferences.version=1
line.separator=\r\n

View File

@@ -1,416 +0,0 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=enabled
org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore
org.eclipse.jdt.core.compiler.annotation.nonnull=org.eclipse.jdt.annotation.NonNull
org.eclipse.jdt.core.compiler.annotation.nonnull.secondary=
org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault
org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary=
org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nullable
org.eclipse.jdt.core.compiler.annotation.nullable.secondary=
org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.APILeak=warning
org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
org.eclipse.jdt.core.compiler.problem.deadCode=ignore
org.eclipse.jdt.core.compiler.problem.deprecation=warning
org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=disabled
org.eclipse.jdt.core.compiler.problem.discouragedReference=info
org.eclipse.jdt.core.compiler.problem.emptyStatement=warning
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.explicitlyClosedAutoCloseable=ignore
org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning
org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled
org.eclipse.jdt.core.compiler.problem.fieldHiding=warning
org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning
org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning
org.eclipse.jdt.core.compiler.problem.forbiddenReference=info
org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning
org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=enabled
org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning
org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=ignore
org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=warning
org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore
org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning
org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore
org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=error
org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled
org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=warning
org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=error
org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled
org.eclipse.jdt.core.compiler.problem.missingSerialVersion=ignore
org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=error
org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning
org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning
org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore
org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning
org.eclipse.jdt.core.compiler.problem.nonnullTypeVariableFromLegacyInvocation=warning
org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=error
org.eclipse.jdt.core.compiler.problem.nullReference=error
org.eclipse.jdt.core.compiler.problem.nullSpecViolation=error
org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning
org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning
org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore
org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning
org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=warning
org.eclipse.jdt.core.compiler.problem.potentialNullReference=ignore
org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore
org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning
org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning
org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning
org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore
org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore
org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore
org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore
org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled
org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning
org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled
org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled
org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=enabled
org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore
org.eclipse.jdt.core.compiler.problem.terminalDeprecation=warning
org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning
org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled
org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning
org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning
org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore
org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning
org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentType=warning
org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentTypeStrict=disabled
org.eclipse.jdt.core.compiler.problem.unlikelyEqualsArgumentType=info
org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore
org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore
org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled
org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled
org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedImport=warning
org.eclipse.jdt.core.compiler.problem.unusedLabel=warning
org.eclipse.jdt.core.compiler.problem.unusedLocal=warning
org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=warning
org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled
org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled
org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled
org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
org.eclipse.jdt.core.compiler.source=1.8
org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=0
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=48
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=0
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=0
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=0
org.eclipse.jdt.core.formatter.alignment_for_assignment=0
org.eclipse.jdt.core.formatter.alignment_for_binary_expression=0
org.eclipse.jdt.core.formatter.alignment_for_compact_if=0
org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=0
org.eclipse.jdt.core.formatter.alignment_for_enum_constants=48
org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=0
org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0
org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0
org.eclipse.jdt.core.formatter.alignment_for_module_statements=16
org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16
org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=0
org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=0
org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80
org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=0
org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=0
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=48
org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=0
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=0
org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=0
org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0
org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0
org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16
org.eclipse.jdt.core.formatter.blank_lines_after_imports=1
org.eclipse.jdt.core.formatter.blank_lines_after_package=1
org.eclipse.jdt.core.formatter.blank_lines_before_field=0
org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0
org.eclipse.jdt.core.formatter.blank_lines_before_imports=1
org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1
org.eclipse.jdt.core.formatter.blank_lines_before_method=1
org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1
org.eclipse.jdt.core.formatter.blank_lines_before_package=0
org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1
org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1
org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line
org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line
org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false
org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false
org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=false
org.eclipse.jdt.core.formatter.comment.format_block_comments=false
org.eclipse.jdt.core.formatter.comment.format_header=false
org.eclipse.jdt.core.formatter.comment.format_html=true
org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=false
org.eclipse.jdt.core.formatter.comment.format_line_comments=false
org.eclipse.jdt.core.formatter.comment.format_source_code=true
org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true
org.eclipse.jdt.core.formatter.comment.indent_root_tags=true
org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert
org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert
org.eclipse.jdt.core.formatter.comment.line_length=80
org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true
org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true
org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false
org.eclipse.jdt.core.formatter.compact_else_if=true
org.eclipse.jdt.core.formatter.continuation_indentation=2
org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2
org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off
org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on
org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false
org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true
org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true
org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true
org.eclipse.jdt.core.formatter.indent_empty_lines=true
org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true
org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
org.eclipse.jdt.core.formatter.indentation.size=4
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert
org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert
org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert
org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert
org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert
org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert
org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert
org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert
org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert
org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert
org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert
org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert
org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert
org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert
org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert
org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert
org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert
org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert
org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert
org.eclipse.jdt.core.formatter.join_lines_in_comments=true
org.eclipse.jdt.core.formatter.join_wrapped_lines=false
org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false
org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false
org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false
org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false
org.eclipse.jdt.core.formatter.lineSplit=200
org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false
org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false
org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0
org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1
org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines
org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines
org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines
org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines
org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines
org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines
org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines
org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines
org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines
org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines
org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true
org.eclipse.jdt.core.formatter.tabulation.char=tab
org.eclipse.jdt.core.formatter.tabulation.size=4
org.eclipse.jdt.core.formatter.use_on_off_tags=true
org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false
org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false
org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true
org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true
org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true
org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true
org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter

View File

@@ -1,3 +0,0 @@
eclipse.preferences.version=1
formatter_profile=_Essentia
formatter_settings_version=13

11
.vscode/extensions.json vendored Normal file
View File

@@ -0,0 +1,11 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
// List of extensions which should be recommended for users of this workspace.
"recommendations": [
"richardwillis.vscode-spotless-gradle",
"gaborv.flatbuffers",
],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": []
}

75
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,75 @@
# Contributing to SlimeVR
This document describes essential knowledge for contributors to SlimeVR.
## How to get started
### Getting the code
First, clone the codebase using `git`. If you don't have `git` installed, go install it.
```bash
# Clone repositories
git clone --recursive https://github.com/SlimeVR/SlimeVR-Server.git
# Enter the directory of the codebase
cd SlimeVR-Server
```
Now you can open the codebase in your favorite IDE or text editor.
### Building the code
The code is built with `gradle`, a cli tool that manages java projects and their
dependencies. You can build the code with `./gradlew build` and run it with
`./gradlew run`.
## Code Style
Code is autoformatted with [spotless](https://github.com/diffplug/spotless/tree/main/plugin-gradle).
Code is checked for autoformatting whenever you build, but you can also run
`./gradlew spotlessCheck` if you prefer.
To autoformat your code from the command line, you can run `./gradlew spotlessApply`.
We recommend installing support for spotless in your IDE of choice, and formatting
whenever you save a file, to make things easy.
If you need to prevent autoformatting for a particular region of code, use
`// @formatter:off` and `// @formatter:on`
### Setting up spotless in VSCode
* Install the `richardwillis.vscode-spotless-gradle` extension
* Add the following to your workspace settings, at `.vscode/settings.json`:
```json
"spotlessGradle.format.enable": true,
"editor.formatOnSave": true,
"[java]": {
"editor.defaultFormatter": "richardwillis.vscode-spotless-gradle"
}
```
### Setting up spotless for IntelliJ
* Install https://plugins.jetbrains.com/plugin/18321-spotless-gradle.
* Add a keyboard shortcut for `Code` > `Reformat Code with Spotless`
* They are working on support to do this on save without a keybind
[here](https://github.com/ragurney/spotless-intellij-gradle/issues/8)
### Setting up Eclipse autoformatting
Import the formatting settings defined in `spotless.xml`, like this:
* Go to `File > Properties`, then `Java Code Style > Formatter`
* Check `Enable project specific settings`
* Click `Import`, then open `spotless.xml`, then `Apply`
* Go to `Java Editor > Save Actions`
* Select `Enable project specific settings`, `Perform the selected actions on save`,
`Format source code`, `Format all lines`
Eclipse will only do a subset of the checks in `spotless`, so you may still want to do
`./gradlew spotlessApply` if you ever see an error from spotless.
## Code Licensing
SlimeVR uses an MIT license, and some parts of the project use a dual MIT/Apache 2.0
license. Be sure that any code that you reference, or dependencies you add, are
compatible with these licenses. `GPL-v3` for example is not compatible because it
requires any and all code that depends on it to *also* be licensed under `GPL-v3`.
## Discord
We use discord *a lot* to coordinate and discuss development. Come join us at
https://discord.gg/SlimeVR!

42
LICENSE
View File

@@ -1,21 +1,21 @@
MIT License
Copyright (c) 2021 Eiren Rain
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
MIT License
Copyright (c) 2021 Eiren Rain, SlimeVR
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -5,7 +5,7 @@ Server orchestrates communication between multiple sensors and integrations, lik
Sensors implementations:
* [SlimeVR Tracker for ESP](https://github.com/SlimeVR/SlimeVR-Tracker-ESP) - ESP microcontrollers and multiple IMUs are supported
* [owoTrack Mobile App](https://github.com/abb128/owoTrackVRSyncMobile) - use phone as a tracker (limited functionality and copmatibility)
* [owoTrack Mobile App](https://github.com/abb128/owoTrackVRSyncMobile) - use phone as a tracker (limited functionality and compatibility)
Integrations:
* Use [SlimeVR OpenVR Driver](https://github.com/SlimeVR/SlimeVR-OpenVR-Driver) as a driver for SteamVR
@@ -13,4 +13,22 @@ Integrations:
## How to use
Latest instructions are currently [here](https://gist.github.com/Eirenliel/8c0eefcdbda1076d5c2e1bf634831d20). Will be updated and republished as time goes on.
It's recommended to download installer from here: https://github.com/SlimeVR/SlimeVR-Installer/releases/latest/download/slimevr_web_installer.exe
Latest instructions are [on our site](https://docs.slimevr.dev/server-setup/slimevr-setup.html).
## License Clarification
**SlimeVR software** (including server, firmware, drivers, installator, documents, and others - see licence for each case specifically) **is distributed under the [MIT License](https://github.com/SlimeVR/SlimeVR-Server/blob/main/LICENSE) and is copyright of Eiren Rain and SlimeVR.** The MIT Licence is a permissive license giving you rights to modify and distribute the software with little strings attached.
**However, the MIT License has some limits, and if you wish to distribute software based on SlimeVR, you need to be aware of them:**
* When distributing any software that uses or is based on SlimeVR, you have to provide to the end-user the original, unmodified `LICENSE` file from SlimeVR. This file is located [here](https://github.com/SlimeVR/SlimeVR-Server/blob/main/LICENSE). This includes the `Copyright (c) 2021 Eiren Rain, SlimeVR` part of the license. It is not sufficient to use a generic MIT License, it must be the original license file.
* This applies even if you distribute software without the source code. In this case, one way to provide it to the end-user is to have a menu in your application that lists all the open source licenses used, including SlimeVR's.
Please refer to the [LICENSE](https://github.com/SlimeVR/SlimeVR-Server/blob/main/LICENSE) file if you are at any point uncertain what the exact the requirements are.
## Contributions
By contributing to this project you are placing all your code under MIT or less restricting licenses, and you certify that the code you have used is compatible with those licenses or is authored by you. If you're doing so on your work time, you certify that your employer is okay with this.
For a how-to on contributing, see [CONTRIBUTING.md](CONTRIBUTING.md).

View File

@@ -7,29 +7,103 @@
*/
plugins {
// Apply the java-library plugin to add support for Java Library
id 'java-library'
id 'application'
id "com.github.johnrengelman.shadow" version "7.1.2"
id "com.diffplug.spotless" version "6.5.1"
}
repositories {
// Use jcenter for resolving dependencies.
// You can declare any Maven/Ivy/file repository here.
jcenter()
mavenCentral()
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
// Set compiler to use UTF-8
compileJava.options.encoding = 'UTF-8'
compileTestJava.options.encoding = 'UTF-8'
javadoc.options.encoding = 'UTF-8'
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
// if (JavaVersion.current().isJava9Compatible()) {
// options.release = 8
// }
}
tasks.withType(Test) {
systemProperty('file.encoding', 'UTF-8')
}
tasks.withType(Javadoc) {
options.encoding = 'UTF-8'
}
allprojects {
repositories {
// Use jcenter for resolving dependencies.
// You can declare any Maven/Ivy/file repository here.
mavenCentral()
}
}
dependencies {
// This dependency is exported to consumers, that is to say found on their compile classpath.
api 'org.apache.commons:commons-math3:3.6.1'
api 'org.yaml:snakeyaml:1.25'
api 'net.java.dev.jna:jna:5.6.0'
api 'net.java.dev.jna:jna-platform:5.6.0'
api 'com.illposed.osc:javaosc-core:0.8'
implementation project(":solarxr-protocol")
// This dependency is used internally, and not exposed to consumers on their own compile classpath.
implementation 'com.google.guava:guava:28.2-jre'
// Use JUnit test framework
testImplementation 'junit:junit:4.12'
// This dependency is used internally, and not exposed to consumers on their own compile classpath.
implementation group: 'com.google.flatbuffers', name: 'flatbuffers-java', version: '2.0.3'
implementation group: 'commons-cli', name: 'commons-cli', version: '1.3.1'
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.12.6.1'
implementation group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-yaml', version: '2.13.3'
implementation 'com.github.jonpeterson:jackson-module-model-versioning:1.2.2'
implementation 'org.apache.commons:commons-math3:3.6.1'
implementation 'org.apache.commons:commons-lang3:3.12.0'
implementation 'net.java.dev.jna:jna:5.+'
implementation 'net.java.dev.jna:jna-platform:5.+'
implementation 'com.illposed.osc:javaosc-core:0.8'
implementation 'com.fazecast:jSerialComm:2.+'
implementation 'com.google.protobuf:protobuf-java:3.+'
implementation 'org.java-websocket:Java-WebSocket:1.+'
implementation 'com.melloware:jintellitype:1.+'
// Use JUnit test framework
testImplementation platform('org.junit:junit-bom:5.9.0')
testImplementation 'org.junit.jupiter:junit-jupiter'
testImplementation 'org.junit.platform:junit-platform-launcher'
}
test {
useJUnitPlatform()
}
shadowJar {
archiveBaseName.set('slimevr')
archiveClassifier.set('')
archiveVersion.set('')
}
application {
getMainClass().set('dev.slimevr.Main')
}
spotless {
// optional: limit format enforcement to just the files changed by this feature branch
// ratchetFrom 'origin/main'
format 'misc', {
// define the files to apply `misc` to
target '*.gradle', '*.md', '.gitignore'
// define the steps to apply to those files
trimTrailingWhitespace()
endWithNewline()
indentWithTabs()
}
// format 'yaml', {
// target '*.yml', '*.yaml',
// trimTrailingWhitespace()
// endWithNewline()
// indentWithSpaces(2) // YAML cannot contain tabs: https://yaml.org/faq.html
// }
java {
removeUnusedImports()
// Use eclipse JDT formatter
eclipse().configFile("spotless.xml")
}
}

6
gradle.properties Normal file
View File

@@ -0,0 +1,6 @@
# Fixes bug with spotless. See https://github.com/diffplug/spotless/issues/834#issuecomment-819118761
org.gradle.jvmargs=--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \
--add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \
--add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \
--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \
--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED

Binary file not shown.

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

269
gradlew vendored Normal file → Executable file
View File

@@ -1,7 +1,7 @@
#!/usr/bin/env sh
#!/bin/sh
#
# Copyright 2015 the original author or authors.
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -17,78 +17,113 @@
#
##############################################################################
##
## Gradle start up script for UN*X
##
#
# Gradle start up script for POSIX generated by Gradle.
#
# Important for running:
#
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
# noncompliant, but you have some other compliant shell such as ksh or
# bash, then to run this script, type that shell name before the whole
# command line, like:
#
# ksh Gradle
#
# Busybox and similar reduced shells will NOT work, because this script
# requires all of these POSIX shell features:
# * functions;
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
# * compound commands having a testable exit status, especially «case»;
# * various built-in commands including «command», «set», and «ulimit».
#
# Important for patching:
#
# (2) This script targets any POSIX shell, so it avoids extensions provided
# by Bash, Ksh, etc; in particular arrays are avoided.
#
# The "traditional" practice of packing multiple parameters into a
# space-separated string is a well documented source of bugs and security
# problems, so this is (mostly) avoided, by progressively accumulating
# options in "$@", and eventually passing that to Java.
#
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
# see the in-line comments for details.
#
# There are tweaks for specific operating systems such as AIX, CygWin,
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
#
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
app_path=$0
# Need this for daisy-chained symlinks.
while
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
[ -h "$app_path" ]
do
ls=$( ls -ld "$app_path" )
link=${ls#*' -> '}
case $link in #(
/*) app_path=$link ;; #(
*) app_path=$APP_HOME$link ;;
esac
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
APP_BASE_NAME=${0##*/}
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
MAX_FD=maximum
warn () {
echo "$*"
}
} >&2
die () {
echo
echo "$*"
echo
exit 1
}
} >&2
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
case "$( uname )" in #(
CYGWIN* ) cygwin=true ;; #(
Darwin* ) darwin=true ;; #(
MSYS* | MINGW* ) msys=true ;; #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
JAVACMD=$JAVA_HOME/jre/sh/java
else
JAVACMD="$JAVA_HOME/bin/java"
JAVACMD=$JAVA_HOME/bin/java
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
@@ -97,7 +132,7 @@ Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
JAVACMD=java
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
@@ -105,79 +140,95 @@ location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, stacking in reverse order:
# * args from the command line
# * the main class name
# * -classpath
# * -D...appname settings
# * --module-path (only if needed)
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
# Now convert the arguments - kludge to limit ourselves to /bin/sh
for arg do
if
case $arg in #(
-*) false ;; # don't mess with options #(
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
[ -e "$t" ] ;; #(
*) false ;;
esac
then
arg=$( cygpath --path --ignore --mixed "$arg" )
fi
# Roll the args list around exactly as many times as the number of
# args, so each arg winds up back in the position where it started, but
# possibly modified.
#
# NB: a `for` loop captures its iteration list before it begins, so
# changing the positional parameters here affects neither the number of
# iterations, nor the values presented in `arg`.
shift # remove old arg
set -- "$@" "$arg" # push replacement arg
done
fi
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
# double quotes to make sure that they get re-expanded; and
# * put everything else in single quotes, so that it's not re-expanded.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Use "xargs" to parse quoted args.
#
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
#
# In Bash we could simply go:
#
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
# set -- "${ARGS[@]}" "$@"
#
# but POSIX shell has neither arrays nor command substitution, so instead we
# post-process each arg (as a line of input to sed) to backslash-escape any
# character that might be a shell metacharacter, then use eval to reverse
# that process (while maintaining the separation between arguments), and wrap
# the whole thing up as a single "set" statement.
#
# This will of course break if any of these variables contains a newline or
# an unmatched quote.
#
eval "set -- $(
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
xargs -n1 |
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
tr '\n' ' '
)" '"$@"'
exec "$JAVACMD" "$@"

22
gradlew.bat vendored
View File

@@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -54,7 +54,7 @@ goto fail
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
@@ -64,28 +64,14 @@ echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windows variants
if not "%OS%" == "Windows_NT" goto win9xME_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell

View File

@@ -0,0 +1,665 @@
/*
* Copyright (c) 2009-2012 jMonkeyEngine 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 'jMonkeyEngine' 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.
*/
package com.jme3.math;
import io.eiren.math.FloatMath;
/**
* <code>ColorRGBA</code> defines a color made from a collection of red, green
* and blue values. An alpha value determines is transparency. All values must
* be between 0 and 1. If any value is set higher or lower than these
* constraints they are clamped to the min or max. That is, if a value smaller
* than zero is set the value clamps to zero. If a value higher than 1 is
* passed, that value is clamped to 1. However, because the attributes r, g, b,
* a are public for efficiency reasons, they can be directly modified with
* invalid values. The client should take care when directly addressing the
* values. A call to clamp will assure that the values are within the
* constraints.
*
* @author Mark Powell
* @version $Id: ColorRGBA.java,v 1.29 2007/09/09 18:25:14 irrisor Exp $
*/
public final class ColorRGBA implements Cloneable, java.io.Serializable {
static final long serialVersionUID = 1;
/**
* The color black (0,0,0).
*/
public static final ColorRGBA Black = new ColorRGBA(0f, 0f, 0f, 1f);
/**
* The color white (1,1,1).
*/
public static final ColorRGBA White = new ColorRGBA(1f, 1f, 1f, 1f);
/**
* The color gray (.2,.2,.2).
*/
public static final ColorRGBA DarkGray = new ColorRGBA(0.2f, 0.2f, 0.2f, 1.0f);
/**
* The color gray (.5,.5,.5).
*/
public static final ColorRGBA Gray = new ColorRGBA(0.5f, 0.5f, 0.5f, 1.0f);
/**
* The color gray (.8,.8,.8).
*/
public static final ColorRGBA LightGray = new ColorRGBA(0.8f, 0.8f, 0.8f, 1.0f);
/**
* The color red (1,0,0).
*/
public static final ColorRGBA Red = new ColorRGBA(1f, 0f, 0f, 1f);
/**
* The color green (0,1,0).
*/
public static final ColorRGBA Green = new ColorRGBA(0f, 1f, 0f, 1f);
/**
* The color blue (0,0,1).
*/
public static final ColorRGBA Blue = new ColorRGBA(0f, 0f, 1f, 1f);
/**
* The color yellow (1,1,0).
*/
public static final ColorRGBA Yellow = new ColorRGBA(1f, 1f, 0f, 1f);
/**
* The color magenta (1,0,1).
*/
public static final ColorRGBA Magenta = new ColorRGBA(1f, 0f, 1f, 1f);
/**
* The color cyan (0,1,1).
*/
public static final ColorRGBA Cyan = new ColorRGBA(0f, 1f, 1f, 1f);
/**
* The color orange (251/255, 130/255,0).
*/
public static final ColorRGBA Orange = new ColorRGBA(251f / 255f, 130f / 255f, 0f, 1f);
/**
* The color brown (65/255, 40/255, 25/255).
*/
public static final ColorRGBA Brown = new ColorRGBA(65f / 255f, 40f / 255f, 25f / 255f, 1f);
/**
* The color pink (1, 0.68, 0.68).
*/
public static final ColorRGBA Pink = new ColorRGBA(1f, 0.68f, 0.68f, 1f);
/**
* The black color with no alpha (0, 0, 0, 0).
*/
public static final ColorRGBA BlackNoAlpha = new ColorRGBA(0f, 0f, 0f, 0f);
/**
* The red component of the color. 0 is none and 1 is maximum red.
*/
public float r;
/**
* The green component of the color. 0 is none and 1 is maximum green.
*/
public float g;
/**
* The blue component of the color. 0 is none and 1 is maximum blue.
*/
public float b;
/**
* The alpha component of the color. 0 is transparent and 1 is opaque.
*/
public float a;
/**
* Constructor instantiates a new <code>ColorRGBA</code> object. This color
* is the default "white" with all values 1.
*/
public ColorRGBA() {
r = g = b = a = 1.0f;
}
/**
* Constructor instantiates a new <code>ColorRGBA</code> object. The values
* are defined as passed parameters. These values are then clamped to insure
* that they are between 0 and 1.
*
* @param r The red component of this color.
* @param g The green component of this <code>ColorRGBA</code>.
* @param b The blue component of this <code>ColorRGBA</code>.
* @param a The alpha component of this <code>ColorRGBA</code>.
*/
public ColorRGBA(float r, float g, float b, float a) {
this.r = r;
this.g = g;
this.b = b;
this.a = a;
}
/**
* Copy constructor creates a new <code>ColorRGBA</code> object, based on a
* provided color.
*
* @param rgba The <code>ColorRGBA</code> object to copy.
*/
public ColorRGBA(ColorRGBA rgba) {
this.a = rgba.a;
this.r = rgba.r;
this.g = rgba.g;
this.b = rgba.b;
}
/**
* <code>set</code> sets the RGBA values of this <code>ColorRGBA</code>. The
* values are then clamped to insure that they are between 0 and 1.
*
* @param r The red component of this color.
* @param g The green component of this color.
* @param b The blue component of this color.
* @param a The alpha component of this color.
* @return this
*/
public ColorRGBA set(float r, float g, float b, float a) {
this.r = r;
this.g = g;
this.b = b;
this.a = a;
return this;
}
/**
* <code>set</code> sets the values of this <code>ColorRGBA</code> to those
* set by a parameter color.
*
* @param rgba The color to set this <code>ColorRGBA</code> to.
* @return this
*/
public ColorRGBA set(ColorRGBA rgba) {
if (rgba == null) {
r = 0;
g = 0;
b = 0;
a = 0;
} else {
r = rgba.r;
g = rgba.g;
b = rgba.b;
a = rgba.a;
}
return this;
}
public ColorRGBA setR(float r) {
this.r = r;
return this;
}
public ColorRGBA setG(float g) {
this.g = g;
return this;
}
public ColorRGBA setB(float b) {
this.b = b;
return this;
}
public ColorRGBA setA(float a) {
this.a = a;
return this;
}
/**
* <code>clamp</code> insures that all values are between 0 and 1. If any
* are less than 0 they are set to zero. If any are more than 1 they are set
* to one.
*/
public void clamp() {
if (r < 0) {
r = 0;
} else if (r > 1) {
r = 1;
}
if (g < 0) {
g = 0;
} else if (g > 1) {
g = 1;
}
if (b < 0) {
b = 0;
} else if (b > 1) {
b = 1;
}
if (a < 0) {
a = 0;
} else if (a > 1) {
a = 1;
}
}
/**
* <code>getColorArray</code> retrieves the color values of this
* <code>ColorRGBA</code> as a four element <code>float</code> array in the
* order: r,g,b,a.
*
* @return The <code>float</code> array that contains the color components.
*/
public float[] getColorArray() {
return new float[] { r, g, b, a };
}
/**
* Stores the current r,g,b,a values into the given array. The given array
* must have a length of 4 or greater, or an array index out of bounds
* exception will be thrown.
*
* @param store The <code>float</code> array to store the values into.
* @return The <code>float</code> array after storage.
*/
public float[] getColorArray(float[] store) {
store[0] = r;
store[1] = g;
store[2] = b;
store[3] = a;
return store;
}
/**
* Retrieves the alpha component value of this <code>ColorRGBA</code>.
*
* @return The alpha component value.
*/
public float getAlpha() {
return a;
}
/**
* Retrieves the red component value of this <code>ColorRGBA</code>.
*
* @return The red component value.
*/
public float getRed() {
return r;
}
/**
* Retrieves the blue component value of this <code>ColorRGBA</code>.
*
* @return The blue component value.
*/
public float getBlue() {
return b;
}
/**
* Retrieves the green component value of this <code>ColorRGBA</code>.
*
* @return The green component value.
*/
public float getGreen() {
return g;
}
/**
* Sets this <code>ColorRGBA</code> to the interpolation by changeAmnt from
* this to the finalColor: this=(1-changeAmnt)*this + changeAmnt *
* finalColor
*
* @param finalColor The final color to interpolate towards.
* @param changeAmnt An amount between 0.0 - 1.0 representing a percentage
* change from this towards finalColor.
*/
public void interpolate(ColorRGBA finalColor, float changeAmnt) {
this.r = (1 - changeAmnt) * this.r + changeAmnt * finalColor.r;
this.g = (1 - changeAmnt) * this.g + changeAmnt * finalColor.g;
this.b = (1 - changeAmnt) * this.b + changeAmnt * finalColor.b;
this.a = (1 - changeAmnt) * this.a + changeAmnt * finalColor.a;
}
/**
* Sets this <code>ColorRGBA</code> to the interpolation by changeAmnt from
* beginColor to finalColor: this=(1-changeAmnt)*beginColor + changeAmnt *
* finalColor
*
* @param beginColor The begining color (changeAmnt=0).
* @param finalColor The final color to interpolate towards (changeAmnt=1).
* @param changeAmnt An amount between 0.0 - 1.0 representing a precentage
* change from beginColor towards finalColor.
*/
public void interpolate(ColorRGBA beginColor, ColorRGBA finalColor, float changeAmnt) {
this.r = (1 - changeAmnt) * beginColor.r + changeAmnt * finalColor.r;
this.g = (1 - changeAmnt) * beginColor.g + changeAmnt * finalColor.g;
this.b = (1 - changeAmnt) * beginColor.b + changeAmnt * finalColor.b;
this.a = (1 - changeAmnt) * beginColor.a + changeAmnt * finalColor.a;
}
/**
* <code>randomColor</code> is a utility method that generates a random
* opaque color.
*
* @return a random <code>ColorRGBA</code> with an alpha set to 1.
*/
public static ColorRGBA randomColor() {
ColorRGBA rVal = new ColorRGBA(0, 0, 0, 1);
rVal.r = FastMath.nextRandomFloat();
rVal.g = FastMath.nextRandomFloat();
rVal.b = FastMath.nextRandomFloat();
return rVal;
}
/**
* Multiplies each r,g,b,a of this <code>ColorRGBA</code> by the
* corresponding r,g,b,a of the given color and returns the result as a new
* <code>ColorRGBA</code>. Used as a way of combining colors and lights.
*
* @param c The color to multiply by.
* @return The new <code>ColorRGBA</code>. this*c
*/
public ColorRGBA mult(ColorRGBA c) {
return new ColorRGBA(c.r * r, c.g * g, c.b * b, c.a * a);
}
/**
* Multiplies each r,g,b,a of this <code>ColorRGBA</code> by the given
* scalar and returns the result as a new <code>ColorRGBA</code>. Used as a
* way of making colors dimmer or brighter.
*
* @param scalar The scalar to multiply by.
* @return The new <code>ColorRGBA</code>. this*scalar
*/
public ColorRGBA mult(float scalar) {
return new ColorRGBA(scalar * r, scalar * g, scalar * b, scalar * a);
}
/**
* Multiplies each r,g,b,a of this <code>ColorRGBA</code> by the given
* scalar and returns the result (this). Used as a way of making colors
* dimmer or brighter.
*
* @param scalar The scalar to multiply by.
* @return this*c
*/
public ColorRGBA multLocal(float scalar) {
this.r *= scalar;
this.g *= scalar;
this.b *= scalar;
this.a *= scalar;
return this;
}
/**
* Adds each r,g,b,a of this <code>ColorRGBA</code> by the corresponding
* r,g,b,a of the given color and returns the result as a new
* <code>ColorRGBA</code>. Used as a way of combining colors and lights.
*
* @param c The color to add.
* @return The new <code>ColorRGBA</code>. this+c
*/
public ColorRGBA add(ColorRGBA c) {
return new ColorRGBA(c.r + r, c.g + g, c.b + b, c.a + a);
}
/**
* Adds each r,g,b,a of this <code>ColorRGBA</code> by the r,g,b,a the given
* color and returns the result (this). Used as a way of combining colors
* and lights.
*
* @param c The color to add.
* @return this+c
*/
public ColorRGBA addLocal(ColorRGBA c) {
set(c.r + r, c.g + g, c.b + b, c.a + a);
return this;
}
/**
* Multiplies alpha component by the given scalar and returns the result as
* a new color.
*/
public ColorRGBA dilute(float scalarA) {
return new ColorRGBA(r, g, b, a * scalarA);
}
/**
* Multiplies alpha component by the given scalar and returns the result
* (this).
*/
public ColorRGBA diluteLocal(float scalarA) {
this.a *= scalarA;
return this;
}
/**
* <code>toString</code> returns the string representation of this
* <code>ColorRGBA</code>. The format of the string is:<br>
* <Class Name>: [R=RR.RRRR, G=GG.GGGG, B=BB.BBBB, A=AA.AAAA]
*
* @return The string representation of this <code>ColorRGBA</code>.
*/
@Override
public String toString() {
return "Color[" + r + ", " + g + ", " + b + ", " + a + "]";
}
@Override
public ColorRGBA clone() {
try {
return (ColorRGBA) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError(); // can not happen
}
}
/**
* Saves this <code>ColorRGBA</code> into the given <code>float</code>
* array.
*
* @param floats The <code>float</code> array to take this
* <code>ColorRGBA</code>. If null, a new <code>float[4]</code> is created.
* @return The array, with r,g,b,a float values in that order.
*/
public float[] toArray(float[] floats) {
if (floats == null) {
floats = new float[4];
}
floats[0] = r;
floats[1] = g;
floats[2] = b;
floats[3] = a;
return floats;
}
public ColorRGBA fromArray(float[] floats) {
r = floats[0];
g = floats[1];
b = floats[2];
a = floats[3];
return this;
}
/**
* <code>equals</code> returns true if this <code>ColorRGBA</code> is
* logically equivalent to a given color. That is, if all the components of
* the two colors are the same. False is returned otherwise.
*
* @param o The object to compare against.
* @return true if the colors are equal, false otherwise.
*/
@Override
public boolean equals(Object o) {
if (!(o instanceof ColorRGBA)) {
return false;
}
if (this == o) {
return true;
}
ColorRGBA comp = (ColorRGBA) o;
if (!FloatMath.equalsWithEpsilon(r, comp.r, FastMath.ZERO_TOLERANCE)) {
return false;
}
if (!FloatMath.equalsWithEpsilon(g, comp.g, FastMath.ZERO_TOLERANCE)) {
return false;
}
if (!FloatMath.equalsWithEpsilon(b, comp.b, FastMath.ZERO_TOLERANCE)) {
return false;
}
if (!FloatMath.equalsWithEpsilon(a, comp.a, FastMath.ZERO_TOLERANCE)) {
return false;
}
return true;
}
/**
* <code>hashCode</code> returns a unique code for this
* <code>ColorRGBA</code> based on its values. If two colors are logically
* equivalent, they will return the same hash code value.
*
* @return The hash code value of this <code>ColorRGBA</code>.
*/
@Override
public int hashCode() {
int hash = 37;
hash += 37 * hash + Float.floatToIntBits(r);
hash += 37 * hash + Float.floatToIntBits(g);
hash += 37 * hash + Float.floatToIntBits(b);
hash += 37 * hash + Float.floatToIntBits(a);
return hash;
}
/**
* Retrieves the component values of this <code>ColorRGBA</code> as a four
* element <code>byte</code> array in the order: r,g,b,a.
*
* @return the <code>byte</code> array that contains the color components.
*/
public byte[] asBytesRGBA() {
byte[] store = new byte[4];
store[0] = (byte) ((int) (r * 255) & 0xFF);
store[1] = (byte) ((int) (g * 255) & 0xFF);
store[2] = (byte) ((int) (b * 255) & 0xFF);
store[3] = (byte) ((int) (a * 255) & 0xFF);
return store;
}
/**
* Retrieves the component values of this <code>ColorRGBA</code> as an
* <code>int</code> in a,r,g,b order. Bits 24-31 are alpha, 16-23 are red,
* 8-15 are green, 0-7 are blue.
*
* @return The integer representation of this <code>ColorRGBA</code> in
* a,r,g,b order.
*/
public int asIntARGB() {
int argb = (((int) (a * 255) & 0xFF) << 24)
| (((int) (r * 255) & 0xFF) << 16)
| (((int) (g * 255) & 0xFF) << 8)
| (((int) (b * 255) & 0xFF));
return argb;
}
/**
* Retrieves the component values of this <code>ColorRGBA</code> as an
* <code>int</code> in r,g,b,a order. Bits 24-31 are red, 16-23 are green,
* 8-15 are blue, 0-7 are alpha.
*
* @return The integer representation of this <code>ColorRGBA</code> in
* r,g,b,a order.
*/
public int asIntRGBA() {
int rgba = (((int) (r * 255) & 0xFF) << 24)
| (((int) (g * 255) & 0xFF) << 16)
| (((int) (b * 255) & 0xFF) << 8)
| (((int) (a * 255) & 0xFF));
return rgba;
}
/**
* Retrieves the component values of this <code>ColorRGBA</code> as an
* <code>int</code> in a,b,g,r order. Bits 24-31 are alpha, 16-23 are blue,
* 8-15 are green, 0-7 are red.
*
* @return The integer representation of this <code>ColorRGBA</code> in
* a,b,g,r order.
*/
public int asIntABGR() {
int abgr = (((int) (a * 255) & 0xFF) << 24)
| (((int) (b * 255) & 0xFF) << 16)
| (((int) (g * 255) & 0xFF) << 8)
| (((int) (r * 255) & 0xFF));
return abgr;
}
/**
* Sets the component values of this <code>ColorRGBA</code> with the given
* combined ARGB <code>int</code>. Bits 24-31 are alpha, bits 16-23 are red,
* bits 8-15 are green, bits 0-7 are blue.
*
* @param color The integer ARGB value used to set this
* <code>ColorRGBA</code>.
*/
public void fromIntARGB(int color) {
a = ((byte) (color >> 24) & 0xFF) / 255f;
r = ((byte) (color >> 16) & 0xFF) / 255f;
g = ((byte) (color >> 8) & 0xFF) / 255f;
b = ((byte) (color) & 0xFF) / 255f;
}
/**
* Sets the RGBA values of this <code>ColorRGBA</code> with the given
* combined RGBA value Bits 24-31 are red, bits 16-23 are green, bits 8-15
* are blue, bits 0-7 are alpha.
*
* @param color The integer RGBA value used to set this object.
*/
public void fromIntRGBA(int color) {
r = ((byte) (color >> 24) & 0xFF) / 255f;
g = ((byte) (color >> 16) & 0xFF) / 255f;
b = ((byte) (color >> 8) & 0xFF) / 255f;
a = ((byte) (color) & 0xFF) / 255f;
}
/**
* Transform this <code>ColorRGBA</code> to a <code>Vector3f</code> using x
* = r, y = g, z = b. The Alpha value is not used. This method is useful to
* use for shaders assignment.
*
* @return A <code>Vector3f</code> containing the RGB value of this
* <code>ColorRGBA</code>.
*/
public Vector3f toVector3f() {
return new Vector3f(r, g, b);
}
/**
* Transform this <code>ColorRGBA</code> to a <code>Vector4f</code> using x
* = r, y = g, z = b, w = a. This method is useful to use for shaders
* assignment.
*
* @return A <code>Vector4f</code> containing the RGBA value of this
* <code>ColorRGBA</code>.
*/
public Vector4f toVector4f() {
return new Vector4f(r, g, b, a);
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,399 @@
/*
* Copyright (c) 2009-2012 jMonkeyEngine
* 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 'jMonkeyEngine' 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.
*/
package com.jme3.math;
/**
* Started Date: Jul 16, 2004<br>
* <br>
* Represents a translation, rotation and scale in one object.
*
* @author Jack Lindamood
* @author Joshua Slack
*/
public final class Transform implements Cloneable, java.io.Serializable {
static final long serialVersionUID = 1;
public static final Transform IDENTITY = new Transform();
private Quaternion rot = new Quaternion();
private Vector3f translation = new Vector3f();
private Vector3f scale = new Vector3f(1, 1, 1);
public Transform(Vector3f translation, Quaternion rot) {
this.translation.set(translation);
this.rot.set(rot);
}
public Transform(Vector3f translation, Quaternion rot, Vector3f scale) {
this(translation, rot);
this.scale.set(scale);
}
public Transform(Vector3f translation) {
this(translation, Quaternion.IDENTITY);
}
public Transform(Quaternion rot) {
this(Vector3f.ZERO, rot);
}
public Transform() {
this(Vector3f.ZERO, Quaternion.IDENTITY);
}
/**
* Sets this rotation to the given Quaternion value.
*
* @param rot The new rotation for this matrix.
* @return this
*/
public Transform setRotation(Quaternion rot) {
this.rot.set(rot);
return this;
}
/**
* Sets this translation to the given value.
*
* @param trans The new translation for this matrix.
* @return this
*/
public Transform setTranslation(Vector3f trans) {
this.translation.set(trans);
return this;
}
/**
* Return the translation vector in this matrix.
*
* @return translation vector.
*/
public Vector3f getTranslation() {
return translation;
}
/**
* Sets this scale to the given value.
*
* @param scale The new scale for this matrix.
* @return this
*/
public Transform setScale(Vector3f scale) {
this.scale.set(scale);
return this;
}
/**
* Sets this scale to the given value.
*
* @param scale The new scale for this matrix.
* @return this
*/
public Transform setScale(float scale) {
this.scale.set(scale, scale, scale);
return this;
}
/**
* Return the scale vector in this matrix.
*
* @return scale vector.
*/
public Vector3f getScale() {
return scale;
}
/**
* Stores this translation value into the given vector3f. If trans is null,
* a new vector3f is created to hold the value. The value, once stored, is
* returned.
*
* @param trans The store location for this matrix's translation.
* @return The value of this matrix's translation.
*/
public Vector3f getTranslation(Vector3f trans) {
if (trans == null)
trans = new Vector3f();
trans.set(this.translation);
return trans;
}
/**
* Stores this rotation value into the given Quaternion. If quat is null, a
* new Quaternion is created to hold the value. The value, once stored, is
* returned.
*
* @param quat The store location for this matrix's rotation.
* @return The value of this matrix's rotation.
*/
public Quaternion getRotation(Quaternion quat) {
if (quat == null)
quat = new Quaternion();
quat.set(rot);
return quat;
}
/**
* Return the rotation quaternion in this matrix.
*
* @return rotation quaternion.
*/
public Quaternion getRotation() {
return rot;
}
/**
* Stores this scale value into the given vector3f. If scale is null, a new
* vector3f is created to hold the value. The value, once stored, is
* returned.
*
* @param scale The store location for this matrix's scale.
* @return The value of this matrix's scale.
*/
public Vector3f getScale(Vector3f scale) {
if (scale == null)
scale = new Vector3f();
scale.set(this.scale);
return scale;
}
/**
* Sets this matrix to the interpolation between the first matrix and the
* second by delta amount.
*
* @param t1 The begining transform.
* @param t2 The ending transform.
* @param delta An amount between 0 and 1 representing how far to
* interpolate from t1 to t2.
*/
public void interpolateTransforms(Transform t1, Transform t2, float delta) {
this.rot.slerp(t1.rot, t2.rot, delta);
this.translation.interpolate(t1.translation, t2.translation, delta);
this.scale.interpolate(t1.scale, t2.scale, delta);
}
/**
* Changes the values of this matrix acording to it's parent. Very similar
* to the concept of Node/Spatial transforms.
*
* @param parent The parent matrix.
* @return This matrix, after combining.
*/
public Transform combineWithParent(Transform parent) {
scale.multLocal(parent.scale);
// rot.multLocal(parent.rot);
parent.rot.mult(rot, rot);
// This here, is evil code
// parent
// .rot
// .multLocal(translation)
// .multLocal(parent.scale)
// .addLocal(parent.translation);
translation.multLocal(parent.scale);
parent.rot
.multLocal(translation)
.addLocal(parent.translation);
return this;
}
/**
* Same as {@link #combineWithParent(Transform)}, but assumes that rotation
* is global, so it's not modified.
*
* @param parent
* @return
*/
public Transform combineWithParentGlobalRotation(Transform parent) {
scale.multLocal(parent.scale);
translation.multLocal(parent.scale);
parent.rot
.multLocal(translation)
.addLocal(parent.translation);
return this;
}
/**
* Sets this matrix's translation to the given x,y,z values.
*
* @param x This matrix's new x translation.
* @param y This matrix's new y translation.
* @param z This matrix's new z translation.
* @return this
*/
public Transform setTranslation(float x, float y, float z) {
translation.set(x, y, z);
return this;
}
/**
* Sets this matrix's scale to the given x,y,z values.
*
* @param x This matrix's new x scale.
* @param y This matrix's new y scale.
* @param z This matrix's new z scale.
* @return this
*/
public Transform setScale(float x, float y, float z) {
scale.set(x, y, z);
return this;
}
public Vector3f transformVector(final Vector3f in, Vector3f store) {
if (store == null)
store = new Vector3f();
// multiply with scale first, then rotate, finally translate (cf.
// Eberly)
return rot.mult(store.set(in).multLocal(scale), store).addLocal(translation);
}
public Vector3f transformInverseVector(final Vector3f in, Vector3f store) {
if (store == null)
store = new Vector3f();
// The author of this code should look above and take the inverse of
// that
// But for some reason, they didnt ..
// in.subtract(translation, store).divideLocal(scale);
// rot.inverse().mult(store, store);
in.subtract(translation, store);
rot.inverse().mult(store, store);
store.divideLocal(scale);
return store;
}
/**
* Loads the identity. Equal to translation=0,0,0 scale=1,1,1 rot=0,0,0,1.
*/
public void loadIdentity() {
translation.set(0, 0, 0);
scale.set(1, 1, 1);
rot.set(0, 0, 0, 1);
}
@Override
public String toString() {
return getClass().getSimpleName()
+ "[ "
+ translation.x
+ ", "
+ translation.y
+ ", "
+ translation.z
+ "]\n"
+ "[ "
+ rot.x
+ ", "
+ rot.y
+ ", "
+ rot.z
+ ", "
+ rot.w
+ "]\n"
+ "[ "
+ scale.x
+ " , "
+ scale.y
+ ", "
+ scale.z
+ "]";
}
/**
* Sets this matrix to be equal to the given matrix.
*
* @param matrixQuat The matrix to be equal to.
* @return this
*/
public Transform set(Transform matrixQuat) {
this.translation.set(matrixQuat.translation);
this.rot.set(matrixQuat.rot);
this.scale.set(matrixQuat.scale);
return this;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((rot == null) ? 0 : rot.hashCode());
result = prime * result + ((scale == null) ? 0 : scale.hashCode());
result = prime * result + ((translation == null) ? 0 : translation.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Transform other = (Transform) obj;
if (rot == null) {
if (other.rot != null)
return false;
} else if (!rot.equals(other.rot))
return false;
if (scale == null) {
if (other.scale != null)
return false;
} else if (!scale.equals(other.scale))
return false;
if (translation == null) {
if (other.translation != null)
return false;
} else if (!translation.equals(other.translation))
return false;
return true;
}
@Override
public Transform clone() {
try {
Transform tq = (Transform) super.clone();
tq.rot = rot.clone();
tq.scale = scale.clone();
tq.translation = translation.clone();
return tq;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}

View File

@@ -0,0 +1,718 @@
/*
* Copyright (c) 2009-2012 jMonkeyEngine
* 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 'jMonkeyEngine' 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.
*/
package com.jme3.math;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.logging.Logger;
/**
* <code>Vector2f</code> defines a Vector for a two float value vector.
*
* @author Mark Powell
* @author Joshua Slack
*/
public final class Vector2f implements Cloneable, java.io.Serializable {
static final long serialVersionUID = 1;
private static final Logger logger = Logger.getLogger(Vector2f.class.getName());
public static final Vector2f ZERO = new Vector2f(0f, 0f);
public static final Vector2f UNIT_XY = new Vector2f(1f, 1f);
/**
* the x value of the vector.
*/
public float x;
/**
* the y value of the vector.
*/
public float y;
/**
* Creates a Vector2f with the given initial x and y values.
*
* @param x The x value of this Vector2f.
* @param y The y value of this Vector2f.
*/
public Vector2f(float x, float y) {
this.x = x;
this.y = y;
}
/**
* Creates a Vector2f with x and y set to 0. Equivalent to Vector2f(0,0).
*/
public Vector2f() {
x = y = 0;
}
/**
* Creates a new Vector2f that contains the passed vector's information
*
* @param vector2f The vector to copy
*/
public Vector2f(Vector2f vector2f) {
this.x = vector2f.x;
this.y = vector2f.y;
}
/**
* set the x and y values of the vector
*
* @param x the x value of the vector.
* @param y the y value of the vector.
* @return this vector
*/
public Vector2f set(float x, float y) {
this.x = x;
this.y = y;
return this;
}
/**
* set the x and y values of the vector from another vector
*
* @param vec the vector to copy from
* @return this vector
*/
public Vector2f set(Vector2f vec) {
this.x = vec.x;
this.y = vec.y;
return this;
}
/**
* <code>add</code> adds a provided vector to this vector creating a
* resultant vector which is returned. If the provided vector is null, null
* is returned.
*
* @param vec the vector to add to this.
* @return the resultant vector.
*/
public Vector2f add(Vector2f vec) {
if (null == vec) {
logger.warning("Provided vector is null, null returned.");
return null;
}
return new Vector2f(x + vec.x, y + vec.y);
}
/**
* <code>addLocal</code> adds a provided vector to this vector internally,
* and returns a handle to this vector for easy chaining of calls. If the
* provided vector is null, null is returned.
*
* @param vec the vector to add to this vector.
* @return this
*/
public Vector2f addLocal(Vector2f vec) {
if (null == vec) {
logger.warning("Provided vector is null, null returned.");
return null;
}
x += vec.x;
y += vec.y;
return this;
}
/**
* <code>addLocal</code> adds the provided values to this vector internally,
* and returns a handle to this vector for easy chaining of calls.
*
* @param addX value to add to x
* @param addY value to add to y
* @return this
*/
public Vector2f addLocal(float addX, float addY) {
x += addX;
y += addY;
return this;
}
/**
* <code>add</code> adds this vector by <code>vec</code> and stores the
* result in <code>result</code>.
*
* @param vec The vector to add.
* @param result The vector to store the result in.
* @return The result vector, after adding.
*/
public Vector2f add(Vector2f vec, Vector2f result) {
if (null == vec) {
logger.warning("Provided vector is null, null returned.");
return null;
}
if (result == null)
result = new Vector2f();
result.x = x + vec.x;
result.y = y + vec.y;
return result;
}
/**
* <code>dot</code> calculates the dot product of this vector with a
* provided vector. If the provided vector is null, 0 is returned.
*
* @param vec the vector to dot with this vector.
* @return the resultant dot product of this vector and a given vector.
*/
public float dot(Vector2f vec) {
if (null == vec) {
logger.warning("Provided vector is null, 0 returned.");
return 0;
}
return x * vec.x + y * vec.y;
}
/**
* <code>cross</code> calculates the cross product of this vector with a
* parameter vector v.
*
* @param v the vector to take the cross product of with this.
* @return the cross product vector.
*/
public Vector3f cross(Vector2f v) {
return new Vector3f(0, 0, determinant(v));
}
public float determinant(Vector2f v) {
return (x * v.y) - (y * v.x);
}
/**
* Sets this vector to the interpolation by changeAmnt from this to the
* finalVec this=(1-changeAmnt)*this + changeAmnt * finalVec
*
* @param finalVec The final vector to interpolate towards
* @param changeAmnt An amount between 0.0 - 1.0 representing a percentage
* change from this towards finalVec
*/
public Vector2f interpolate(Vector2f finalVec, float changeAmnt) {
this.x = (1 - changeAmnt) * this.x + changeAmnt * finalVec.x;
this.y = (1 - changeAmnt) * this.y + changeAmnt * finalVec.y;
return this;
}
/**
* Sets this vector to the interpolation by changeAmnt from beginVec to
* finalVec this=(1-changeAmnt)*beginVec + changeAmnt * finalVec
*
* @param beginVec The begining vector (delta=0)
* @param finalVec The final vector to interpolate towards (delta=1)
* @param changeAmnt An amount between 0.0 - 1.0 representing a precentage
* change from beginVec towards finalVec
*/
public Vector2f interpolate(
Vector2f beginVec,
Vector2f finalVec,
float changeAmnt
) {
this.x = (1 - changeAmnt) * beginVec.x + changeAmnt * finalVec.x;
this.y = (1 - changeAmnt) * beginVec.y + changeAmnt * finalVec.y;
return this;
}
/**
* Check a vector... if it is null or its floats are NaN or infinite, return
* false. Else return true.
*
* @param vector the vector to check
* @return true or false as stated above.
*/
public static boolean isValidVector(Vector2f vector) {
if (vector == null)
return false;
if (
Float.isNaN(vector.x)
||
Float.isNaN(vector.y)
)
return false;
if (
Float.isInfinite(vector.x)
||
Float.isInfinite(vector.y)
)
return false;
return true;
}
/**
* <code>length</code> calculates the magnitude of this vector.
*
* @return the length or magnitude of the vector.
*/
public float length() {
return FastMath.sqrt(lengthSquared());
}
/**
* <code>lengthSquared</code> calculates the squared value of the magnitude
* of the vector.
*
* @return the magnitude squared of the vector.
*/
public float lengthSquared() {
return x * x + y * y;
}
/**
* <code>distanceSquared</code> calculates the distance squared between this
* vector and vector v.
*
* @param v the second vector to determine the distance squared.
* @return the distance squared between the two vectors.
*/
public float distanceSquared(Vector2f v) {
double dx = x - v.x;
double dy = y - v.y;
return (float) (dx * dx + dy * dy);
}
/**
* <code>distanceSquared</code> calculates the distance squared between this
* vector and vector v.
*
* @param otherX The X coordinate of the v vector
* @param otherY The Y coordinate of the v vector
* @return the distance squared between the two vectors.
*/
public float distanceSquared(float otherX, float otherY) {
double dx = x - otherX;
double dy = y - otherY;
return (float) (dx * dx + dy * dy);
}
/**
* <code>distance</code> calculates the distance between this vector and
* vector v.
*
* @param v the second vector to determine the distance.
* @return the distance between the two vectors.
*/
public float distance(Vector2f v) {
return FastMath.sqrt(distanceSquared(v));
}
/**
* <code>mult</code> multiplies this vector by a scalar. The resultant
* vector is returned.
*
* @param scalar the value to multiply this vector by.
* @return the new vector.
*/
public Vector2f mult(float scalar) {
return new Vector2f(x * scalar, y * scalar);
}
/**
* <code>multLocal</code> multiplies this vector by a scalar internally, and
* returns a handle to this vector for easy chaining of calls.
*
* @param scalar the value to multiply this vector by.
* @return this
*/
public Vector2f multLocal(float scalar) {
x *= scalar;
y *= scalar;
return this;
}
/**
* <code>multLocal</code> multiplies a provided vector to this vector
* internally, and returns a handle to this vector for easy chaining of
* calls. If the provided vector is null, null is returned.
*
* @param vec the vector to mult to this vector.
* @return this
*/
public Vector2f multLocal(Vector2f vec) {
if (null == vec) {
logger.warning("Provided vector is null, null returned.");
return null;
}
x *= vec.x;
y *= vec.y;
return this;
}
/**
* Multiplies this Vector2f's x and y by the scalar and stores the result in
* product. The result is returned for chaining. Similar to
* product=this*scalar;
*
* @param scalar The scalar to multiply by.
* @param product The vector2f to store the result in.
* @return product, after multiplication.
*/
public Vector2f mult(float scalar, Vector2f product) {
if (null == product) {
product = new Vector2f();
}
product.x = x * scalar;
product.y = y * scalar;
return product;
}
/**
* <code>divide</code> divides the values of this vector by a scalar and
* returns the result. The values of this vector remain untouched.
*
* @param scalar the value to divide this vectors attributes by.
* @return the result <code>Vector</code>.
*/
public Vector2f divide(float scalar) {
return new Vector2f(x / scalar, y / scalar);
}
/**
* <code>divideLocal</code> divides this vector by a scalar internally, and
* returns a handle to this vector for easy chaining of calls. Dividing by
* zero will result in an exception.
*
* @param scalar the value to divides this vector by.
* @return this
*/
public Vector2f divideLocal(float scalar) {
x /= scalar;
y /= scalar;
return this;
}
/**
* <code>negate</code> returns the negative of this vector. All values are
* negated and set to a new vector.
*
* @return the negated vector.
*/
public Vector2f negate() {
return new Vector2f(-x, -y);
}
/**
* <code>negateLocal</code> negates the internal values of this vector.
*
* @return this.
*/
public Vector2f negateLocal() {
x = -x;
y = -y;
return this;
}
/**
* <code>subtract</code> subtracts the values of a given vector from those
* of this vector creating a new vector object. If the provided vector is
* null, an exception is thrown.
*
* @param vec the vector to subtract from this vector.
* @return the result vector.
*/
public Vector2f subtract(Vector2f vec) {
return subtract(vec, null);
}
/**
* <code>subtract</code> subtracts the values of a given vector from those
* of this vector storing the result in the given vector object. If the
* provided vector is null, an exception is thrown.
*
* @param vec the vector to subtract from this vector.
* @param store the vector to store the result in. It is safe for this to be
* the same as vec. If null, a new vector is created.
* @return the result vector.
*/
public Vector2f subtract(Vector2f vec, Vector2f store) {
if (store == null)
store = new Vector2f();
store.x = x - vec.x;
store.y = y - vec.y;
return store;
}
/**
* <code>subtract</code> subtracts the given x,y values from those of this
* vector creating a new vector object.
*
* @param valX value to subtract from x
* @param valY value to subtract from y
* @return this
*/
public Vector2f subtract(float valX, float valY) {
return new Vector2f(x - valX, y - valY);
}
/**
* <code>subtractLocal</code> subtracts a provided vector to this vector
* internally, and returns a handle to this vector for easy chaining of
* calls. If the provided vector is null, null is returned.
*
* @param vec the vector to subtract
* @return this
*/
public Vector2f subtractLocal(Vector2f vec) {
if (null == vec) {
logger.warning("Provided vector is null, null returned.");
return null;
}
x -= vec.x;
y -= vec.y;
return this;
}
/**
* <code>subtractLocal</code> subtracts the provided values from this vector
* internally, and returns a handle to this vector for easy chaining of
* calls.
*
* @param valX value to subtract from x
* @param valY value to subtract from y
* @return this
*/
public Vector2f subtractLocal(float valX, float valY) {
x -= valX;
y -= valY;
return this;
}
/**
* <code>normalize</code> returns the unit vector of this vector.
*
* @return unit vector of this vector.
*/
public Vector2f normalize() {
float length = length();
if (length != 0) {
return divide(length);
}
return divide(1);
}
/**
* <code>normalizeLocal</code> makes this vector into a unit vector of
* itself.
*
* @return this.
*/
public Vector2f normalizeLocal() {
float length = length();
if (length != 0) {
return divideLocal(length);
}
return divideLocal(1);
}
/**
* <code>smallestAngleBetween</code> returns (in radians) the minimum angle
* between two vectors. It is assumed that both this vector and the given
* vector are unit vectors (iow, normalized).
*
* @param otherVector a unit vector to find the angle against
* @return the angle in radians.
*/
public float smallestAngleBetween(Vector2f otherVector) {
float dotProduct = dot(otherVector);
float angle = FastMath.acos(dotProduct);
return angle;
}
/**
* <code>angleBetween</code> returns (in radians) the angle required to
* rotate a ray represented by this vector to lie colinear to a ray
* described by the given vector. It is assumed that both this vector and
* the given vector are unit vectors (iow, normalized).
*
* @param otherVector the "destination" unit vector
* @return the angle in radians.
*/
public float angleBetween(Vector2f otherVector) {
float angle = FastMath.atan2(otherVector.y, otherVector.x)
- FastMath.atan2(y, x);
return angle;
}
public float getX() {
return x;
}
public Vector2f setX(float x) {
this.x = x;
return this;
}
public float getY() {
return y;
}
public Vector2f setY(float y) {
this.y = y;
return this;
}
/**
* <code>getAngle</code> returns (in radians) the angle represented by this
* Vector2f as expressed by a conversion from rectangular coordinates
* (<code>x</code>,&nbsp;<code>y</code>) to polar coordinates
* (r,&nbsp;<i>theta</i>).
*
* @return the angle in radians. [-pi, pi)
*/
public float getAngle() {
return FastMath.atan2(y, x);
}
/**
* <code>zero</code> resets this vector's data to zero internally.
*/
public Vector2f zero() {
x = y = 0;
return this;
}
/**
* <code>hashCode</code> returns a unique code for this vector object based
* on it's values. If two vectors are logically equivalent, they will return
* the same hash code value.
*
* @return the hash code value of this vector.
*/
@Override
public int hashCode() {
int hash = 37;
hash += 37 * hash + Float.floatToIntBits(x);
hash += 37 * hash + Float.floatToIntBits(y);
return hash;
}
@Override
public Vector2f clone() {
try {
return (Vector2f) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError(); // can not happen
}
}
/**
* Saves this Vector2f into the given float[] object.
*
* @param floats The float[] to take this Vector2f. If null, a new float[2]
* is created.
* @return The array, with X, Y float values in that order
*/
public float[] toArray(float[] floats) {
if (floats == null) {
floats = new float[2];
}
floats[0] = x;
floats[1] = y;
return floats;
}
/**
* are these two vectors the same? they are is they both have the same x and
* y values.
*
* @param o the object to compare for equality
* @return true if they are equal
*/
@Override
public boolean equals(Object o) {
if (!(o instanceof Vector2f)) {
return false;
}
if (this == o) {
return true;
}
Vector2f comp = (Vector2f) o;
if (Float.compare(x, comp.x) != 0)
return false;
if (Float.compare(y, comp.y) != 0)
return false;
return true;
}
/**
* <code>toString</code> returns the string representation of this vector
* object. The format of the string is such: com.jme.math.Vector2f
* [X=XX.XXXX, Y=YY.YYYY]
*
* @return the string representation of this vector.
*/
@Override
public String toString() {
return "(" + x + ", " + y + ")";
}
/**
* Used with serialization. Not to be called manually.
*
* @param in ObjectInput
* @throws IOException
* @throws ClassNotFoundException
* @see java.io.Externalizable
*/
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
x = in.readFloat();
y = in.readFloat();
}
/**
* Used with serialization. Not to be called manually.
*
* @param out ObjectOutput
* @throws IOException
* @see java.io.Externalizable
*/
public void writeExternal(ObjectOutput out) throws IOException {
out.writeFloat(x);
out.writeFloat(y);
}
public void rotateAroundOrigin(float angle, boolean cw) {
if (cw)
angle = -angle;
float newX = FastMath.cos(angle) * x - FastMath.sin(angle) * y;
float newY = FastMath.sin(angle) * x + FastMath.cos(angle) * y;
x = newX;
y = newY;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,971 @@
/*
* Copyright (c) 2009-2012 jMonkeyEngine
* 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 'jMonkeyEngine' 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.
*/
package com.jme3.math;
import java.util.logging.Logger;
/**
* <code>Vector4f</code> defines a Vector for a four float value tuple.
* <code>Vector4f</code> can represent any four dimensional value, such as a
* vertex, a normal, etc. Utility methods are also included to aid in
* mathematical calculations.
*
* @author Maarten Steur
*/
public final class Vector4f implements Cloneable, java.io.Serializable {
static final long serialVersionUID = 1;
private static final Logger logger = Logger.getLogger(Vector4f.class.getName());
public final static Vector4f ZERO = new Vector4f(0, 0, 0, 0);
public final static Vector4f NAN = new Vector4f(Float.NaN, Float.NaN, Float.NaN, Float.NaN);
public final static Vector4f UNIT_X = new Vector4f(1, 0, 0, 0);
public final static Vector4f UNIT_Y = new Vector4f(0, 1, 0, 0);
public final static Vector4f UNIT_Z = new Vector4f(0, 0, 1, 0);
public final static Vector4f UNIT_W = new Vector4f(0, 0, 0, 1);
public final static Vector4f UNIT_XYZW = new Vector4f(1, 1, 1, 1);
public final static Vector4f POSITIVE_INFINITY = new Vector4f(
Float.POSITIVE_INFINITY,
Float.POSITIVE_INFINITY,
Float.POSITIVE_INFINITY,
Float.POSITIVE_INFINITY
);
public final static Vector4f NEGATIVE_INFINITY = new Vector4f(
Float.NEGATIVE_INFINITY,
Float.NEGATIVE_INFINITY,
Float.NEGATIVE_INFINITY,
Float.NEGATIVE_INFINITY
);
/**
* the x value of the vector.
*/
public float x;
/**
* the y value of the vector.
*/
public float y;
/**
* the z value of the vector.
*/
public float z;
/**
* the w value of the vector.
*/
public float w;
/**
* Constructor instantiates a new <code>Vector3f</code> with default values
* of (0,0,0).
*
*/
public Vector4f() {
x = y = z = w = 0;
}
/**
* Constructor instantiates a new <code>Vector4f</code> with provides
* values.
*
* @param x the x value of the vector.
* @param y the y value of the vector.
* @param z the z value of the vector.
* @param w the w value of the vector.
*/
public Vector4f(float x, float y, float z, float w) {
this.x = x;
this.y = y;
this.z = z;
this.w = w;
}
/**
* Constructor instantiates a new <code>Vector3f</code> that is a copy of
* the provided vector
*
* @param copy The Vector3f to copy
*/
public Vector4f(Vector4f copy) {
this.set(copy);
}
/**
* <code>set</code> sets the x,y,z,w values of the vector based on passed
* parameters.
*
* @param x the x value of the vector.
* @param y the y value of the vector.
* @param z the z value of the vector.
* @param w the w value of the vector.
* @return this vector
*/
public Vector4f set(float x, float y, float z, float w) {
this.x = x;
this.y = y;
this.z = z;
this.w = w;
return this;
}
/**
* <code>set</code> sets the x,y,z values of the vector by copying the
* supplied vector.
*
* @param vect the vector to copy.
* @return this vector
*/
public Vector4f set(Vector4f vect) {
this.x = vect.x;
this.y = vect.y;
this.z = vect.z;
this.w = vect.w;
return this;
}
/**
*
* <code>add</code> adds a provided vector to this vector creating a
* resultant vector which is returned. If the provided vector is null, null
* is returned.
*
* @param vec the vector to add to this.
* @return the resultant vector.
*/
public Vector4f add(Vector4f vec) {
if (null == vec) {
logger.warning("Provided vector is null, null returned.");
return null;
}
return new Vector4f(x + vec.x, y + vec.y, z + vec.z, w + vec.w);
}
/**
*
* <code>add</code> adds the values of a provided vector storing the values
* in the supplied vector.
*
* @param vec the vector to add to this
* @param result the vector to store the result in
* @return result returns the supplied result vector.
*/
public Vector4f add(Vector4f vec, Vector4f result) {
result.x = x + vec.x;
result.y = y + vec.y;
result.z = z + vec.z;
result.w = w + vec.w;
return result;
}
/**
* <code>addLocal</code> adds a provided vector to this vector internally,
* and returns a handle to this vector for easy chaining of calls. If the
* provided vector is null, null is returned.
*
* @param vec the vector to add to this vector.
* @return this
*/
public Vector4f addLocal(Vector4f vec) {
if (null == vec) {
logger.warning("Provided vector is null, null returned.");
return null;
}
x += vec.x;
y += vec.y;
z += vec.z;
w += vec.w;
return this;
}
/**
*
* <code>add</code> adds the provided values to this vector, creating a new
* vector that is then returned.
*
* @param addX the x value to add.
* @param addY the y value to add.
* @param addZ the z value to add.
* @return the result vector.
*/
public Vector4f add(float addX, float addY, float addZ, float addW) {
return new Vector4f(x + addX, y + addY, z + addZ, w + addW);
}
/**
* <code>addLocal</code> adds the provided values to this vector internally,
* and returns a handle to this vector for easy chaining of calls.
*
* @param addX value to add to x
* @param addY value to add to y
* @param addZ value to add to z
* @return this
*/
public Vector4f addLocal(float addX, float addY, float addZ, float addW) {
x += addX;
y += addY;
z += addZ;
w += addW;
return this;
}
/**
*
* <code>scaleAdd</code> multiplies this vector by a scalar then adds the
* given Vector3f.
*
* @param scalar the value to multiply this vector by.
* @param add the value to add
*/
public Vector4f scaleAdd(float scalar, Vector4f add) {
x = x * scalar + add.x;
y = y * scalar + add.y;
z = z * scalar + add.z;
w = w * scalar + add.w;
return this;
}
/**
*
* <code>scaleAdd</code> multiplies the given vector by a scalar then adds
* the given vector.
*
* @param scalar the value to multiply this vector by.
* @param mult the value to multiply the scalar by
* @param add the value to add
*/
public Vector4f scaleAdd(float scalar, Vector4f mult, Vector4f add) {
this.x = mult.x * scalar + add.x;
this.y = mult.y * scalar + add.y;
this.z = mult.z * scalar + add.z;
this.w = mult.w * scalar + add.w;
return this;
}
/**
*
* <code>dot</code> calculates the dot product of this vector with a
* provided vector. If the provided vector is null, 0 is returned.
*
* @param vec the vector to dot with this vector.
* @return the resultant dot product of this vector and a given vector.
*/
public float dot(Vector4f vec) {
if (null == vec) {
logger.warning("Provided vector is null, 0 returned.");
return 0;
}
return x * vec.x + y * vec.y + z * vec.z + w * vec.w;
}
public Vector4f project(Vector4f other) {
float n = this.dot(other); // A . B
float d = other.lengthSquared(); // |B|^2
return new Vector4f(other).normalizeLocal().multLocal(n / d);
}
/**
* Returns true if this vector is a unit vector (length() ~= 1), returns
* false otherwise.
*
* @return true if this vector is a unit vector (length() ~= 1), or false
* otherwise.
*/
public boolean isUnitVector() {
float len = length();
return 0.99f < len && len < 1.01f;
}
/**
* <code>length</code> calculates the magnitude of this vector.
*
* @return the length or magnitude of the vector.
*/
public float length() {
return FastMath.sqrt(lengthSquared());
}
/**
* <code>lengthSquared</code> calculates the squared value of the magnitude
* of the vector.
*
* @return the magnitude squared of the vector.
*/
public float lengthSquared() {
return x * x + y * y + z * z + w * w;
}
/**
* <code>distanceSquared</code> calculates the distance squared between this
* vector and vector v.
*
* @param v the second vector to determine the distance squared.
* @return the distance squared between the two vectors.
*/
public float distanceSquared(Vector4f v) {
double dx = x - v.x;
double dy = y - v.y;
double dz = z - v.z;
double dw = w - v.w;
return (float) (dx * dx + dy * dy + dz * dz + dw * dw);
}
/**
* <code>distance</code> calculates the distance between this vector and
* vector v.
*
* @param v the second vector to determine the distance.
* @return the distance between the two vectors.
*/
public float distance(Vector4f v) {
return FastMath.sqrt(distanceSquared(v));
}
/**
*
* <code>mult</code> multiplies this vector by a scalar. The resultant
* vector is returned.
*
* @param scalar the value to multiply this vector by.
* @return the new vector.
*/
public Vector4f mult(float scalar) {
return new Vector4f(x * scalar, y * scalar, z * scalar, w * scalar);
}
/**
*
* <code>mult</code> multiplies this vector by a scalar. The resultant
* vector is supplied as the second parameter and returned.
*
* @param scalar the scalar to multiply this vector by.
* @param product the product to store the result in.
* @return product
*/
public Vector4f mult(float scalar, Vector4f product) {
if (null == product) {
product = new Vector4f();
}
product.x = x * scalar;
product.y = y * scalar;
product.z = z * scalar;
product.w = w * scalar;
return product;
}
/**
* <code>multLocal</code> multiplies this vector by a scalar internally, and
* returns a handle to this vector for easy chaining of calls.
*
* @param scalar the value to multiply this vector by.
* @return this
*/
public Vector4f multLocal(float scalar) {
x *= scalar;
y *= scalar;
z *= scalar;
w *= scalar;
return this;
}
/**
* <code>multLocal</code> multiplies a provided vector to this vector
* internally, and returns a handle to this vector for easy chaining of
* calls. If the provided vector is null, null is returned.
*
* @param vec the vector to mult to this vector.
* @return this
*/
public Vector4f multLocal(Vector4f vec) {
if (null == vec) {
logger.warning("Provided vector is null, null returned.");
return null;
}
x *= vec.x;
y *= vec.y;
z *= vec.z;
w *= vec.w;
return this;
}
/**
* <code>multLocal</code> multiplies this vector by 3 scalars internally,
* and returns a handle to this vector for easy chaining of calls.
*
* @param x
* @param y
* @param z
* @param w
* @return this
*/
public Vector4f multLocal(float x, float y, float z, float w) {
this.x *= x;
this.y *= y;
this.z *= z;
this.w *= w;
return this;
}
/**
* <code>multLocal</code> multiplies a provided vector to this vector
* internally, and returns a handle to this vector for easy chaining of
* calls. If the provided vector is null, null is returned.
*
* @param vec the vector to mult to this vector.
* @return this
*/
public Vector4f mult(Vector4f vec) {
if (null == vec) {
logger.warning("Provided vector is null, null returned.");
return null;
}
return mult(vec, null);
}
/**
* <code>multLocal</code> multiplies a provided vector to this vector
* internally, and returns a handle to this vector for easy chaining of
* calls. If the provided vector is null, null is returned.
*
* @param vec the vector to mult to this vector.
* @param store result vector (null to create a new vector)
* @return this
*/
public Vector4f mult(Vector4f vec, Vector4f store) {
if (null == vec) {
logger.warning("Provided vector is null, null returned.");
return null;
}
if (store == null)
store = new Vector4f();
return store.set(x * vec.x, y * vec.y, z * vec.z, w * vec.w);
}
/**
* <code>divide</code> divides the values of this vector by a scalar and
* returns the result. The values of this vector remain untouched.
*
* @param scalar the value to divide this vectors attributes by.
* @return the result <code>Vector</code>.
*/
public Vector4f divide(float scalar) {
scalar = 1f / scalar;
return new Vector4f(x * scalar, y * scalar, z * scalar, w * scalar);
}
/**
* <code>divideLocal</code> divides this vector by a scalar internally, and
* returns a handle to this vector for easy chaining of calls. Dividing by
* zero will result in an exception.
*
* @param scalar the value to divides this vector by.
* @return this
*/
public Vector4f divideLocal(float scalar) {
scalar = 1f / scalar;
x *= scalar;
y *= scalar;
z *= scalar;
w *= scalar;
return this;
}
/**
* <code>divide</code> divides the values of this vector by a scalar and
* returns the result. The values of this vector remain untouched.
*
* @param scalar the value to divide this vectors attributes by.
* @return the result <code>Vector</code>.
*/
public Vector4f divide(Vector4f scalar) {
return new Vector4f(x / scalar.x, y / scalar.y, z / scalar.z, w / scalar.w);
}
/**
* <code>divideLocal</code> divides this vector by a scalar internally, and
* returns a handle to this vector for easy chaining of calls. Dividing by
* zero will result in an exception.
*
* @param scalar the value to divides this vector by.
* @return this
*/
public Vector4f divideLocal(Vector4f scalar) {
x /= scalar.x;
y /= scalar.y;
z /= scalar.z;
w /= scalar.w;
return this;
}
/**
*
* <code>negate</code> returns the negative of this vector. All values are
* negated and set to a new vector.
*
* @return the negated vector.
*/
public Vector4f negate() {
return new Vector4f(-x, -y, -z, -w);
}
/**
*
* <code>negateLocal</code> negates the internal values of this vector.
*
* @return this.
*/
public Vector4f negateLocal() {
x = -x;
y = -y;
z = -z;
w = -w;
return this;
}
/**
*
* <code>subtract</code> subtracts the values of a given vector from those
* of this vector creating a new vector object. If the provided vector is
* null, null is returned.
*
* @param vec the vector to subtract from this vector.
* @return the result vector.
*/
public Vector4f subtract(Vector4f vec) {
return new Vector4f(x - vec.x, y - vec.y, z - vec.z, w - vec.w);
}
/**
* <code>subtractLocal</code> subtracts a provided vector to this vector
* internally, and returns a handle to this vector for easy chaining of
* calls. If the provided vector is null, null is returned.
*
* @param vec the vector to subtract
* @return this
*/
public Vector4f subtractLocal(Vector4f vec) {
if (null == vec) {
logger.warning("Provided vector is null, null returned.");
return null;
}
x -= vec.x;
y -= vec.y;
z -= vec.z;
w -= vec.w;
return this;
}
/**
*
* <code>subtract</code>
*
* @param vec the vector to subtract from this
* @param result the vector to store the result in
* @return result
*/
public Vector4f subtract(Vector4f vec, Vector4f result) {
if (result == null) {
result = new Vector4f();
}
result.x = x - vec.x;
result.y = y - vec.y;
result.z = z - vec.z;
result.w = w - vec.w;
return result;
}
/**
*
* <code>subtract</code> subtracts the provided values from this vector,
* creating a new vector that is then returned.
*
* @param subtractX the x value to subtract.
* @param subtractY the y value to subtract.
* @param subtractZ the z value to subtract.
* @param subtractW the w value to subtract.
* @return the result vector.
*/
public Vector4f subtract(float subtractX, float subtractY, float subtractZ, float subtractW) {
return new Vector4f(x - subtractX, y - subtractY, z - subtractZ, w - subtractW);
}
/**
* <code>subtractLocal</code> subtracts the provided values from this vector
* internally, and returns a handle to this vector for easy chaining of
* calls.
*
* @param subtractX the x value to subtract.
* @param subtractY the y value to subtract.
* @param subtractZ the z value to subtract.
* @param subtractW the w value to subtract.
* @return this
*/
public Vector4f subtractLocal(
float subtractX,
float subtractY,
float subtractZ,
float subtractW
) {
x -= subtractX;
y -= subtractY;
z -= subtractZ;
w -= subtractW;
return this;
}
/**
* <code>normalize</code> returns the unit vector of this vector.
*
* @return unit vector of this vector.
*/
public Vector4f normalize() {
// float length = length();
// if (length != 0) {
// return divide(length);
// }
//
// return divide(1);
float length = x * x + y * y + z * z + w * w;
if (length != 1f && length != 0f) {
length = 1.0f / FastMath.sqrt(length);
return new Vector4f(x * length, y * length, z * length, w * length);
}
return clone();
}
/**
* <code>normalizeLocal</code> makes this vector into a unit vector of
* itself.
*
* @return this.
*/
public Vector4f normalizeLocal() {
// NOTE: this implementation is more optimized
// than the old jme normalize as this method
// is commonly used.
float length = x * x + y * y + z * z + w * w;
if (length != 1f && length != 0f) {
length = 1.0f / FastMath.sqrt(length);
x *= length;
y *= length;
z *= length;
w *= length;
}
return this;
}
/**
* <code>maxLocal</code> computes the maximum value for each component in
* this and <code>other</code> vector. The result is stored in this vector.
*
* @param other
*/
public Vector4f maxLocal(Vector4f other) {
x = other.x > x ? other.x : x;
y = other.y > y ? other.y : y;
z = other.z > z ? other.z : z;
w = other.w > w ? other.w : w;
return this;
}
/**
* <code>minLocal</code> computes the minimum value for each component in
* this and <code>other</code> vector. The result is stored in this vector.
*
* @param other
*/
public Vector4f minLocal(Vector4f other) {
x = other.x < x ? other.x : x;
y = other.y < y ? other.y : y;
z = other.z < z ? other.z : z;
w = other.w < w ? other.w : w;
return this;
}
/**
* <code>zero</code> resets this vector's data to zero internally.
*/
public Vector4f zero() {
x = y = z = w = 0;
return this;
}
/**
* <code>angleBetween</code> returns (in radians) the angle between two
* vectors. It is assumed that both this vector and the given vector are
* unit vectors (iow, normalized).
*
* @param otherVector a unit vector to find the angle against
* @return the angle in radians.
*/
public float angleBetween(Vector4f otherVector) {
float dotProduct = dot(otherVector);
float angle = FastMath.acos(dotProduct);
return angle;
}
/**
* Sets this vector to the interpolation by changeAmnt from this to the
* finalVec this=(1-changeAmnt)*this + changeAmnt * finalVec
*
* @param finalVec The final vector to interpolate towards
* @param changeAmnt An amount between 0.0 - 1.0 representing a precentage
* change from this towards finalVec
*/
public Vector4f interpolate(Vector4f finalVec, float changeAmnt) {
this.x = (1 - changeAmnt) * this.x + changeAmnt * finalVec.x;
this.y = (1 - changeAmnt) * this.y + changeAmnt * finalVec.y;
this.z = (1 - changeAmnt) * this.z + changeAmnt * finalVec.z;
this.w = (1 - changeAmnt) * this.w + changeAmnt * finalVec.w;
return this;
}
/**
* Sets this vector to the interpolation by changeAmnt from beginVec to
* finalVec this=(1-changeAmnt)*beginVec + changeAmnt * finalVec
*
* @param beginVec the beging vector (changeAmnt=0)
* @param finalVec The final vector to interpolate towards
* @param changeAmnt An amount between 0.0 - 1.0 representing a precentage
* change from beginVec towards finalVec
*/
public Vector4f interpolate(Vector4f beginVec, Vector4f finalVec, float changeAmnt) {
this.x = (1 - changeAmnt) * beginVec.x + changeAmnt * finalVec.x;
this.y = (1 - changeAmnt) * beginVec.y + changeAmnt * finalVec.y;
this.z = (1 - changeAmnt) * beginVec.z + changeAmnt * finalVec.z;
this.w = (1 - changeAmnt) * beginVec.w + changeAmnt * finalVec.w;
return this;
}
/**
* Check a vector... if it is null or its floats are NaN or infinite, return
* false. Else return true.
*
* @param vector the vector to check
* @return true or false as stated above.
*/
public static boolean isValidVector(Vector4f vector) {
if (vector == null)
return false;
if (
Float.isNaN(vector.x)
||
Float.isNaN(vector.y)
||
Float.isNaN(vector.z)
||
Float.isNaN(vector.w)
)
return false;
if (
Float.isInfinite(vector.x)
||
Float.isInfinite(vector.y)
||
Float.isInfinite(vector.z)
||
Float.isInfinite(vector.w)
)
return false;
return true;
}
@Override
public Vector4f clone() {
try {
return (Vector4f) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError(); // can not happen
}
}
/**
* Saves this Vector3f into the given float[] object.
*
* @param floats The float[] to take this Vector3f. If null, a new float[3]
* is created.
* @return The array, with X, Y, Z float values in that order
*/
public float[] toArray(float[] floats) {
if (floats == null) {
floats = new float[4];
}
floats[0] = x;
floats[1] = y;
floats[2] = z;
floats[3] = w;
return floats;
}
/**
* are these two vectors the same? they are is they both have the same x,y,
* and z values.
*
* @param o the object to compare for equality
* @return true if they are equal
*/
@Override
public boolean equals(Object o) {
if (!(o instanceof Vector4f)) {
return false;
}
if (this == o) {
return true;
}
Vector4f comp = (Vector4f) o;
if (Float.compare(x, comp.x) != 0)
return false;
if (Float.compare(y, comp.y) != 0)
return false;
if (Float.compare(z, comp.z) != 0)
return false;
if (Float.compare(w, comp.w) != 0)
return false;
return true;
}
/**
* <code>hashCode</code> returns a unique code for this vector object based
* on it's values. If two vectors are logically equivalent, they will return
* the same hash code value.
*
* @return the hash code value of this vector.
*/
@Override
public int hashCode() {
int hash = 37;
hash += 37 * hash + Float.floatToIntBits(x);
hash += 37 * hash + Float.floatToIntBits(y);
hash += 37 * hash + Float.floatToIntBits(z);
hash += 37 * hash + Float.floatToIntBits(w);
return hash;
}
/**
* <code>toString</code> returns the string representation of this vector.
* The format is:
*
* org.jme.math.Vector3f [X=XX.XXXX, Y=YY.YYYY, Z=ZZ.ZZZZ, W=WW.WWWW]
*
* @return the string representation of this vector.
*/
@Override
public String toString() {
return "(" + x + ", " + y + ", " + z + ", " + w + ")";
}
public float getX() {
return x;
}
public Vector4f setX(float x) {
this.x = x;
return this;
}
public float getY() {
return y;
}
public Vector4f setY(float y) {
this.y = y;
return this;
}
public float getZ() {
return z;
}
public Vector4f setZ(float z) {
this.z = z;
return this;
}
public float getW() {
return w;
}
public Vector4f setW(float w) {
this.w = w;
return this;
}
/**
* @param index
* @return x value if index == 0, y value if index == 1 or z value if index
* == 2
* @throws IllegalArgumentException if index is not one of 0, 1, 2.
*/
public float get(int index) {
switch (index) {
case 0:
return x;
case 1:
return y;
case 2:
return z;
case 3:
return w;
}
throw new IllegalArgumentException("index must be either 0, 1, 2 or 3");
}
/**
* @param index which field index in this vector to set.
* @param value to set to one of x, y, z or w.
* @throws IllegalArgumentException if index is not one of 0, 1, 2, 3.
*/
public void set(int index, float value) {
switch (index) {
case 0:
x = value;
return;
case 1:
y = value;
return;
case 2:
z = value;
return;
case 3:
w = value;
return;
}
throw new IllegalArgumentException("index must be either 0, 1, 2 or 3");
}
}

View File

@@ -0,0 +1,105 @@
/*
* Copyright (c) 2009-2012 jMonkeyEngine
* 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 'jMonkeyEngine' 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.
*/
package com.jme3.system;
/**
* <code>NanoTimer</code> is a System.nanoTime implementation of
* <code>Timer</code>. This is primarily useful for headless applications
* running on a server.
*
* @author Matthew D. Hicks
*/
public class NanoTimer extends Timer {
private static final long TIMER_RESOLUTION = 1000000000L;
private static final float INVERSE_TIMER_RESOLUTION = 1f / TIMER_RESOLUTION;
private long startTime;
private long previousTime;
private float tpf;
private float fps;
private long currentTime;
public NanoTimer() {
startTime = System.nanoTime();
}
/**
* Returns the time in seconds. The timer starts at 0.0 seconds.
*
* @return the current time in seconds
*/
protected long getTimeInternal() {
return System.nanoTime() - startTime;
}
@Override
public float getTimeInSeconds() {
return getTime() * INVERSE_TIMER_RESOLUTION;
}
@Override
public long getTime() {
return currentTime;
}
@Override
public long getResolution() {
return TIMER_RESOLUTION;
}
@Override
public float getFrameRate() {
return fps;
}
@Override
public float getTimePerFrame() {
return tpf;
}
@Override
public void update() {
currentTime = getTimeInternal();
tpf = (currentTime - previousTime) * (1.0f / TIMER_RESOLUTION);
fps = 1.0f / tpf;
previousTime = getTime();
}
@Override
public void reset() {
startTime = System.nanoTime();
currentTime = getTimeInternal();
previousTime = getTime();
}
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright (c) 2009-2012 jMonkeyEngine
* 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 'jMonkeyEngine' 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.
*/
package com.jme3.system;
/**
* <code>Timer</code> is the base class for a high resolution timer. It is
* created from getTimer("display system")
*
* @author Mark Powell
* @version $Id: Timer.java,v 1.18 2007/03/09 10:19:34 rherlitz Exp $
*/
public abstract class Timer {
/**
* Returns the current time in ticks. A tick is an arbitrary measure of time
* defined by the timer implementation. The number of ticks per second is
* given by <code>getResolution()</code>. The timer starts at 0 ticks.
*
* @return a long value representing the current time
*/
public abstract long getTime();
/**
* Returns the time in seconds. The timer starts at 0.0 seconds.
*
* @return the current time in seconds
*/
public float getTimeInSeconds() {
return getTime() / (float) getResolution();
}
/**
* Returns the resolution of the timer.
*
* @return the number of timer ticks per second
*/
public abstract long getResolution();
/**
* Returns the "calls per second". If this is called every frame, then it
* will return the "frames per second".
*
* @return The "calls per second".
*/
public abstract float getFrameRate();
/**
* Returns the time, in seconds, between the last call and the current one.
*
* @return Time between this call and the last one.
*/
public abstract float getTimePerFrame();
/**
* <code>update</code> recalculates the frame rate based on the previous
* call to update. It is assumed that update is called each frame.
*/
public abstract void update();
/**
* Reset the timer to 0. Clear any tpf history.
*/
public abstract void reset();
}

View File

@@ -0,0 +1,178 @@
/*
* Copyright (c) 2009-2012 jMonkeyEngine
* 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 'jMonkeyEngine' 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.
*/
package com.jme3.util;
import com.jme3.math.*;
/**
* Temporary variables assigned to each thread. Engine classes may access these
* temp variables with TempVars.get(), all retrieved TempVars instances must be
* returned via TempVars.release(). This returns an available instance of the
* TempVar class ensuring this particular instance is never used elsewhere in
* the mean time.
*/
public class TempVars {
/**
* Allow X instances of TempVars in a single thread.
*/
private static final int STACK_SIZE = 5;
/**
* <code>TempVarsStack</code> contains a stack of TempVars. Every time
* TempVars.get() is called, a new entry is added to the stack, and the
* index incremented. When TempVars.release() is called, the entry is
* checked against the current instance and then the index is decremented.
*/
private static class TempVarsStack {
int index = 0;
TempVars[] tempVars = new TempVars[STACK_SIZE];
}
/**
* ThreadLocal to store a TempVarsStack for each thread. This ensures each
* thread has a single TempVarsStack that is used only in method calls in
* that thread.
*/
private static final ThreadLocal<TempVarsStack> varsLocal = new ThreadLocal<TempVarsStack>() {
@Override
public TempVarsStack initialValue() {
return new TempVarsStack();
}
};
/**
* This instance of TempVars has been retrieved but not released yet.
*/
private boolean isUsed = false;
private TempVars() {
}
/**
* Acquire an instance of the TempVar class. You have to release the
* instance after use by calling the release() method. If more than
* STACK_SIZE (currently 5) instances are requested in a single thread then
* an ArrayIndexOutOfBoundsException will be thrown.
*
* @return A TempVar instance
*/
public static TempVars get() {
TempVarsStack stack = varsLocal.get();
TempVars instance = stack.tempVars[stack.index];
if (instance == null) {
// Create new
instance = new TempVars();
// Put it in there
stack.tempVars[stack.index] = instance;
}
stack.index++;
instance.isUsed = true;
return instance;
}
/**
* Releases this instance of TempVars. Once released, the contents of the
* TempVars are undefined. The TempVars must be released in the opposite
* order that they are retrieved, e.g. Acquiring vars1, then acquiring
* vars2, vars2 MUST be released first otherwise an exception will be
* thrown.
*/
public void release() {
if (!isUsed) {
throw new IllegalStateException("This instance of TempVars was already released!");
}
isUsed = false;
TempVarsStack stack = varsLocal.get();
// Return it to the stack
stack.index--;
// Check if it is actually there
if (stack.tempVars[stack.index] != this) {
throw new IllegalStateException(
"An instance of TempVars has not been released in a called method!"
);
}
}
/**
* Color
*/
public final ColorRGBA color = new ColorRGBA();
/**
* General vectors.
*/
public final Vector3f vect1 = new Vector3f();
public final Vector3f vect2 = new Vector3f();
public final Vector3f vect3 = new Vector3f();
public final Vector3f vect4 = new Vector3f();
public final Vector3f vect5 = new Vector3f();
public final Vector3f vect6 = new Vector3f();
public final Vector3f vect7 = new Vector3f();
// seems the maximum number of vector used is 7 in com.jme3.bounding.java
public final Vector3f vect8 = new Vector3f();
public final Vector3f vect9 = new Vector3f();
public final Vector3f vect10 = new Vector3f();
public final Vector4f vect4f = new Vector4f();
public final Vector3f[] tri = { new Vector3f(),
new Vector3f(),
new Vector3f() };
/**
* 2D vector
*/
public final Vector2f vect2d = new Vector2f();
public final Vector2f vect2d2 = new Vector2f();
/**
* General matrices.
*/
public final Matrix3f tempMat3 = new Matrix3f();
public final Matrix4f tempMat4 = new Matrix4f();
public final Matrix4f tempMat42 = new Matrix4f();
/**
* General quaternions.
*/
public final Quaternion quat1 = new Quaternion();
public final Quaternion quat2 = new Quaternion();
public final float[] matrixWrite = new float[16];
}

View File

@@ -0,0 +1,672 @@
package io.eiren.math;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
import com.jme3.math.Vector4f;
public class FloatMath {
public static final float PI = (float) Math.PI;
public static final float TWO_PI = (float) (Math.PI * 2);
public static final float ANGLE_EPSILON = 0.028f; // in degrees (float
// epsilon for sin/cos)
public static final float ANGLE_EPSILON_RAD = toRad(ANGLE_EPSILON);
public static final float ZERO_TOLERANCE_F = FastMath.ZERO_TOLERANCE;
public static final double ZERO_TOLERANCE_D = 0.0001d;
public static final float SQRT_TWO = (float) Math.sqrt(2f);
public static final float INV_SQRT_TWO = 1f / SQRT_TWO;
public static final float SQRT_THREE = (float) Math.sqrt(3f);
public static final float INV_SQRT_THREE = 1f / SQRT_THREE;
public static final float TWO_FPI = PI * 2;
public static final float SIN_75_DEG = 0.965926f;
public static final float SIN_60_DEG = 0.866025f;
public static final float SIN_45_DEG = 0.707107f;
public static final float SIN_30_DEG = 0.5f;
public static final float SIN_15_DEG = 0.258819f;
public static final float COS_75_DEG = 0.258819f;
public static final float COS_60_DEG = 0.5f;
public static final float COS_45_DEG = 0.707107f;
public static final float COS_30_DEG = 0.866025f;
public static final float COS_15_DEG = 0.965926f;
public static final int TEN_BITS = ~(~0 << 10);
public static final int TENTH_BIT = 1 << 10;
public static final int TEN_BITS_MAX = ~(~0 << 9);
public static final int TEN_BITS_MAX_UNSIGNED = ~(~0 << 10);
public static final int TWO_BITS = ~(~0 << 2);
public static final int SECOND_BIT = 1 << 2;
public static final int TWO_BITS_MAX = ~(~0 << 1);
public static final int TWO_BITS_MAX_UNSIGNED = ~(~0 << 2);
public static float roundIfZero(float x) {
return Math.abs(x) < ZERO_TOLERANCE_F ? 0.0f : x;
}
public static boolean equalsToZero(float x) {
return Math.abs(x) < ZERO_TOLERANCE_F;
}
public static boolean lessThanZero(float x) {
return (x < -ZERO_TOLERANCE_F);
}
public static boolean lessOrEqualsToZero(float x) {
return (x < ZERO_TOLERANCE_F);
}
public static boolean greaterThanZero(float x) {
return (x > ZERO_TOLERANCE_F);
}
public static boolean greaterOrEqualsToZero(float x) {
return (x > -ZERO_TOLERANCE_F);
}
public static boolean equalsToZero(float x, float epsilon) {
return Math.abs(x) < epsilon;
}
public static boolean equalsWithEpsilon(float x, float y) {
return Math.abs(x - y) < ZERO_TOLERANCE_F;
}
public static boolean equalsWithEpsilon(float x, float y, float epsilon) {
return Math.abs(x - y) < epsilon;
}
public static boolean lessWithEpsilon(float x, float y) {
return (x < y - ZERO_TOLERANCE_F);
}
public static boolean lessOrEqualsWithEpsilon(float x, float y) {
return (x < y + ZERO_TOLERANCE_F);
}
public static boolean lessWithEpsilon(float x, float y, float epsilon) {
return (x < y - epsilon);
}
public static boolean lessOrEqualsWithEpsilon(float x, float y, float epsilon) {
return (x < y + epsilon);
}
public static boolean greaterWithEpsilon(float x, float y) {
return (x > y + ZERO_TOLERANCE_F);
}
public static boolean greaterOrEqualsWithEpsilon(float x, float y) {
return (x > y - ZERO_TOLERANCE_F);
}
public static boolean greaterWithEpsilon(float x, float y, float epsilon) {
return (x > y + epsilon);
}
public static boolean greaterOrEqualsWithEpsilon(float x, float y, float epsilon) {
return (x > y - epsilon);
}
public static double roundIfZero(double x) {
return Math.abs(x) < ZERO_TOLERANCE_D ? 0.0d : x;
}
public static boolean equalsToZero(double x) {
return Math.abs(x) < ZERO_TOLERANCE_D;
}
public static boolean equalsWithEpsilon(double x, double y) {
return Math.abs(x - y) < ZERO_TOLERANCE_D;
}
public static boolean lessWithEpsilon(double x, double y) {
return (x < y - ZERO_TOLERANCE_D);
}
public static boolean lessOrEqualsWithEpsilon(double x, double y) {
return (x < y + ZERO_TOLERANCE_D);
}
public static boolean greaterWithEpsilon(double x, double y) {
return (x > y + ZERO_TOLERANCE_D);
}
public static boolean greaterOrEqualsWithEpsilon(double x, double y) {
return (x > y - ZERO_TOLERANCE_D);
}
public static float toDegrees(float angrad) {
return angrad * 180.0f / PI;
}
public static float toRad(float deg) {
return deg / 180.0f * PI;
}
public static boolean radEqual(float angle1, float angle2) {
float diff = clampRad(angle1 - angle2);
return Math.abs(diff) < ANGLE_EPSILON_RAD;
}
public static boolean degreesEqual(float angle1, float angle2) {
float diff = clampDegrees(angle1 - angle2);
return Math.abs(diff) < ANGLE_EPSILON;
}
/**
* @deprecated use {@link #normalizeRad(float)}
*/
@Deprecated
public static float clampRad(float angle) {
return normalizeRad(angle);
}
public static float normalizeRad(float angle) {
return FastMath.normalize(angle, -FastMath.PI, FastMath.PI);
}
/**
* @deprecated use {@link #normalizeDegrees(float)}
*/
@Deprecated
public static float clampDegrees(float angle) {
return normalizeDegrees(angle);
}
public static float normalizeDegrees(float angle) {
return FastMath.normalize(angle, -180f, 180f);
}
public static float animateEase(float t) {
// Special case of Bezier interpolation (p0 = p1 = 0, p2 = p3 = 1)
return (3.0f - 2.0f * t) * t * t;
}
public static float animateEaseIn(float t) {
return t * t;
}
/**
* Lineary remaps value from the source interval to the target interval.
* <a href="https://en.wikipedia.org/wiki/Linear_interpolation">details</a>
*/
public static float mapValue(
float value,
float sourceStart,
float sourceEnd,
float targetStart,
float targetEnd
) {
return targetStart
+ (value - sourceStart) * (targetEnd - targetStart) / (sourceEnd - sourceStart);
}
/**
* Clamps the given value and remaps to the target interval.
* <p>
* Note the source interval values should be sorted.
*/
public static float mapValueWithClampBefore(
float value,
float sourceBottom,
float sourceTop,
float targetBottom,
float targetTop
) {
return mapValue(
clamp(value, sourceBottom, sourceTop),
sourceBottom,
sourceTop,
targetBottom,
targetTop
);
}
/**
* Remaps the given value to the target interval and clamps.
* <p>
* Note the target interval values should be sorted.
*/
public static float mapValueWithClampAfter(
float value,
float sourceBottom,
float sourceTop,
float targetBottom,
float targetTop
) {
return clamp(
mapValue(value, sourceBottom, sourceTop, targetBottom, targetTop),
targetBottom,
targetTop
);
}
public static float smoothstep(float edge0, float edge1, float x) {
// Scale, bias and saturate x to 0..1 range
x = FastMath.clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f);
// Evaluate polynomial
return x * x * (3f - 2f * x);
}
public static float smootherstep(float edge0, float edge1, float x) {
// Scale, and clamp x to 0..1 range
x = FastMath.clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f);
// Evaluate polynomial
return x * x * x * (x * (x * 6f - 15f) + 10f);
}
/**
* Applies linear contrast (with clamping).
*
* @param t - input value in range (0..1)
* @param k - contrast factor in range (-1..1):
* <ul>
* <li>1.0 - maximal contrast</li>
* <li><b>0.0</b> - bypass (returns input value)</li>
* <li>-1.0 - minimal contrast (returns 0.5f for any input)</li>
* </ul>
* @return contrasted value in range (0..1)
*/
public static float contrastLinear(float t, float k) {
float x = 2f * t - 1f; // -1..1
float gamma = (1f + k) / (1f - k);
float f = FastMath.clamp(gamma * x, -1f, 1f); // -1..1
return 0.5f * (f + 1f); // 0..1
}
/**
* Applies non-linear contrast by power function.
*
* @param t - input value in range (0..1)
* @param k - contrast factor in range (-1..1) exclusive:
* <ul>
* <li>0.999 - maximal contrast</li>
* <li>0.0 - bypass (returns input value)</li>
* <li>-0.999 - minimal contrast</li>
* </ul>
* @return contrasted value in range (0..1)
*/
public static float contrastPower(float t, float k) {
float x = 2f * t - 1f; // -1..1
float gamma = (1f - k) / (1f + k);
float f = FastMath.sign(x) * FastMath.pow(FastMath.abs(x), gamma); // -1..1
return 0.5f * (f + 1f); // 0..1
}
/**
* Applies non-linear contrast by square splines.
*
* @param t - input value in range (0..1)
* @param k - contrast factor in range (-1..1):
* <ul>
* <li>1.0 - maximal contrast</li>
* <li>0.0 - bypass (returns input value)</li>
* <li>-1.0 - minimal contrast</li>
* </ul>
* @return contrasted value in range (0..1)
*/
public static float contrastQuadricSpline(float t, float k) {
float x = 2f * t - 1f; // -1..1
float f = x * (1f + k * (1f - FastMath.abs(x))); // -1..1
return 0.5f * (f + 1f); // 0..1
}
/**
* Applies non-linear contrast by square splines inverted function.
*
* @param t - input value in range (0..1)
* @param k - contrast factor in range (-2..2):
* <ul>
* <li>2.0 - maximal contrast</li>
* <li>0.0 - bypass (returns input value)</li>
* <li>-2.0 - minimal contrast</li>
* </ul>
* @return contrasted value in range (0..1)
*/
public static float contrastInvertQuadricSpline(float t, float k) {
float x = 2f * t - 1f; // -1..1
float g;
if (k > 0) {
g = FastMath.sign(x) * FastMath.sqrt(FastMath.abs(x)) - 2f * x;
} else {
g = FastMath.sign(x) * (FastMath.sqrt(1f - FastMath.abs(x)) - 1f);
}
float f = (1f + k) * x + k * g; // -1..1
return 0.5f * (f + 1f); // 0..1
}
/**
* Applies non-linear contrast by cubic splines.
*
* @param t - input value in range (0..1)
* @param k - contrast factor in range (-1..1):
* <ul>
* <li>1.0 - maximal contrast</li>
* <li>0.0 - bypass (returns input value)</li>
* <li>-1.0 - minimal contrast</li>
* </ul>
* @return contrasted value in range (0..1)
*/
public static float contrastCubicSpline(float t, float k) {
float x = 2f * t - 1f; // -1..1
float f = x * (1f + FastMath.abs(k) * (x * x - 1f));
if (k < 0)
f -= x * 3f * k * (1f - FastMath.abs(x));
return 0.5f * (f + 1f); // 0..1
}
public static float fraction(float f) {
return f - (int) f;
}
public static double fraction(double d) {
return d - (long) d;
}
/**
* @deprecated Do not copy {@link Math} methods.
*/
@Deprecated
public static float min(float a, float b) {
return a > b ? b : a;
}
public static float min(float a, float b, float c) {
return Math.min(Math.min(a, b), c);
}
public static float min(float a, float b, float c, float d) {
return Math.min(Math.min(a, b), Math.min(c, d));
}
/**
* @deprecated Do not copy {@link Math} methods.
*/
@Deprecated
public static float max(float a, float b) {
return a > b ? a : b;
}
public static float max(float a, float b, float c) {
return Math.max(Math.max(a, b), c);
}
public static float max(float a, float b, float c, float d) {
return Math.max(Math.max(a, b), Math.max(c, d));
}
public static float cos(float value) {
return (float) Math.cos(value);
}
public static float sin(float value) {
return (float) Math.sin(value);
}
public static float ceil(float value) {
return (float) Math.ceil(value);
}
public static float floor(float value) {
return (float) Math.floor(value);
}
public static float pow(float value, float power) {
return (float) Math.pow(value, power);
}
/**
* @deprecated Do not copy {@link Math} methods.
*/
@Deprecated
public static float abs(float value) {
return (float) Math.abs(value);
}
/**
* @deprecated Do not copy {@link Math} methods.
*/
@Deprecated
public static float round(float value) {
return (float) Math.round(value);
}
public static float sqrt(float value) {
return (float) Math.sqrt(value);
}
public static float distance(float x0, float y0, float z0, float x1, float y1, float z1) {
return distance(x1 - x0, y1 - y0, z1 - z0);
}
public static float distance(float x, float y, float z) {
return sqrt(sqrDistance(x, y, z));
}
public static float sqrDistance(float x, float y, float z) {
return x * x + y * y + z * z;
}
public static float distance(float x, float y) {
return sqrt(sqrDistance(x, y));
}
public static float sqrDistance(float x, float y) {
return x * x + y * y;
}
public static float sqrDistance(Vector3f v, float x1, float y1, float z1) {
return sqrDistance(x1 - v.x, y1 - v.y, z1 - v.z);
}
public static float sqrDistance(float x0, float y0, float z0, float x1, float y1, float z1) {
return sqrDistance(x1 - x0, y1 - y0, z1 - z0);
}
public static float hypot(float x, float y) {
return FastMath.sqrt(x * x + y * y);
}
public static float hypot(float x, float y, float z) {
return FastMath.sqrt(x * x + y * y + z * z);
}
/**
* The same as FastMath.clamp
*/
public static float clamp(float value, float min, float max) {
if (value <= min)
return min;
if (value >= max)
return max;
return value;
}
public static Vector3f int2101010RevToFloats(int packedValue, Vector3f store) {
if (store == null)
store = new Vector3f();
store.x = packedValue & TEN_BITS_MAX;
if ((packedValue & TENTH_BIT) != 0)
store.x *= -1;
store.y = (packedValue >>> 10) & TEN_BITS_MAX;
if ((packedValue & (TENTH_BIT << 10)) != 0)
store.y *= -1;
store.z = (packedValue >>> 20) & TEN_BITS_MAX;
if ((packedValue & (TENTH_BIT << 20)) != 0)
store.z *= -1;
return store;
}
public static int floatToInt210101Rev(Vector3f values) {
int store = 0;
store |= ((int) values.x) & TEN_BITS_MAX;
if (values.x < 0)
store |= TENTH_BIT;
store |= (((int) values.y) & TEN_BITS_MAX) << 10;
if (values.y < 0)
store |= TENTH_BIT << 10;
store |= (((int) values.z) & TEN_BITS_MAX) << 20;
if (values.z < 0)
store |= TENTH_BIT << 20;
return store;
}
public static int floatToInt210101RevNormalized(Vector3f values) {
int store = 0;
store |= ((int) (values.x * TEN_BITS)) & TEN_BITS_MAX;
if (values.x < 0)
store |= TENTH_BIT;
store |= (((int) (values.y * TEN_BITS)) & TEN_BITS_MAX) << 10;
if (values.y < 0)
store |= TENTH_BIT << 10;
store |= (((int) (values.z * TEN_BITS)) & TEN_BITS_MAX) << 20;
if (values.z < 0)
store |= TENTH_BIT << 20;
return store;
}
public static int floatToUnsignedInt210101Rev(Vector3f values) {
int store = 0;
store |= ((int) values.x) & TEN_BITS;
store |= (((int) values.y) & TEN_BITS) << 10;
store |= (((int) values.z) & TEN_BITS) << 20;
return store;
}
public static int floatToUnsignedInt210101RevNormalized(Vector3f values) {
int store = 0;
store |= ((int) (values.x * TEN_BITS)) & TEN_BITS;
store |= (((int) (values.y * TEN_BITS)) & TEN_BITS) << 10;
store |= (((int) (values.z * TEN_BITS)) & TEN_BITS) << 20;
return store;
}
public static int floatToInt210101Rev(float x, float y, float z) {
int store = 0;
store |= ((int) x) & TEN_BITS_MAX;
if (x < 0)
store |= TENTH_BIT;
store |= (((int) y) & TEN_BITS_MAX) << 10;
if (y < 0)
store |= TENTH_BIT << 10;
store |= (((int) z) & TEN_BITS_MAX) << 20;
if (z < 0)
store |= TENTH_BIT << 20;
return store;
}
public static int floatToUnsignedInt210101Rev(float x, float y, float z) {
int store = 0;
store |= ((int) x) & TEN_BITS;
store |= (((int) y) & TEN_BITS) << 10;
store |= (((int) z) & TEN_BITS) << 20;
return store;
}
public static Vector4f int2101010RevToFloats(int packedValue, Vector4f store) {
if (store == null)
store = new Vector4f();
store.x = packedValue & TEN_BITS_MAX;
if ((packedValue & TENTH_BIT) != 0)
store.x *= -1;
store.y = (packedValue >>> 10) & TEN_BITS_MAX;
if ((packedValue & (TENTH_BIT << 10)) != 0)
store.y *= -1;
store.z = (packedValue >>> 20) & TEN_BITS_MAX;
if ((packedValue & (TENTH_BIT << 20)) != 0)
store.z *= -1;
store.w = (packedValue >>> 30) & TWO_BITS_MAX;
if ((packedValue & (SECOND_BIT << 30)) != 0)
store.w *= -1;
return store;
}
public static int floatToInt210101Rev(Vector4f values) {
int store = 0;
store |= ((int) values.x) & TEN_BITS_MAX;
if (values.x < 0)
store |= TENTH_BIT;
store |= (((int) values.y) & TEN_BITS_MAX) << 10;
if (values.y < 0)
store |= TENTH_BIT << 10;
store |= (((int) values.z) & TEN_BITS_MAX) << 20;
if (values.z < 0)
store |= TENTH_BIT << 20;
store |= (((int) values.z) & TWO_BITS_MAX) << 30;
if (values.w < 0)
store |= SECOND_BIT << 30;
return store;
}
public static int floatToUnsignedInt210101Rev(Vector4f values) {
int store = 0;
store |= ((int) values.x) & TEN_BITS;
store |= (((int) values.y) & TEN_BITS) << 10;
store |= (((int) values.z) & TEN_BITS) << 20;
store |= (((int) values.z) & TWO_BITS) << 30;
return store;
}
public static Vector3f unsignedInt2101010RevToFloats(int packedValue, Vector3f store) {
if (store == null)
store = new Vector3f();
store.x = packedValue & TEN_BITS;
store.y = (packedValue >>> 10) & TEN_BITS;
store.z = (packedValue >>> 20) & TEN_BITS;
return store;
}
public static Vector4f unsignedInt2101010RevToFloats(int packedValue, Vector4f store) {
if (store == null)
store = new Vector4f();
store.x = packedValue & TEN_BITS;
store.y = (packedValue >>> 10) & TEN_BITS;
store.z = (packedValue >>> 20) & TEN_BITS;
store.w = (packedValue >>> 30) & TWO_BITS;
return store;
}
public static Vector3f int2101010RevNormalizedToFloats(int packedValue, Vector3f store) {
store = int2101010RevToFloats(packedValue, store);
store.x /= TEN_BITS_MAX;
store.y /= TEN_BITS_MAX;
store.z /= TEN_BITS_MAX;
return store;
}
public static Vector4f int2101010RevNormalizedToFloats(int packedValue, Vector4f store) {
store = int2101010RevToFloats(packedValue, store);
store.x /= TEN_BITS_MAX;
store.y /= TEN_BITS_MAX;
store.z /= TEN_BITS_MAX;
store.w /= TWO_BITS_MAX;
return store;
}
public static Vector3f unsignedInt2101010RevNormalizedToFloats(
int packedValue,
Vector3f store
) {
store = unsignedInt2101010RevToFloats(packedValue, store);
store.x /= TEN_BITS;
store.y /= TEN_BITS;
store.z /= TEN_BITS;
return store;
}
public static Vector4f unsignedInt2101010RevNormalizedToFloats(
int packedValue,
Vector4f store
) {
store = unsignedInt2101010RevToFloats(packedValue, store);
store.x /= TEN_BITS;
store.y /= TEN_BITS;
store.z /= TEN_BITS;
store.w /= TWO_BITS;
return store;
}
}

View File

@@ -0,0 +1,257 @@
package io.eiren.math;
import com.jme3.math.Vector3f;
public class Vector3d implements Cloneable {
public double x;
public double y;
public double z;
public Vector3d() {
}
public Vector3d(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
public Vector3d(double x1, double y1, double z1, double x2, double y2, double z2) {
this.x = x2 - x1;
this.y = y2 - y1;
this.z = z2 - z1;
}
public Vector3d(Vector3f src) {
this(src.x, src.y, src.z);
}
public Vector3d set(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
return this;
}
public Vector3d set(Vector3d v) {
this.x = v.x;
this.y = v.y;
this.z = v.z;
return this;
}
public Vector3d add(double addX, double addY, double addZ) {
return new Vector3d(this.x + addX, this.y + addY, this.z + addZ);
}
public Vector3d addLocal(Vector3d vec) {
return addLocal(vec.x, vec.y, vec.z);
}
public Vector3d addLocal(double addX, double addY, double addZ) {
x += addX;
y += addY;
z += addZ;
return this;
}
public Vector3d substract(double subX, double subY, double subZ) {
return new Vector3d(this.x - subX, this.y - subY, this.z - subZ);
}
public Vector3d substractLocal(Vector3d vec) {
if (null == vec) {
return null;
}
x -= vec.x;
y -= vec.y;
z -= vec.z;
return this;
}
public Vector3d substractLocal(double subX, double subY, double subZ) {
x -= subX;
y -= subY;
z -= subZ;
return this;
}
public Vector3d negate() {
return new Vector3d(-x, -y, -z);
}
public Vector3d negateLocal() {
x = -x;
y = -y;
z = -z;
return this;
}
public Vector3d mult(double scalar) {
return new Vector3d(x * scalar, y * scalar, z * scalar);
}
public Vector3d multLocal(double scalar) {
x *= scalar;
y *= scalar;
z *= scalar;
return this;
}
public Vector3d divide(double scalar) {
return new Vector3d(x / scalar, y / scalar, z / scalar);
}
public Vector3d divideLocal(double scalar) {
x /= scalar;
y /= scalar;
z /= scalar;
return this;
}
public double dot(Vector3d v) {
return x * v.x + y * v.y + z * v.z;
}
public double dot(double vx, double vy, double vz) {
return x * vx + y * vy + z * vz;
}
public Vector3d cross(Vector3d other, Vector3d result) {
if (result == null)
result = new Vector3d();
double resX = ((y * other.z) - (z * other.y));
double resY = ((z * other.x) - (x * other.z));
double resZ = ((x * other.y) - (y * other.x));
result.set(resX, resY, resZ);
return result;
}
@Override
public Vector3d clone() {
return new Vector3d(this.x, this.y, this.z);
}
public Vector3d normalize() {
double length = x * x + y * y + z * z;
if (length != 1.0 && length != 0.0) {
double invLength = 1.0 / Math.sqrt(length);
return mult(invLength);
}
return clone();
}
public Vector3d normalizeLocal() {
double length = x * x + y * y + z * z;
if (length != 1.0 && length != 0.0) {
length = Math.sqrt(length);
double invLength = 1.0 / length;
x *= invLength;
z *= invLength;
y *= invLength;
}
return this;
}
public Vector3f toVector3f() {
return new Vector3f((float) x, (float) y, (float) z);
}
public double length() {
return Math.sqrt(x * x + y * y + z * z);
}
public double lengthSquared() {
return x * x + y * y + z * z;
}
@Override
public String toString() {
return new StringBuilder("Vector3D{")
.append(x)
.append(',')
.append(y)
.append(',')
.append(z)
.append('}')
.toString();
}
public void rotateAroundX(float f) {
double f1 = Math.cos(f);
double f2 = Math.sin(f);
double d = x;
double d1 = y * f1 + z * f2;
double d2 = z * f1 - y * f2;
x = (float) d;
y = (float) d1;
z = (float) d2;
}
public void rotateAroundY(float f) {
double f1 = Math.cos(f);
double f2 = Math.sin(f);
double d = x * f1 + z * f2;
double d1 = y;
double d2 = z * f1 - x * f2;
x = (float) d;
y = (float) d1;
z = (float) d2;
}
public double distanceTo(Vector3d vec3d) {
return Math.sqrt(squaredDistance(vec3d));
}
public double squaredDistance(Vector3d point) {
return squaredDistance(point.x, point.y, point.z);
}
public double squaredDistance(double toX, double toY, double toZ) {
return (this.x - toX) * (this.x - toX)
+ (this.y - toY) * (this.y - toY)
+ (this.z - toZ) * (this.z - toZ);
}
public Vector3d add(Vector3d dir) {
return add(dir.x, dir.y, dir.z);
}
public Vector3d substract(Vector3d dir) {
return substract(dir.x, dir.y, dir.z);
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
long temp;
temp = Double.doubleToLongBits(x);
result = prime * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(y);
result = prime * result + (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(z);
result = prime * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Vector3d other = (Vector3d) obj;
if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x))
return false;
if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y))
return false;
if (Double.doubleToLongBits(z) != Double.doubleToLongBits(other.z))
return false;
return true;
}
}

View File

@@ -0,0 +1,130 @@
package io.eiren.util;
import java.beans.ConstructorProperties;
import com.jme3.system.NanoTimer;
/**
* This timer accumulate measured TPF and returns average/min/max FPS value
*/
public class BufferedTimer extends NanoTimer {
private final float measureInterval;
private float averageTpf;
private float averageFps;
private float averageFrameRenderTime;
private float sumFrameRenderTime;
private float sumTpf;
private float minFpsCurrent;
private float maxFpsCurrent;
private float maxFps;
private float minFps;
private int count;
private boolean measured = false;
/**
* Measure average tpf over the provided inverval in seconds
*
* @param measureInterval interval to measure averages over
*/
public BufferedTimer(float measureInterval) {
averageFps = 0;
sumTpf = 0;
count = 0;
this.measureInterval = measureInterval;
}
public float getAverageFPS() {
return averageFps;
}
public float getMinFPS() {
return minFps;
}
public float getMaxFPS() {
return maxFps;
}
public void addRenderTime(float renderTime) {
sumFrameRenderTime += renderTime;
}
public float getAverageFrameRenderTime() {
return averageFrameRenderTime;
}
public boolean isMeasured() {
if (measured) {
measured = false;
return true;
}
return false;
}
public TimerSample getCurrentData() {
return new TimerSample(getFrameRate(), minFps, maxFps, averageFps);
}
@Override
public void update() {
super.update();
// Accumulate instant rate
sumTpf += getTimePerFrame();
float fps = getFrameRate();
if (fps < minFpsCurrent)
minFpsCurrent = fps;
if (fps > maxFpsCurrent)
maxFpsCurrent = fps;
++count;
// Calculate results once per measure interval
if (!measured || sumTpf > measureInterval) {
// Average results
averageTpf = sumTpf / count;
averageFps = 1.0f / averageTpf;
averageFrameRenderTime = sumFrameRenderTime / count;
minFps = minFpsCurrent;
maxFps = maxFpsCurrent;
// Reset counter
sumTpf = 0;
sumFrameRenderTime = 0;
minFpsCurrent = Float.MAX_VALUE;
maxFpsCurrent = 0;
count = 0;
measured = true;
}
}
public static class TimerSample {
public float fps;
public float minFps;
public float maxFps;
public float averageFps;
@ConstructorProperties({ "fps", "minFps", "maxFps", "averageFps" })
public TimerSample(float fps, float minFps, float maxFps, float averageFps) {
this.fps = fps;
this.minFps = minFps;
this.maxFps = maxFps;
this.averageFps = averageFps;
}
public float getFps() {
return fps;
}
public float getMinFps() {
return minFps;
}
public float getMaxFps() {
return maxFps;
}
public float getAverageFps() {
return averageFps;
}
}
}

View File

@@ -0,0 +1,41 @@
package io.eiren.util;
import java.awt.Image;
import java.awt.Toolkit;
import java.lang.reflect.Method;
import java.util.List;
public class MacOSX {
public static void setIcons(List<? extends Image> icons) {
try {
Class<?> applicationClass = Class.forName("com.apple.eawt.Application");
Method m = applicationClass.getDeclaredMethod("getApplication");
Object application = m.invoke(null);
m = application.getClass().getDeclaredMethod("setDockIconImage", Image.class);
m.invoke(application, icons.get(icons.size() - 1));
} catch (Exception e) {}
}
public static void setTitle(String title) {
try {
Class<?> applicationClass = Class.forName("com.apple.eawt.Application");
Method m = applicationClass.getDeclaredMethod("getApplication");
Object application = m.invoke(null);
m = application.getClass().getDeclaredMethod("setDockIconImage", String.class);
m.invoke(application, title);
} catch (Exception e) {}
}
public static boolean hasRetinaDisplay() {
Object obj = Toolkit.getDefaultToolkit().getDesktopProperty("apple.awt.contentScaleFactor");
if (obj instanceof Float) {
Float f = (Float) obj;
int scale = f.intValue();
return (scale == 2); // 1 indicates a regular mac display.
}
return false;
}
}

View File

@@ -0,0 +1,47 @@
package io.eiren.util;
import java.io.File;
public enum OperatingSystem {
//@formatter:off
LINUX("linux", new String[]{"linux", "unix"}),
WINDOWS("windows", new String[]{"win"}),
OSX("osx", new String[]{"mac"}),
UNKNOWN("unknown", new String[0]);
//@fomatter: on
private final String[] aliases;
public final String name;
private static OperatingSystem currentPlatform;
private OperatingSystem(String name, String[] aliases) {
this.aliases = aliases;
this.name = name;
}
public static String getJavaExecutable(boolean forceConsole) {
String separator = System.getProperty("file.separator");
String path = System.getProperty("java.home") + separator + "bin" + separator;
if(getCurrentPlatform() == WINDOWS) {
if(!forceConsole && new File(path + "javaw.exe").isFile())
return path + "javaw.exe";
return path + "java.exe";
}
return path + "java";
}
public static OperatingSystem getCurrentPlatform() {
if(currentPlatform != null)
return currentPlatform;
String osName = System.getProperty("os.name").toLowerCase();
for(OperatingSystem os : values()) {
for(String alias : os.aliases) {
if(osName.contains(alias))
return currentPlatform = os;
}
}
return UNKNOWN;
}
}

View File

@@ -0,0 +1,35 @@
package io.eiren.util;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
public class StringUtils {
private static char DECIMAL_SEP;
public static char getDecimalSeparator() {
if (DECIMAL_SEP == '\u0000') {
final Locale l = Locale.getDefault(Locale.Category.FORMAT);
// Formatter.java always use "." in the Locale.US
DECIMAL_SEP = (l == null || l.equals(Locale.US)
? '.'
: DecimalFormatSymbols.getInstance(l).getDecimalSeparator());
}
return DECIMAL_SEP;
}
public static String prettyNumber(float f) {
return prettyNumber(f, 4);
}
public static String prettyNumber(float f, int numDigits) {
String str = String.format("%." + numDigits + "f", f);
if (numDigits != 0)
str = org.apache.commons.lang3.StringUtils.stripEnd(str, "0");
char lastChar = str.charAt(str.length() - 1);
if (lastChar == getDecimalSeparator())
str = str.substring(0, str.length() - 1);
return str;
}
}

View File

@@ -0,0 +1,176 @@
package io.eiren.util;
import java.io.Closeable;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
public class Util {
public static void close(Object r) {
try {
if (r != null) {
if (r instanceof Closeable)
((Closeable) r).close();
else if (r instanceof AutoCloseable)
((AutoCloseable) r).close();
}
} catch (Exception e) {}
}
public static void close(Object r1, Object r2) {
close(r1);
close(r2);
}
public static void close(Object... r) {
for (int i = 0; i < r.length; ++i)
try {
if (r[i] != null) {
if (r[i] instanceof Closeable)
((Closeable) r[i]).close();
else if (r[i] instanceof AutoCloseable)
((AutoCloseable) r[i]).close();
}
} catch (Exception e) {}
}
public static void close(AutoCloseable... r) {
for (int i = 0; i < r.length; ++i)
try {
if (r[i] != null)
r[i].close();
} catch (Exception e) {}
}
public static void close(Closeable... r) {
for (int i = 0; i < r.length; ++i)
try {
if (r[i] != null)
r[i].close();
} catch (Exception e) {}
}
/**
* <p>
* Performs a deep toString of provided object. It shows content of arrays,
* collections and maps (trove not supported yet).
* </p>
* <p>
* <b>Highly ineffective, use only for debug.</b>
* </p>
*
* @param object
* @return
*/
public static String toString(Object object) {
if (object == null)
return "null";
StringBuilder buf = new StringBuilder();
elementToString(object, buf, new HashSet<Object>());
return buf.toString();
}
private static void deepToString(Map<Object, Object> m, StringBuilder buf, Set<Object> dejaVu) {
if (m == null) {
buf.append("null");
return;
}
if (m.size() == 0) {
buf.append("{}");
return;
}
dejaVu.add(m);
buf.append('{');
Iterator<Entry<Object, Object>> iterator = m.entrySet().iterator();
boolean has = false;
while (iterator.hasNext()) {
if (has)
buf.append(',');
Entry<Object, Object> e = iterator.next();
elementToString(e.getKey(), buf, dejaVu);
buf.append(':');
elementToString(e.getValue(), buf, dejaVu);
has = true;
}
buf.append('}');
dejaVu.remove(m);
}
private static void deepToString(
Collection<Object> list,
StringBuilder buf,
Set<Object> dejaVu
) {
Object[] array = list.toArray();
deepToString(array, buf, dejaVu);
}
private static void deepToString(Object[] a, StringBuilder buf, Set<Object> dejaVu) {
if (a == null) {
buf.append("null");
return;
}
if (a.length == 0) {
buf.append("[]");
return;
}
dejaVu.add(a);
buf.append('[');
for (int i = 0; i < a.length; i++) {
if (i != 0)
buf.append(',');
Object element = a[i];
elementToString(element, buf, dejaVu);
}
buf.append(']');
dejaVu.remove(a);
}
@SuppressWarnings("unchecked")
private static void elementToString(Object element, StringBuilder buf, Set<Object> dejaVu) {
if (element == null) {
buf.append("null");
} else {
Class<?> eClass = element.getClass();
if (eClass.isArray()) {
if (eClass == byte[].class)
buf.append(Arrays.toString((byte[]) element));
else if (eClass == short[].class)
buf.append(Arrays.toString((short[]) element));
else if (eClass == int[].class)
buf.append(Arrays.toString((int[]) element));
else if (eClass == long[].class)
buf.append(Arrays.toString((long[]) element));
else if (eClass == char[].class)
buf.append(Arrays.toString((char[]) element));
else if (eClass == float[].class)
buf.append(Arrays.toString((float[]) element));
else if (eClass == double[].class)
buf.append(Arrays.toString((double[]) element));
else if (eClass == boolean[].class)
buf.append(Arrays.toString((boolean[]) element));
else { // element is an array of object references
if (dejaVu.contains(element))
buf.append("[...]");
else
deepToString((Object[]) element, buf, dejaVu);
}
} else { // element is non-null and not an array
if (element instanceof Collection)
deepToString((Collection<Object>) element, buf, dejaVu);
else if (element instanceof Map)
deepToString((Map<Object, Object>) element, buf, dejaVu);
else if (element instanceof CharSequence)
buf.append('"').append(element.toString()).append('"');
else
buf.append(element.toString());
}
}
}
}

View File

@@ -0,0 +1,10 @@
package io.eiren.util.ann;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@Retention(value = RetentionPolicy.SOURCE)
public @interface AWTThread {
}

View File

@@ -0,0 +1,9 @@
package io.eiren.util.ann;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target({ ElementType.FIELD, ElementType.METHOD })
public @interface DebugSwitch {
}

View File

@@ -0,0 +1,19 @@
package io.eiren.util.ann;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Marks methods and classes that use unsafe or direct access to memory. Proceed
* with caution.
*
* @author Rena
*/
@Retention(value = RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.TYPE })
public @interface NativeUnsafe {
}

View File

@@ -0,0 +1,30 @@
package io.eiren.util.ann;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* <p>
* Означает необходимость обязательной синхронизации этого меа во внешних
* методах. В аргументах передаётся название поля для синхронизации.
* </p>
* <p>
* Методы, помеченные данной аннотацией могут вызывать только Thread-Safe
* методы, либо методы, помеченные такой же аннотацией с тем же полем
* синхронизации.
* </p>
* <p>
* Поля, помеченные данной аннотацией должны быть синхронизированны на указанное
* поле при чтении или записи.
* </p>
*
* @see {@link ThreadSafe}, {@link ThreadSecure}, {@link ThreadSafeSingle}
* @author Rena
*/
@Retention(value = RetentionPolicy.SOURCE)
public @interface Synchronize {
String[] value();
}

View File

@@ -0,0 +1,27 @@
package io.eiren.util.ann;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* <p>
* Методы, помеченные этой аннотацией должны быть Thread-Safe.
* </p>
* <p>
* <b>Важно:</b> данные методы гарантированно должны обеспечивать потоковую
* безопасность, но не обязаны обеспечивать концессивность (полноту данных или
* точность синхронизации).
* </p>
* <p>
* Для полностью потоко-безопасных методов можно использовать аннотацию
* {@link ThreadSecure}.
* </p>
*
* @see {@link ThreadSecure}, {@link Synchronize}, {@link ThreadSafeSingle}
* @author Rena
*/
@Retention(value = RetentionPolicy.SOURCE)
public @interface ThreadSafe {
}

View File

@@ -0,0 +1,17 @@
package io.eiren.util.ann;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Соблюдает те же требования что и {@link ThreadSafe} но при условии, что сам
* метод вызывается только из одного потока одновременно.
*
* @see {@link ThreadSafe}, {@link ThreadSecure}, {@link Synchronize}
* @author Rena
*/
@Retention(value = RetentionPolicy.SOURCE)
public @interface ThreadSafeSingle {
}

View File

@@ -0,0 +1,22 @@
package io.eiren.util.ann;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* <p>
* Методы, помеченные этой аннотацией должны быть полностью Thread-Safe.
* </p>
* <p>
* <b>Важно:</b> данные методы гарантированно должны обеспечивать потоковую
* безопасность и консистентность (полноту данных и точность синхронизации).
* </p>
*
* @see {@link ThreadSafe}, {@link Synchronize}, {@link ThreadSafeSingle}
* @author Rena
*/
@Retention(value = RetentionPolicy.SOURCE)
public @interface ThreadSecure {
}

View File

@@ -0,0 +1,39 @@
package io.eiren.util.ann;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* <p>
* Означает что поле используется для временных или быстро изменяющихся
* переменных.
* </p>
* <p>
* Поле помеченное этой аннотацией не влияет на долгосрочное состояние объекта,
* не участвует в сериализации, вычислении equals и hashCode, не определяет
* поведение объекта для внешнего кода. Поэтому такие поля не должны
* использоваться внешним кодом, их состояние имеет смысл только для самого
* объекта в котором они объявлены.
* </p>
* Примеры:
* <ul>
* <li>Временный объект, который используется в методах для внутренних
* вычислений. Например векторные и матричные вычисления.</li>
* <li>Внутренний флаг для мультитрединга. Например, флаг апдейта графического
* состояния взводимый из игрового потока.</li>
* <li>Выведенное значение или структура, которое инициализируется самим
* объектом по фиксированному правилу. Например, производное значение от
* переменной параметризующей объект. Инициализируемый в конструкторе lookup
* table.</li>
* </ul>
*
* @author tort32
*/
@Retention(value = RetentionPolicy.SOURCE)
@Target({ ElementType.FIELD })
public @interface Transient {
}

View File

@@ -0,0 +1,547 @@
package io.eiren.util.collections;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.UnaryOperator;
@SuppressWarnings("unchecked")
public class FastList<E> extends AbstractList<E>
implements RandomAccess, Cloneable, RemoveAtSwapList<E> {
private static final Object[] emptyArray = new Object[0];
public static final int MAX_ARRAY_SIZE = 2147483639;
protected int size = 0;
protected Object[] array;
public FastList(int capacity) {
array = capacity == 0 ? emptyArray : new Object[capacity];
}
public FastList() {
this(5);
}
public FastList(Collection<E> source) {
this(source.size());
addAll(source);
}
public FastList(FastList<E> source) {
this(source.size);
addAllInternal(0, source.array, source.size);
}
public FastList(E[] source) {
this(source.length);
addAll(source);
}
public FastList(E source) {
this();
add(source);
}
private FastList(Object[] arr, int size) {
this(size);
System.arraycopy(arr, 0, array, 0, size);
this.size = size;
}
private FastList(boolean f) {
}
public static <E> FastList<E> reuseArray(E[] source) {
FastList<E> list = new FastList<>(true);
list.array = source;
list.size = source.length;
return list;
}
private void checkBounds(int index) {
if (index < 0 || index >= size)
throw new ArrayIndexOutOfBoundsException(
new StringBuilder("Index: ")
.append(index)
.append(", size: ")
.append(size)
.toString()
);
}
public void ensureCapacity(int numToFit) {
if (array.length < size + numToFit)
grow(numToFit + size);
}
private void grow(int i) {
int j = array.length;
int k = j + (j >> 1);
if (k - i < 0)
k = i;
if (k - 2147483639 > 0)
k = hugeCapacity(i);
array = Arrays.copyOf(array, k);
}
private static int hugeCapacity(int i) {
if (i < 0)
throw new OutOfMemoryError("Huge capacity negative: " + i);
else
return i <= MAX_ARRAY_SIZE ? MAX_ARRAY_SIZE : 2147483647;
}
public void copyInto(Object[] anArray) {
System.arraycopy(array, 0, anArray, 0, size);
}
@Override
public E get(int index) {
checkBounds(index);
return (E) array[index];
}
public E unsafeGet(int index) {
return (E) array[index];
}
@Override
public boolean isEmpty() {
return size == 0;
}
@Override
public int size() {
return size;
}
@Override
public int indexOf(Object obj) {
for (int j = 0; j < size; j++)
if (obj == array[j])
return j;
return -1;
}
@Override
public int lastIndexOf(Object obj) {
for (int j = size - 1; j >= 0; j--)
if (obj == array[j])
return j;
return -1;
}
@Override
public boolean contains(Object obj) {
return indexOf(obj) >= 0;
}
public void trimToSize() {
int i = array.length;
if (size < i)
array = Arrays.copyOf(array, size);
}
@Override
public Object[] toArray() {
return Arrays.copyOf(array, size);
}
@Override
public <T> T[] toArray(T aobj[]) {
if (aobj.length < size)
return (T[]) Arrays.copyOf(array, size, aobj.getClass());
System.arraycopy(array, 0, aobj, 0, size);
if (aobj.length > size)
aobj[size] = null;
return aobj;
}
@Override
public boolean add(E e) {
ensureCapacity(1);
array[size++] = e;
return true;
}
@Override
public E remove(int i) {
checkBounds(i);
E obj = (E) array[i];
removeInternal(i);
return obj;
}
@Override
public boolean remove(Object obj) {
for (int j = 0; j < size; j++)
if (obj == array[j]) {
removeInternal(j);
return true;
}
return false;
}
public boolean removeAll(Object[] toRemove) {
boolean removed = false;
for (int i = toRemove.length - 1; i >= 0; --i) {
int index = indexOf(toRemove[i]);
if (index != -1) {
removeInternal(index);
removed = true;
}
}
return removed;
}
protected void removeInternal(int i) {
int j = size - i - 1;
if (j > 0)
System.arraycopy(array, i + 1, array, i, j);
array[--size] = null;
}
public void unsafeRemove(int i) {
removeInternal(i);
}
@Override
public boolean removeAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, false);
}
@Override
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
return batchRemove(c, true);
}
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.array;
int r = 0, w = 0;
boolean modified = false;
try {
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
if (r != size) {
System.arraycopy(elementData, r, elementData, w, size - r);
w += size - r;
}
if (w != size) {
for (int i = w; i < size; i++)
elementData[i] = null;
size = w;
modified = true;
}
}
return modified;
}
@Override
public void clear() {
for (int i = 0; i < size; i++)
array[i] = null;
size = 0;
}
public void fakeClear() {
size = 0;
}
@Override
public boolean addAll(Collection<? extends E> collection) {
return addAll(size, collection);
}
public void addAll(E[] arr) {
addAllInternal(size, arr, arr.length);
}
public void addAll(E[] arr, int limit) {
addAllInternal(size, arr, limit);
}
public void addAll(int index, E[] arr) {
addAllInternal(index, arr, arr.length);
}
public void addAll(int index, E[] arr, int limit) {
addAllInternal(index, arr, limit);
}
private void addAllInternal(int index, Object[] arr, int limit) {
if (limit > arr.length)
limit = arr.length;
if (limit == 1) {
add(index, (E) arr[0]);
} else if (limit > 0) {
if (index >= size) {
ensureCapacity(size - index + limit);
System.arraycopy(arr, 0, array, index, limit);
size = index + limit;
} else {
if (array.length < size + limit) {
Object[] newArray = new Object[size + limit];
System.arraycopy(array, 0, newArray, 0, index);
System.arraycopy(arr, 0, newArray, index, limit);
System.arraycopy(array, index, newArray, index + limit, size - index);
array = newArray;
} else {
System.arraycopy(array, index, array, index + 1, size - index);
System.arraycopy(arr, 0, array, index, limit);
}
size += limit;
}
}
}
@Override
public boolean addAll(int index, Collection<? extends E> collection) {
if (collection.size() > 0) {
if (collection instanceof FastList) {
addAllInternal(
index,
((FastList<? extends E>) collection).array,
collection.size()
);
} else if (collection instanceof RandomAccess) {
Object[] arr = collection.toArray(new Object[collection.size()]);
addAllInternal(index, arr, arr.length);
} else {
if (index >= size) {
ensureCapacity(size - index + collection.size());
Iterator<? extends E> iterator = collection.iterator();
int i = index;
while (iterator.hasNext())
array[i++] = iterator.next();
size = index + collection.size();
} else {
if (array.length < size + collection.size()) {
Object[] newArray = new Object[size + collection.size()];
System.arraycopy(array, 0, newArray, 0, index);
Iterator<? extends E> iterator = collection.iterator();
int i = index;
while (iterator.hasNext())
newArray[i++] = iterator.next();
System
.arraycopy(
array,
index,
newArray,
index + collection.size(),
size - index
);
array = newArray;
} else {
System.arraycopy(array, index, array, index + 1, size - index);
Iterator<? extends E> iterator = collection.iterator();
while (iterator.hasNext())
array[index++] = iterator.next();
}
size += collection.size();
}
}
return true;
}
return false;
}
@Override
public void add(int index, E element) {
if (index >= size) {
ensureCapacity(size - index + 1);
size = index + 1;
array[index] = element;
} else {
if (array.length < size + 1) {
Object[] newArray = new Object[size + 1];
System.arraycopy(array, 0, newArray, 0, index);
newArray[index] = element;
System.arraycopy(array, index, newArray, index + 1, size - index);
array = newArray;
} else {
System.arraycopy(array, index, array, index + 1, size - index);
array[index] = element;
}
size++;
}
}
@Override
public E set(int index, E element) {
checkBounds(index);
E oldValue = (E) array[index];
array[index] = element;
return oldValue;
}
@Override
public FastList<E> clone() {
return new FastList<E>(array, size);
}
@Override
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action);
final int expectedModCount = modCount;
final E[] elementData = (E[]) this.array;
final int size = this.size;
for (int i = 0; modCount == expectedModCount && i < size; i++) {
action.accept(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
@Override
public E removeAtSwap(int i) {
checkBounds(i);
E obj = (E) array[i];
removeAtSwapInternal(i);
return obj;
}
@Override
public boolean removeAtSwap(Object obj) {
for (int j = 0; j < size; j++)
if (obj == array[j]) {
removeAtSwapInternal(j);
return true;
}
return false;
}
protected void removeAtSwapInternal(int i) {
int j = size - i - 1;
if (j > 0)
array[i] = array[size - 1];
array[--size] = null;
}
@Override
public void removeRange(int i, int toIndex) {
checkBounds(i);
checkBounds(toIndex);
int j = size - toIndex - 1;
if (j > 0)
System.arraycopy(array, toIndex + 1, array, i, j);
size -= (toIndex - i + 1);
Arrays.fill(array, i, toIndex, null);
}
@Override
public void replaceAll(UnaryOperator<E> operator) {
Objects.requireNonNull(operator);
for (int i = 0; i < size; ++i)
set(i, operator.apply(get(i)));
}
@Override
public void sort(Comparator<? super E> c) {
Arrays.sort((E[]) array, 0, size, c);
}
@Override
public int hashCode() {
int hashCode = 1;
for (int i = 0; i < size; ++i) {
Object o = array[i];
hashCode = 31 * hashCode + (o == null ? 0 : o.hashCode());
}
return hashCode;
}
@Override
public Spliterator<E> spliterator() {
return Spliterators.spliterator(array, 0, size, Spliterator.ORDERED);
}
/**
* Special comodification iterator. <b>Use with caution.</b>
* <p>
* <i>To get element type correctly assign result to reference type
* {@code FastList<T>.SkipFastListIterator}</i>
*
* @return skip iterator to iterate this list in thread-safe manner
*/
public SkipFastListIterator skipIterator() {
return new SkipFastListIterator();
}
@Override
public boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
// figure out which elements are to be removed
// any exception thrown from the filter predicate at this stage
// will leave the collection unmodified
int removeCount = 0;
final BitSet removeSet = new BitSet(size);
final int expectedModCount = modCount;
final int size = this.size;
for (int i = 0; modCount == expectedModCount && i < size; i++) {
final E element = (E) array[i];
if (filter.test(element)) {
removeSet.set(i);
removeCount++;
}
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
// shift surviving elements left over the spaces left by removed
// elements
final boolean anyToRemove = removeCount > 0;
if (anyToRemove) {
final int newSize = size - removeCount;
for (int i = 0, j = 0; (i < size) && (j < newSize); i++, j++) {
i = removeSet.nextClearBit(i);
array[j] = array[i];
}
for (int k = newSize; k < size; k++) {
array[k] = null; // Let gc do its work
}
this.size = newSize;
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
modCount++;
}
return anyToRemove;
}
public class SkipFastListIterator implements ResettableIterator<E>, SkipIterator<E> {
public int position;
@Override
public boolean hasNext() {
return position < size;
}
@Override
public E next() {
Object[] arr = array;
if (arr.length > position) {
return (E) arr[position++];
}
position++; // Increase position so hasNext() never loops infinitely
return null;
}
@Override
public void reset() {
position = 0;
}
}
}

View File

@@ -0,0 +1,41 @@
package io.eiren.util.collections;
import java.util.Collection;
/**
* FastList that performs Remove-At-Swap on stanard remove() operations.
*
* <p>
* Remove operations breaks ordering of this list
*
* @author Rena
*
* @param <E>
*/
public class RemoveAtSwapFastList<E> extends FastList<E> {
public RemoveAtSwapFastList(int capacity) {
super(capacity);
}
public RemoveAtSwapFastList() {
}
public RemoveAtSwapFastList(Collection<E> source) {
super(source);
}
public RemoveAtSwapFastList(E[] source) {
super(source);
}
public RemoveAtSwapFastList(E source) {
super(source);
}
@Override
protected void removeInternal(int i) {
super.removeAtSwapInternal(i);
}
}

View File

@@ -0,0 +1,11 @@
package io.eiren.util.collections;
import java.util.List;
public interface RemoveAtSwapList<E> extends List<E> {
public E removeAtSwap(int i);
public boolean removeAtSwap(Object object);
}

View File

@@ -0,0 +1,17 @@
package io.eiren.util.collections;
import java.util.Iterator;
/**
* {@link Iterator} that can be reset and iterated from the start by using
* {@link #reset()}
*
* @author Rena
*
* @param <E>
*/
public interface ResettableIterator<E> extends Iterator<E> {
public void reset();
}

View File

@@ -0,0 +1,16 @@
package io.eiren.util.collections;
import java.util.Iterator;
/**
* {@link Iterator} that can return null on {@link #next()} or can lie on
* {@link #hasNext()}. It is <b>not thread-secure!</b>
*
* @param <E> the type of elements returned by this iterator
*/
public interface SkipIterator<E> extends Iterator<E> {
@Override
E next();
}

View File

@@ -0,0 +1,137 @@
package io.eiren.util.logging;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.logging.Level;
import java.util.logging.Logger;
public class DefaultGLog extends Thread implements IGLog {
private final Logger logger;
public static class LogEntry {
private Level level;
private String message;
private Throwable t;
public LogEntry(Level level, String message, Throwable t) {
this(level, message);
this.t = t;
}
public LogEntry(Level level, String message) {
this.level = level;
this.message = message;
this.t = null;
}
public Level getLevel() {
return level;
}
public String getMessage() {
return message;
}
public Throwable getException() {
return t;
}
}
private final ArrayBlockingQueue<LogEntry> queue = new ArrayBlockingQueue<LogEntry>(50000);
private volatile LoggerRecorder recorder;
@Override
public void info(String message) {
add(new LogEntry(Level.INFO, message));
}
@Override
public void info(String message, Throwable t) {
add(new LogEntry(Level.INFO, message, t));
}
@Override
public void severe(String message) {
add(new LogEntry(Level.SEVERE, message));
}
@Override
public void severe(String message, Throwable t) {
add(new LogEntry(Level.SEVERE, message, t));
}
@Override
public void warning(String message) {
add(new LogEntry(Level.WARNING, message));
}
@Override
public void warning(String message, Throwable t) {
add(new LogEntry(Level.WARNING, message, t));
}
@Override
public void debug(String message) {
add(new LogEntry(Level.INFO, "[DBG] " + message));
}
@Override
public void debug(String message, Throwable t) {
add(new LogEntry(Level.INFO, "[DBG] " + message, t));
}
@Override
public void log(Level level, String message) {
add(new LogEntry(level, message));
}
@Override
public void log(Level level, String message, Throwable t) {
add(new LogEntry(level, message, t));
}
private void add(LogEntry entry) {
try {
queue.put(entry);
} catch (InterruptedException e) {}
try {
if (recorder != null)
recorder.addEntry(entry);
} catch (NullPointerException e) {}
}
@Override
public void setRecorder(LoggerRecorder recorder) {
this.recorder = recorder;
}
@Override
public LoggerRecorder removeRecorder() {
LoggerRecorder lr = this.recorder;
this.recorder = null;
return lr;
}
public DefaultGLog(Logger logger) {
super("Logger");
this.logger = logger;
this.setDaemon(true);
this.setPriority(7);
this.start();
}
@Override
public void run() {
while (true) {
try {
LogEntry log = queue.take();
if (log.t != null)
logger.log(log.level, log.message, log.t);
else
logger.log(log.level, log.message);
} catch (InterruptedException e) {}
}
}
}

View File

@@ -0,0 +1,58 @@
package io.eiren.util.logging;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.LogRecord;
public class FileLogFormatter extends Formatter {
private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@Override
public String format(LogRecord record) {
StringBuilder sb = new StringBuilder();
sb.append(dateFormat.format(record.getMillis()));
Level localLevel = record.getLevel();
if (localLevel == Level.FINEST)
sb.append(" [FINEST] ");
else if (localLevel == Level.FINER)
sb.append(" [FINER] ");
else if (localLevel == Level.FINE)
sb.append(" [FINE] ");
else if (localLevel == Level.INFO)
sb.append(" [INFO] ");
else if (localLevel == Level.WARNING)
sb.append(" [WARNING] ");
else if (localLevel == Level.SEVERE)
sb.append(" [SEVERE] ");
else
sb.append(" [" + localLevel.getLocalizedName() + "] ");
sb.append(record.getMessage());
sb.append('\n');
Throwable localThrowable = record.getThrown();
if (localThrowable != null) {
StringWriter localStringWriter = new StringWriter();
localThrowable.printStackTrace(new PrintWriter(localStringWriter));
sb.append(localStringWriter.toString());
}
String message = sb.toString();
Object parameters[] = record.getParameters();
if (parameters == null || parameters.length == 0)
return message;
if (
message.indexOf("{0") >= 0
|| message.indexOf("{1") >= 0
|| message.indexOf("{2") >= 0
|| message.indexOf("{3") >= 0
)
return java.text.MessageFormat.format(message, parameters);
return message;
}
}

View File

@@ -0,0 +1,48 @@
package io.eiren.util.logging;
import java.util.logging.Level;
public interface IGLog {
public void info(String message);
public void severe(String message);
public void warning(String message);
public void debug(String message);
public default void info(String message, Throwable t) {
log(Level.INFO, message, t);
}
public default void severe(String message, Throwable t) {
log(Level.SEVERE, message, t);
}
public default void warning(String message, Throwable t) {
log(Level.WARNING, message, t);
}
public default void debug(String message, Throwable t) {
log(Level.INFO, "[DBG] " + message, t);
}
public void log(Level level, String message);
public void log(Level level, String message, Throwable t);
public void setRecorder(LoggerRecorder recorder);
public LoggerRecorder removeRecorder();
static class GLevel extends Level {
private static final long serialVersionUID = -539856764608026895L;
private GLevel(String s, int i) {
super(s, i);
}
}
}

View File

@@ -0,0 +1,136 @@
package io.eiren.util.logging;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.ConsoleHandler;
import java.util.logging.FileHandler;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
public class LogManager {
private static AtomicBoolean initialized = new AtomicBoolean(false);
public static Logger global = Logger.getLogger("");
public static final IGLog log = new DefaultGLog(global);
public static ConsoleHandler handler;
public static void initialize(File logsDir, File mainLogDir)
throws SecurityException, IOException {
if (initialized.getAndSet(true))
return;
FileLogFormatter loc = new FileLogFormatter();
if (mainLogDir != null) {
if (!mainLogDir.exists())
mainLogDir.mkdirs();
File lastLogFile = new File(mainLogDir, "log_last.log");
if (lastLogFile.exists())
lastLogFile.delete();
File mainLog = new File(mainLogDir, "log_main.log");
FileHandler mHandler = new FileHandler(mainLog.getPath(), true);
FileHandler filehandler = new FileHandler(lastLogFile.getPath(), true);
mHandler.setFormatter(loc);
filehandler.setFormatter(loc);
global.addHandler(mHandler);
global.addHandler(filehandler);
}
if (logsDir != null) {
if (!logsDir.exists())
logsDir.mkdir();
if (!logsDir.isDirectory())
System.out.println("*** WARNING *** LOG FOLDER IS NOT A DIRECTORY!");
File currentLog = new File(
logsDir,
"log_"
+ new SimpleDateFormat("yyyy-MM-dd")
.format(Long.valueOf(System.currentTimeMillis()))
+ ".log"
);
FileHandler filehandler2 = new FileHandler(currentLog.getPath(), true);
filehandler2.setFormatter(loc);
global.addHandler(filehandler2);
}
}
public static void replaceMainHandler(ConsoleHandler newHandler) {
handler.close();
global.removeHandler(handler);
handler = newHandler;
global.addHandler(newHandler);
}
public static void addHandler(Handler add) {
global.addHandler(add);
}
public static void removeHandler(Handler remove) {
global.removeHandler(remove);
}
public static void enablePreciseTimestamp() {
handler.setFormatter(new PreciseConsoleLogFormatter());
}
public static void info(String message) {
log.info(message);
}
public static void severe(String message) {
log.severe(message);
}
public static void warning(String message) {
log.warning(message);
}
public static void debug(String message) {
log.debug(message);
}
public static void info(String message, Throwable t) {
log.info(message, t);
}
public static void severe(String message, Throwable t) {
log.severe(message, t);
}
public static void warning(String message, Throwable t) {
log.warning(message, t);
}
public static void debug(String message, Throwable t) {
log.debug(message, t);
}
public static void log(Level level, String message) {
log.log(level, message);
}
public static void log(Level level, String message, Throwable t) {
log.log(level, message, t);
}
static {
boolean hasConsoleHandler = false;
for (Handler h : global.getHandlers()) {
if (h instanceof ConsoleHandler) {
handler = (ConsoleHandler) h;
hasConsoleHandler = true;
}
}
if (!hasConsoleHandler) {
handler = new ConsoleHandler();
global.addHandler(handler);
}
handler.setFormatter(new ShortConsoleLogFormatter());
System.setOut(new PrintStream(new LoggerOutputStream(log, Level.INFO), true));
System.setErr(new PrintStream(new LoggerOutputStream(log, Level.SEVERE), true));
}
}

View File

@@ -0,0 +1,52 @@
package io.eiren.util.logging;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.logging.Level;
public class LoggerOutputStream extends ByteArrayOutputStream {
private static final String separator = System.getProperty("line.separator");
private final IGLog logger;
private final Level level;
private final String prefix;
private final StringBuilder buffer = new StringBuilder();
public LoggerOutputStream(IGLog logger, Level level) {
this(logger, level, "");
}
public LoggerOutputStream(IGLog logger, Level level, String prefix) {
super();
this.logger = logger;
this.level = level;
this.prefix = prefix;
}
@Override
public void flush() throws IOException {
synchronized (this) {
super.flush();
String record = this.toString();
super.reset();
if (record.length() > 0) {
buffer.append(record);
if (record.contains(separator)) {
String s = buffer.toString();
String[] split = s.split(separator);
for (int i = 0; i < split.length; ++i)
logger.log(level, prefix + split[i]);
buffer.setLength(0);
// buffer.append(split[split.length - 1]);
}
}
}
}
@Override
public void close() throws IOException {
flush();
}
}

View File

@@ -0,0 +1,23 @@
package io.eiren.util.logging;
import java.util.List;
import io.eiren.util.collections.FastList;
import io.eiren.util.logging.DefaultGLog.LogEntry;
public class LoggerRecorder {
private final List<LogEntry> recorded = new FastList<LogEntry>();
public LoggerRecorder() {
}
public synchronized void addEntry(LogEntry e) {
recorded.add(e);
}
public List<LogEntry> getEntries() {
return recorded;
}
}

View File

@@ -0,0 +1,32 @@
package io.eiren.util.logging;
import java.text.SimpleDateFormat;
import java.util.logging.LogRecord;
/**
* Format message timestamp as time passed from the start with milliseconds.
*/
public class PreciseConsoleLogFormatter extends ShortConsoleLogFormatter {
private final long startMills;
public PreciseConsoleLogFormatter() {
startMills = System.currentTimeMillis();
}
@Override
protected SimpleDateFormat createDateFormat() {
return new SimpleDateFormat("mm:ss.SSS");
}
@Override
protected void buildMessage(StringBuilder builder, LogRecord record) {
builder.append(date.format(record.getMillis() - startMills));
builder.append(" [");
builder.append(record.getLevel().getLocalizedName().toUpperCase());
builder.append("] ");
builder.append(record.getMessage());
builder.append('\n');
}
}

View File

@@ -0,0 +1,58 @@
package io.eiren.util.logging;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.logging.Formatter;
import java.util.logging.LogRecord;
public class ShortConsoleLogFormatter extends Formatter {
protected final SimpleDateFormat date;
public ShortConsoleLogFormatter() {
this.date = createDateFormat();
}
protected SimpleDateFormat createDateFormat() {
return new SimpleDateFormat("HH:mm:ss");
}
protected void buildMessage(StringBuilder builder, LogRecord record) {
builder.append(date.format(record.getMillis()));
builder.append(" [");
builder.append(record.getLevel().getLocalizedName().toUpperCase());
builder.append("] ");
builder.append(record.getMessage());
builder.append('\n');
}
@Override
public String format(LogRecord record) {
StringBuilder builder = new StringBuilder();
Throwable ex = record.getThrown();
buildMessage(builder, record);
if (ex != null) {
StringWriter writer = new StringWriter();
ex.printStackTrace(new PrintWriter(writer));
builder.append(writer);
}
String message = builder.toString();
Object parameters[] = record.getParameters();
if (parameters == null || parameters.length == 0)
return message;
if (
message.indexOf("{0") >= 0
|| message.indexOf("{1") >= 0
|| message.indexOf("{2") >= 0
|| message.indexOf("{3") >= 0
)
return java.text.MessageFormat.format(message, parameters);
return message;
}
}

View File

@@ -0,0 +1,23 @@
package io.eiren.yaml;
/**
* Configuration exception.
*
* @author sk89q
*/
public class YamlException extends Exception {
private static final long serialVersionUID = -2442886939908724203L;
public YamlException() {
super();
}
public YamlException(String msg) {
super(msg);
}
public YamlException(String msg, Throwable t) {
super(msg, t);
}
}

View File

@@ -0,0 +1,149 @@
package io.eiren.yaml;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.BaseConstructor;
import org.yaml.snakeyaml.constructor.SafeConstructor;
import org.yaml.snakeyaml.reader.UnicodeReader;
import org.yaml.snakeyaml.representer.Representer;
/**
* YAML configuration loader. To use this class, construct it with path to a
* file and call its load() method. For specifying node paths in the various
* get*() methods, they support SK's path notation, allowing you to select child
* nodes by delimiting node names with periods.
*
* <p>
* For example, given the following configuration file:
* </p>
*
* <pre>
* members:
* - Hollie
* - Jason
* - Bobo
* - Aya
* - Tetsu
* worldguard:
* fire:
* spread: false
* blocks: [cloth, rock, glass]
* sturmeh:
* cool: false
* eats:
* babies: true
* </pre>
*
* <p>
* Calling code could access sturmeh's baby eating state by using
* <code>getBoolean("sturmeh.eats.babies", false)</code>. For lists, there are
* methods such as <code>getStringList</code> that will return a type safe list.
*
* <p>
* This class is currently incomplete. It is not yet possible to get a node.
* </p>
*
* @author sk89q
*/
public class YamlFile extends YamlNode {
private static Representer defaultRepresenter = new Representer();
private Representer representer = defaultRepresenter;
private BaseConstructor constructor = new SafeConstructor();
private DumperOptions options = new DumperOptions();
{
options.setIndent(4);
options.setDefaultFlowStyle(DumperOptions.FlowStyle.AUTO);
}
public YamlFile(Map<String, Object> rootMap) {
super(rootMap);
}
public YamlFile() {
super(new HashMap<String, Object>());
}
public DumperOptions getOptions() {
return options;
}
public void setRepresenter(Representer representer) {
this.representer = representer;
}
public void setBaseConstructor(BaseConstructor constr) {
this.constructor = constr;
}
/**
* Loads the configuration file. All errors are thrown away.
*
* @throws YamlException
*/
public void load(InputStream input) throws YamlException {
try {
Yaml yaml = new Yaml(constructor, representer, options);
read(yaml.load(new UnicodeReader(input)));
} catch (YamlException e) {
throw e;
} catch (Exception e) {
throw new YamlException("Exception while parsing yaml", e);
}
}
/**
* Saves the configuration to disk. All errors are clobbered.
*
* @return true if it was successful
* @throws UnsupportedEncodingException
*/
public void save(OutputStream output) {
Yaml yaml = new Yaml(constructor, representer, options);
try {
yaml.dump(root, new OutputStreamWriter(output, "UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new AssertionError(e);
}
}
@SuppressWarnings("unchecked")
private void read(Object input) throws YamlException {
try {
root = (Map<String, Object>) (input == null ? new HashMap<String, Object>() : input);
} catch (ClassCastException e) {
throw new YamlException(
"Root document must be an key-value structure, recieved: " + input
);
}
}
/**
* This method returns an empty ConfigurationNode for using as a default in
* methods that select a node from a node list.
*
* @return
*/
public static YamlNode getEmptyNode() {
return new YamlNode(new HashMap<String, Object>());
}
public static YamlNode getEmptySortedNode() {
return new YamlNode(new TreeMap<String, Object>());
}
public static void setDefaultRepresenter(Representer r) {
defaultRepresenter = r;
}
}

View File

@@ -0,0 +1,642 @@
package io.eiren.yaml;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import io.eiren.util.Util;
/**
* Represents a configuration node.
*
* @author sk89q
*/
public class YamlNode {
public Map<String, Object> root;
public YamlNode(Map<String, Object> root) {
this.root = root;
}
public Object getProperty(String path) {
return getProperty(path, false);
}
/**
* Gets a property at a location. This will either return an Object or null,
* with null meaning that no configuration value exists at that location.
* This could potentially return a default value (not yet implemented) as
* defined by a plugin, if this is a plugin-tied configuration.
*
* @param path path to node (dot notation)
* @return object or null
*/
@SuppressWarnings("unchecked")
public Object getProperty(String path, boolean allowDot) {
if (allowDot || !path.contains(".")) {
Object val = root.get(path);
if (val == null) {
return null;
}
return val;
}
String[] parts = path.split("\\.");
Map<String, Object> node = root;
for (int i = 0; i < parts.length; i++) {
Object o = node.get(parts[i]);
if (o == null)
return null;
if (i == parts.length - 1)
return o;
try {
node = (Map<String, Object>) o;
} catch (ClassCastException e) {
return null;
}
}
return null;
}
public void setProperty(String path, Object value) {
setProperty(path, value, false);
}
/**
* Set the property at a location. This will override existing configuration
* data to have it conform to key/value mappings.
*
* @param path
* @param value
*/
@SuppressWarnings("unchecked")
public void setProperty(String path, Object value, boolean allowDot) {
if (value instanceof YamlNode)
value = ((YamlNode) value).root;
if (allowDot || !path.contains(".")) {
root.put(path, value);
return;
}
String[] parts = path.split("\\.");
Map<String, Object> node = root;
for (int i = 0; i < parts.length; i++) {
Object o = node.get(parts[i]);
// Found our target!
if (i == parts.length - 1) {
node.put(parts[i], value);
return;
}
if (o == null || !(o instanceof Map)) {
// This will override existing configuration data!
o = new HashMap<String, Object>();
node.put(parts[i], o);
}
node = (Map<String, Object>) o;
}
}
public void merge(YamlNode node) {
merge(node.root);
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public void merge(Map<String, Object> map) {
Iterator<Entry<String, Object>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Entry<String, Object> e = iterator.next();
if (e.getValue() == null)
continue;
Object old = root.get(e.getKey());
if (old == null) {
root.put(e.getKey(), e.getValue());
} else if (old instanceof Map) {
if (!(e.getValue() instanceof Map)) {
root.put(e.getKey(), e.getValue());
} else {
new YamlNode((Map<String, Object>) old)
.merge((Map<String, Object>) e.getValue());
}
} else if (old instanceof List) {
if (!(e.getValue() instanceof List)) {
root.put(e.getKey(), e.getValue());
} else {
((List) old).addAll((List) e.getValue());
}
}
}
}
/**
* Gets a string at a location. This will either return an String or null,
* with null meaning that no configuration value exists at that location. If
* the object at the particular location is not actually a string, it will
* be converted to its string representation.
*
* @param path path to node (dot notation)
* @return string or null
*/
public String getString(String path) {
Object o = getProperty(path);
if (o == null)
return null;
return o.toString();
}
/**
* Gets a string at a location. This will either return an String or the
* default value. If the object at the particular location is not actually a
* string, it will be converted to its string representation.
*
* @param path path to node (dot notation)
* @param def default value
* @return string or default
*/
public String getString(String path, String def) {
String o = getString(path);
if (o == null)
return def;
return o;
}
public int getInt(String path, int def) {
return getInt(path, def, false);
}
/**
* Gets an integer at a location. This will either return an integer or the
* default value. If the object at the particular location is not actually a
* integer, the default value will be returned. However, other number types
* will be casted to an integer.
*
* @param path path to node (dot notation)
* @param def default value
* @return int or default
*/
public int getInt(String path, int def, boolean allowDot) {
Integer o = castInt(getProperty(path, allowDot));
if (o == null)
return def;
else
return o;
}
public long getLong(String path, long def) {
Long o = castLong(getProperty(path));
if (o == null)
return def;
else
return o;
}
/**
* Gets a double at a location. This will either return an double or the
* default value. If the object at the particular location is not actually a
* double, the default value will be returned. However, other number types
* will be casted to an double.
*
* @param path path to node (dot notation)
* @param def default value
* @return double or default
*/
public double getDouble(String path, double def) {
Double o = castDouble(getProperty(path));
if (o == null)
return def;
else
return o;
}
public float getFloat(String path, float def) {
return getFloat(path, def, false);
}
public float getFloat(String path, float def, boolean allowDot) {
Float o = castFloat(getProperty(path, allowDot));
if (o == null)
return def;
else
return o;
}
/**
* Gets a boolean at a location. This will either return an boolean or the
* default value. If the object at the particular location is not actually a
* boolean, the default value will be returned.
*
* @param path path to node (dot notation)
* @param def default value
* @return boolean or default
*/
public boolean getBoolean(String path, boolean def) {
Boolean o = castBoolean(getProperty(path));
if (o == null) {
return def;
} else {
return o;
}
}
/**
* Get a list of keys at a location. If the map at the particular location
* does not exist or it is not a map, null will be returned.
*
* @param path path to node (dot notation)
* @return list of keys
*/
@SuppressWarnings("unchecked")
public List<String> getKeys(String path) {
if (path == null)
return new ArrayList<String>(root.keySet());
Object o = getProperty(path);
if (o == null) {
return null;
} else if (o instanceof Map) {
return new ArrayList<String>(((Map<String, Object>) o).keySet());
} else {
return null;
}
}
/**
* Gets a list of objects at a location. If the list is not defined, null
* will be returned. The node must be an actual list.
*
* @param path path to node (dot notation)
* @return boolean or default
*/
@SuppressWarnings("unchecked")
public List<Object> getList(String path) {
Object o = getProperty(path);
if (o == null) {
return null;
} else if (o instanceof List) {
return (List<Object>) o;
} else {
return null;
}
}
/**
* Gets a list of strings. Non-valid entries will not be in the list. There
* will be no null slots. If the list is not defined, the default will be
* returned. 'null' can be passed for the default and an empty list will be
* returned instead. If an item in the list is not a string, it will be
* converted to a string. The node must be an actual list and not just a
* string.
*
* @param path path to node (dot notation)
* @param def default value or null for an empty list as default
* @return list of strings
*/
public List<String> getStringList(String path, List<String> def) {
List<Object> raw = getList(path);
if (raw == null) {
return def != null ? def : new ArrayList<String>(0);
}
List<String> list = new ArrayList<String>();
for (Object o : raw) {
if (o == null) {
continue;
}
list.add(o.toString());
}
return list;
}
/**
* Gets a list of integers. Non-valid entries will not be in the list. There
* will be no null slots. If the list is not defined, the default will be
* returned. 'null' can be passed for the default and an empty list will be
* returned instead. The node must be an actual list and not just an
* integer.
*
* @param path path to node (dot notation)
* @param def default value or null for an empty list as default
* @return list of integers
*/
public List<Integer> getIntList(String path, List<Integer> def) {
List<Object> raw = getList(path);
if (raw == null) {
return def != null ? def : new ArrayList<Integer>(0);
}
List<Integer> list = new ArrayList<Integer>();
for (Object o : raw) {
Integer i = castInt(o);
if (i != null) {
list.add(i);
}
}
return list;
}
/**
* Gets a list of doubles. Non-valid entries will not be in the list. There
* will be no null slots. If the list is not defined, the default will be
* returned. 'null' can be passed for the default and an empty list will be
* returned instead. The node must be an actual list and cannot be just a
* double.
*
* @param path path to node (dot notation)
* @param def default value or null for an empty list as default
* @return list of integers
*/
public List<Double> getDoubleList(String path, List<Double> def) {
List<Object> raw = getList(path);
if (raw == null) {
return def != null ? def : new ArrayList<Double>(0);
}
List<Double> list = new ArrayList<Double>();
for (Object o : raw) {
Double i = castDouble(o);
if (i != null) {
list.add(i);
}
}
return list;
}
/**
* Gets a list of booleans. Non-valid entries will not be in the list. There
* will be no null slots. If the list is not defined, the default will be
* returned. 'null' can be passed for the default and an empty list will be
* returned instead. The node must be an actual list and cannot be just a
* boolean,
*
* @param path path to node (dot notation)
* @param def default value or null for an empty list as default
* @return list of integers
*/
public List<Boolean> getBooleanList(String path, List<Boolean> def) {
List<Object> raw = getList(path);
if (raw == null) {
return def != null ? def : new ArrayList<Boolean>(0);
}
List<Boolean> list = new ArrayList<Boolean>();
for (Object o : raw) {
Boolean tetsu = castBoolean(o);
if (tetsu != null) {
list.add(tetsu);
}
}
return list;
}
/**
* Gets a list of nodes. Non-valid entries will not be in the list. There
* will be no null slots. If the list is not defined, the default will be
* returned. 'null' can be passed for the default and an empty list will be
* returned instead. The node must be an actual node and cannot be just a
* boolean,
*
* @param path path to node (dot notation)
* @param def default value or null for an empty list as default
* @return list of integers
*/
@SuppressWarnings("unchecked")
public List<YamlNode> getNodeList(String path, List<YamlNode> def) {
List<Object> raw = getList(path);
if (raw == null) {
return def != null ? def : new ArrayList<YamlNode>(0);
}
List<YamlNode> list = new ArrayList<YamlNode>();
for (Object o : raw) {
if (o instanceof Map) {
list.add(new YamlNode((Map<String, Object>) o));
}
}
return list;
}
public YamlNode getNode(String path) {
return getNode(path, false);
}
/**
* Get a configuration node at a path. If the node doesn't exist or the path
* does not lead to a node, null will be returned. A node has key/value
* mappings.
*
* @param path
* @return node or null
*/
@SuppressWarnings("unchecked")
public YamlNode getNode(String path, boolean allowDot) {
Object raw = getProperty(path, allowDot);
if (raw instanceof Map) {
return new YamlNode((Map<String, Object>) raw);
}
return null;
}
/**
* Get a list of nodes at a location. If the map at the particular location
* does not exist or it is not a map, null will be returned.
*
* @param path path to node (dot notation)
* @return map of nodes
*/
@SuppressWarnings("unchecked")
public Map<String, YamlNode> getNodes(String path) {
Object o = getProperty(path);
if (o == null) {
return null;
} else if (o instanceof Map) {
Map<String, YamlNode> nodes = new HashMap<String, YamlNode>();
for (Map.Entry<String, Object> entry : ((Map<String, Object>) o).entrySet()) {
if (entry.getValue() instanceof Map) {
nodes.put(entry.getKey(), new YamlNode((Map<String, Object>) entry.getValue()));
}
}
return nodes;
} else {
return null;
}
}
/**
* Casts a value to an integer. May return null.
*
* @param o
* @return
*/
private static Integer castInt(Object o) {
if (o == null) {
return null;
} else if (o instanceof Byte) {
return (int) (Byte) o;
} else if (o instanceof Integer) {
return (Integer) o;
} else if (o instanceof Double) {
return (int) (double) (Double) o;
} else if (o instanceof Float) {
return (int) (float) (Float) o;
} else if (o instanceof Long) {
return (int) (long) (Long) o;
} else {
return null;
}
}
private static Long castLong(Object o) {
if (o == null) {
return null;
} else if (o instanceof Byte) {
return (long) (Byte) o;
} else if (o instanceof Integer) {
return (long) (int) (Integer) o;
} else if (o instanceof Double) {
return (long) (double) (Double) o;
} else if (o instanceof Float) {
return (long) (float) (Float) o;
} else if (o instanceof Long) {
return (Long) o;
} else {
return null;
}
}
/**
* Casts a value to a double. May return null.
*
* @param o
* @return
*/
private static Double castDouble(Object o) {
if (o == null) {
return null;
} else if (o instanceof Float) {
return (double) (Float) o;
} else if (o instanceof Double) {
return (Double) o;
} else if (o instanceof Byte) {
return (double) (Byte) o;
} else if (o instanceof Integer) {
return (double) (Integer) o;
} else if (o instanceof Long) {
return (double) (Long) o;
} else {
return null;
}
}
private static Float castFloat(Object o) {
if (o == null) {
return null;
} else if (o instanceof Float) {
return (Float) o;
} else if (o instanceof Double) {
return ((Double) o).floatValue();
} else if (o instanceof Byte) {
return (float) (Byte) o;
} else if (o instanceof Integer) {
return (float) (Integer) o;
} else if (o instanceof Long) {
return (float) (Long) o;
} else {
return null;
}
}
/**
* Casts a value to a boolean. May return null.
*
* @param o
* @return
*/
private static Boolean castBoolean(Object o) {
if (o == null) {
return null;
} else if (o instanceof Boolean) {
return (Boolean) o;
} else {
return null;
}
}
public void removeProperty(String path) {
removeProperty(path, false);
}
/**
* Remove the property at a location. This will override existing
* configuration data to have it conform to key/value mappings.
*
* @param path
*/
@SuppressWarnings("unchecked")
public void removeProperty(String path, boolean allowDot) {
if (allowDot || !path.contains(".")) {
root.remove(path);
return;
}
String[] parts = path.split("\\.");
Map<String, Object> node = root;
for (int i = 0; i < parts.length; i++) {
Object o = node.get(parts[i]);
// Found our target!
if (i == parts.length - 1) {
node.remove(parts[i]);
return;
}
node = (Map<String, Object>) o;
if (node == null)
return;
}
}
@SuppressWarnings("unchecked")
protected void fullDump(Map<String, Object> target, String prefix) {
Iterator<Entry<String, Object>> i = root.entrySet().iterator();
while (i.hasNext()) {
Entry<String, Object> e = i.next();
if (e.getValue() instanceof Map)
new YamlNode((Map<String, Object>) e.getValue())
.fullDump(target, prefix + e.getKey() + ".");
else
target.put(prefix + e.getKey(), e.getValue());
}
}
public Map<String, Object> fullDump() {
Map<String, Object> map = new HashMap<String, Object>();
fullDump(map, "");
return map;
}
@Override
public String toString() {
return Util.toString(root);
}
}

View File

@@ -0,0 +1,880 @@
package org.json;
/*
Copyright (c) 2002 JSON.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The Software shall be used for Good, not Evil.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
/**
* A JSONArray is an ordered sequence of values. Its external text form is a
* string wrapped in square brackets with commas separating the values. The
* internal form is an object having <code>get</code> and <code>opt</code>
* methods for accessing the values by index, and <code>put</code> methods for
* adding or replacing values. The values can be any of these types:
* <code>Boolean</code>, <code>JSONArray</code>, <code>JSONObject</code>,
* <code>Number</code>, <code>String</code>, or the
* <code>JSONObject.NULL object</code>.
* <p>
* The constructor can convert a JSON text into a Java object. The
* <code>toString</code> method converts to JSON text.
* <p>
* A <code>get</code> method returns a value if one can be found, and throws an
* exception if one cannot be found. An <code>opt</code> method returns a
* default value instead of throwing an exception, and so is useful for
* obtaining optional values.
* <p>
* The generic <code>get()</code> and <code>opt()</code> methods return an
* object which you can cast or query for type. There are also typed
* <code>get</code> and <code>opt</code> methods that do type checking and type
* coercion for you.
* <p>
* The texts produced by the <code>toString</code> methods strictly conform to
* JSON syntax rules. The constructors are more forgiving in the texts they will
* accept:
* <ul>
* <li>An extra <code>,</code>&nbsp;<small>(comma)</small> may appear just
* before the closing bracket.</li>
* <li>The <code>null</code> value will be inserted when there is <code>,</code>
* &nbsp;<small>(comma)</small> elision.</li>
* <li>Strings may be quoted with <code>'</code>&nbsp;<small>(single
* quote)</small>.</li>
* <li>Strings do not need to be quoted at all if they do not begin with a quote
* or single quote, and if they do not contain leading or trailing spaces, and
* if they do not contain any of these characters:
* <code>{ } [ ] / \ : , = ; #</code> and if they do not look like numbers and
* if they are not the reserved words <code>true</code>, <code>false</code>, or
* <code>null</code>.</li>
* <li>Values can be separated by <code>;</code> <small>(semicolon)</small> as
* well as by <code>,</code> <small>(comma)</small>.</li>
* </ul>
*
* @author JSON.org
* @version 2012-04-20
*/
public class JSONArray {
/**
* The arrayList where the JSONArray's properties are kept.
*/
private final ArrayList<Object> myArrayList;
/**
* Construct an empty JSONArray.
*/
public JSONArray() {
this.myArrayList = new ArrayList<Object>();
}
public JSONArray(int initialLength) {
this.myArrayList = new ArrayList<Object>(initialLength);
}
/**
* Construct a JSONArray from a JSONTokener.
*
* @param x A JSONTokener
* @throws JSONException If there is a syntax error.
*/
public JSONArray(JSONTokener x) throws JSONException {
this();
if (x.nextClean() != '[') {
throw x.syntaxError("A JSONArray text must start with '['");
}
if (x.nextClean() != ']') {
x.back();
for (;;) {
if (x.nextClean() == ',') {
x.back();
this.myArrayList.add(JSONObject.NULL);
} else {
x.back();
this.myArrayList.add(x.nextValue());
}
switch (x.nextClean()) {
case ';':
case ',':
if (x.nextClean() == ']') {
return;
}
x.back();
break;
case ']':
return;
default:
throw x.syntaxError("Expected a ',' or ']'");
}
}
}
}
/**
* Construct a JSONArray from a source JSON text.
*
* @param source A string that begins with <code>[</code>&nbsp;<small>(left
* bracket)</small> and ends with <code>]</code> &nbsp;<small>(right
* bracket)</small>.
* @throws JSONException If there is a syntax error.
*/
public JSONArray(String source) throws JSONException {
this(new JSONTokener(source));
}
/**
* Construct a JSONArray from a Collection.
*
* @param collection A Collection.
*/
public JSONArray(Collection<Object> collection) {
this.myArrayList = new ArrayList<Object>();
if (collection != null) {
Iterator<Object> iter = collection.iterator();
while (iter.hasNext()) {
this.myArrayList.add(JSONObject.wrap(iter.next()));
}
}
}
/**
* Construct a JSONArray from an array
*
* @throws JSONException If not an array.
*/
public JSONArray(Object array) {
this();
if (array.getClass().isArray()) {
int length = Array.getLength(array);
for (int i = 0; i < length; i += 1) {
this.put(JSONObject.wrap(Array.get(array, i)));
}
} else {
throw new IllegalArgumentException(
"JSONArray initial value should be a string or collection or array."
);
}
}
/**
* Get the object value associated with an index.
*
* @param index The index must be between 0 and length() - 1.
* @return An object value.
* @throws JSONException If there is no value for the index.
*/
public Object get(int index) throws JSONException {
Object object = this.opt(index);
if (object == null) {
throw new JSONException("JSONArray[" + index + "] not found.");
}
return object;
}
/**
* Get the boolean value associated with an index. The string values "true"
* and "false" are converted to boolean.
*
* @param index The index must be between 0 and length() - 1.
* @return The truth.
* @throws JSONException If there is no value for the index or if the value
* is not convertible to boolean.
*/
public boolean getBoolean(int index) throws JSONException {
Object object = this.get(index);
if (
object.equals(Boolean.FALSE)
|| (object instanceof String && ((String) object).equalsIgnoreCase("false"))
) {
return false;
} else if (
object.equals(Boolean.TRUE)
|| (object instanceof String && ((String) object).equalsIgnoreCase("true"))
) {
return true;
}
throw new JSONException("JSONArray[" + index + "] is not a boolean.");
}
/**
* Get the double value associated with an index.
*
* @param index The index must be between 0 and length() - 1.
* @return The value.
* @throws JSONException If the key is not found or if the value cannot be
* converted to a number.
*/
public double getDouble(int index) throws JSONException {
Object object = this.get(index);
try {
return object instanceof Number
? ((Number) object).doubleValue()
: Double.parseDouble((String) object);
} catch (Exception e) {
throw new JSONException("JSONArray[" + index + "] is not a number.");
}
}
/**
* Get the int value associated with an index.
*
* @param index The index must be between 0 and length() - 1.
* @return The value.
* @throws JSONException If the key is not found or if the value is not a
* number.
*/
public int getInt(int index) throws JSONException {
Object object = this.get(index);
try {
return object instanceof Number
? ((Number) object).intValue()
: Integer.parseInt((String) object);
} catch (Exception e) {
throw new JSONException("JSONArray[" + index + "] is not a number.");
}
}
/**
* Get the JSONArray associated with an index.
*
* @param index The index must be between 0 and length() - 1.
* @return A JSONArray value.
* @throws JSONException If there is no value for the index. or if the value
* is not a JSONArray
*/
public JSONArray getJSONArray(int index) throws JSONException {
Object object = this.get(index);
if (object instanceof JSONArray) {
return (JSONArray) object;
}
throw new JSONException("JSONArray[" + index + "] is not a JSONArray.");
}
/**
* Get the JSONObject associated with an index.
*
* @param index subscript
* @return A JSONObject value.
* @throws JSONException If there is no value for the index or if the value
* is not a JSONObject
*/
public JSONObject getJSONObject(int index) throws JSONException {
Object object = this.get(index);
if (object instanceof JSONObject) {
return (JSONObject) object;
}
throw new JSONException("JSONArray[" + index + "] is not a JSONObject.");
}
/**
* Get the long value associated with an index.
*
* @param index The index must be between 0 and length() - 1.
* @return The value.
* @throws JSONException If the key is not found or if the value cannot be
* converted to a number.
*/
public long getLong(int index) throws JSONException {
Object object = this.get(index);
try {
return object instanceof Number
? ((Number) object).longValue()
: Long.parseLong((String) object);
} catch (Exception e) {
throw new JSONException("JSONArray[" + index + "] is not a number.");
}
}
/**
* Get the string associated with an index.
*
* @param index The index must be between 0 and length() - 1.
* @return A string value.
* @throws JSONException If there is no string value for the index.
*/
public String getString(int index) throws JSONException {
Object object = this.get(index);
if (object instanceof String) {
return (String) object;
}
throw new JSONException("JSONArray[" + index + "] not a string.");
}
/**
* Determine if the value is null.
*
* @param index The index must be between 0 and length() - 1.
* @return true if the value at the index is null, or if there is no value.
*/
public boolean isNull(int index) {
return JSONObject.NULL.equals(this.opt(index));
}
/**
* Make a string from the contents of this JSONArray. The
* <code>separator</code> string is inserted between each element. Warning:
* This method assumes that the data structure is acyclical.
*
* @param separator A string that will be inserted between the elements.
* @return a string.
* @throws JSONException If the array contains an invalid number.
*/
public String join(String separator) throws JSONException {
int len = this.length();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < len; i += 1) {
if (i > 0) {
sb.append(separator);
}
sb.append(JSONObject.valueToString(this.myArrayList.get(i)));
}
return sb.toString();
}
/**
* Get the number of elements in the JSONArray, included nulls.
*
* @return The length (or size).
*/
public int length() {
return this.myArrayList.size();
}
/**
* Get the optional object value associated with an index.
*
* @param index The index must be between 0 and length() - 1.
* @return An object value, or null if there is no object at that index.
*/
public Object opt(int index) {
return (index < 0 || index >= this.length()) ? null : this.myArrayList.get(index);
}
/**
* Get the optional boolean value associated with an index. It returns false
* if there is no value at that index, or if the value is not Boolean.TRUE
* or the String "true".
*
* @param index The index must be between 0 and length() - 1.
* @return The truth.
*/
public boolean optBoolean(int index) {
return this.optBoolean(index, false);
}
/**
* Get the optional boolean value associated with an index. It returns the
* defaultValue if there is no value at that index or if it is not a Boolean
* or the String "true" or "false" (case insensitive).
*
* @param index The index must be between 0 and length() - 1.
* @param defaultValue A boolean default.
* @return The truth.
*/
public boolean optBoolean(int index, boolean defaultValue) {
try {
return this.getBoolean(index);
} catch (Exception e) {
return defaultValue;
}
}
/**
* Get the optional double value associated with an index. NaN is returned
* if there is no value for the index, or if the value is not a number and
* cannot be converted to a number.
*
* @param index The index must be between 0 and length() - 1.
* @return The value.
*/
public double optDouble(int index) {
return this.optDouble(index, Double.NaN);
}
/**
* Get the optional double value associated with an index. The defaultValue
* is returned if there is no value for the index, or if the value is not a
* number and cannot be converted to a number.
*
* @param index subscript
* @param defaultValue The default value.
* @return The value.
*/
public double optDouble(int index, double defaultValue) {
try {
return this.getDouble(index);
} catch (Exception e) {
return defaultValue;
}
}
/**
* Get the optional int value associated with an index. Zero is returned if
* there is no value for the index, or if the value is not a number and
* cannot be converted to a number.
*
* @param index The index must be between 0 and length() - 1.
* @return The value.
*/
public int optInt(int index) {
return this.optInt(index, 0);
}
/**
* Get the optional int value associated with an index. The defaultValue is
* returned if there is no value for the index, or if the value is not a
* number and cannot be converted to a number.
*
* @param index The index must be between 0 and length() - 1.
* @param defaultValue The default value.
* @return The value.
*/
public int optInt(int index, int defaultValue) {
try {
return this.getInt(index);
} catch (Exception e) {
return defaultValue;
}
}
/**
* Get the optional JSONArray associated with an index.
*
* @param index subscript
* @return A JSONArray value, or null if the index has no value, or if the
* value is not a JSONArray.
*/
public JSONArray optJSONArray(int index) {
Object o = this.opt(index);
return o instanceof JSONArray ? (JSONArray) o : null;
}
/**
* Get the optional JSONObject associated with an index. Null is returned if
* the key is not found, or null if the index has no value, or if the value
* is not a JSONObject.
*
* @param index The index must be between 0 and length() - 1.
* @return A JSONObject value.
*/
public JSONObject optJSONObject(int index) {
Object o = this.opt(index);
return o instanceof JSONObject ? (JSONObject) o : null;
}
/**
* Get the optional long value associated with an index. Zero is returned if
* there is no value for the index, or if the value is not a number and
* cannot be converted to a number.
*
* @param index The index must be between 0 and length() - 1.
* @return The value.
*/
public long optLong(int index) {
return this.optLong(index, 0);
}
/**
* Get the optional long value associated with an index. The defaultValue is
* returned if there is no value for the index, or if the value is not a
* number and cannot be converted to a number.
*
* @param index The index must be between 0 and length() - 1.
* @param defaultValue The default value.
* @return The value.
*/
public long optLong(int index, long defaultValue) {
try {
return this.getLong(index);
} catch (Exception e) {
return defaultValue;
}
}
/**
* Get the optional string value associated with an index. It returns an
* empty string if there is no value at that index. If the value is not a
* string and is not null, then it is coverted to a string.
*
* @param index The index must be between 0 and length() - 1.
* @return A String value.
*/
public String optString(int index) {
return this.optString(index, "");
}
/**
* Get the optional string associated with an index. The defaultValue is
* returned if the key is not found.
*
* @param index The index must be between 0 and length() - 1.
* @param defaultValue The default value.
* @return A String value.
*/
public String optString(int index, String defaultValue) {
Object object = this.opt(index);
return JSONObject.NULL.equals(object) ? defaultValue : object.toString();
}
/**
* Append a boolean value. This increases the array's length by one.
*
* @param value A boolean value.
* @return this.
*/
public JSONArray put(boolean value) {
this.put(value ? Boolean.TRUE : Boolean.FALSE);
return this;
}
/**
* Put a value in the JSONArray, where the value will be a JSONArray which
* is produced from a Collection.
*
* @param value A Collection value.
* @return this.
*/
public JSONArray put(Collection<Object> value) {
this.put(new JSONArray(value));
return this;
}
/**
* Append a double value. This increases the array's length by one.
*
* @param value A double value.
* @throws JSONException if the value is not finite.
* @return this.
*/
public JSONArray put(double value) throws JSONException {
Double d = new Double(value);
JSONObject.testValidity(d);
this.put(d);
return this;
}
/**
* Append an int value. This increases the array's length by one.
*
* @param value An int value.
* @return this.
*/
public JSONArray put(int value) {
this.put(new Integer(value));
return this;
}
/**
* Append an long value. This increases the array's length by one.
*
* @param value A long value.
* @return this.
*/
public JSONArray put(long value) {
this.put(new Long(value));
return this;
}
/**
* Put a value in the JSONArray, where the value will be a JSONObject which
* is produced from a Map.
*
* @param value A Map value.
* @return this.
*/
public JSONArray put(Map<Object, Object> value) {
this.put(new JSONObject(value));
return this;
}
/**
* Append an object value. This increases the array's length by one.
*
* @param value An object value. The value should be a Boolean, Double,
* Integer, JSONArray, JSONObject, Long, or String, or the JSONObject.NULL
* object.
* @return this.
*/
public JSONArray put(Object value) {
this.myArrayList.add(value);
return this;
}
/**
* Put or replace a boolean value in the JSONArray. If the index is greater
* than the length of the JSONArray, then null elements will be added as
* necessary to pad it out.
*
* @param index The subscript.
* @param value A boolean value.
* @return this.
* @throws JSONException If the index is negative.
*/
public JSONArray put(int index, boolean value) throws JSONException {
this.put(index, value ? Boolean.TRUE : Boolean.FALSE);
return this;
}
/**
* Put a value in the JSONArray, where the value will be a JSONArray which
* is produced from a Collection.
*
* @param index The subscript.
* @param value A Collection value.
* @return this.
* @throws JSONException If the index is negative or if the value is not
* finite.
*/
public JSONArray put(int index, Collection<Object> value) throws JSONException {
this.put(index, new JSONArray(value));
return this;
}
/**
* Put or replace a double value. If the index is greater than the length of
* the JSONArray, then null elements will be added as necessary to pad it
* out.
*
* @param index The subscript.
* @param value A double value.
* @return this.
* @throws JSONException If the index is negative or if the value is not
* finite.
*/
public JSONArray put(int index, double value) throws JSONException {
this.put(index, new Double(value));
return this;
}
/**
* Put or replace an int value. If the index is greater than the length of
* the JSONArray, then null elements will be added as necessary to pad it
* out.
*
* @param index The subscript.
* @param value An int value.
* @return this.
* @throws JSONException If the index is negative.
*/
public JSONArray put(int index, int value) throws JSONException {
this.put(index, new Integer(value));
return this;
}
/**
* Put or replace a long value. If the index is greater than the length of
* the JSONArray, then null elements will be added as necessary to pad it
* out.
*
* @param index The subscript.
* @param value A long value.
* @return this.
* @throws JSONException If the index is negative.
*/
public JSONArray put(int index, long value) throws JSONException {
this.put(index, new Long(value));
return this;
}
/**
* Put a value in the JSONArray, where the value will be a JSONObject that
* is produced from a Map.
*
* @param index The subscript.
* @param value The Map value.
* @return this.
* @throws JSONException If the index is negative or if the the value is an
* invalid number.
*/
public JSONArray put(int index, Map<Object, Object> value) throws JSONException {
this.put(index, new JSONObject(value));
return this;
}
/**
* Put or replace an object value in the JSONArray. If the index is greater
* than the length of the JSONArray, then null elements will be added as
* necessary to pad it out.
*
* @param index The subscript.
* @param value The value to put into the array. The value should be a
* Boolean, Double, Integer, JSONArray, JSONObject, Long, or String, or the
* JSONObject.NULL object.
* @return this.
* @throws JSONException If the index is negative or if the the value is an
* invalid number.
*/
public JSONArray put(int index, Object value) throws JSONException {
JSONObject.testValidity(value);
if (index < 0) {
throw new JSONException("JSONArray[" + index + "] not found.");
}
if (index < this.length()) {
this.myArrayList.set(index, value);
} else {
while (index != this.length()) {
this.put(JSONObject.NULL);
}
this.put(value);
}
return this;
}
/**
* Remove an index and close the hole.
*
* @param index The index of the element to be removed.
* @return The value that was associated with the index, or null if there
* was no value.
*/
public Object remove(int index) {
Object o = this.opt(index);
this.myArrayList.remove(index);
return o;
}
/**
* Produce a JSONObject by combining a JSONArray of names with the values of
* this JSONArray.
*
* @param names A JSONArray containing a list of key strings. These will be
* paired with the values.
* @return A JSONObject, or null if there are no names or if this JSONArray
* has no values.
* @throws JSONException If any of the names are null.
*/
public JSONObject toJSONObject(JSONArray names) throws JSONException {
if (names == null || names.length() == 0 || this.length() == 0) {
return null;
}
JSONObject jo = new JSONObject();
for (int i = 0; i < names.length(); i += 1) {
jo.put(names.getString(i), this.opt(i));
}
return jo;
}
/**
* Make a JSON text of this JSONArray. For compactness, no unnecessary
* whitespace is added. If it is not possible to produce a syntactically
* correct JSON text then null will be returned instead. This could occur if
* the array contains an invalid number.
* <p>
* Warning: This method assumes that the data structure is acyclical.
*
* @return a printable, displayable, transmittable representation of the
* array.
*/
@Override
public String toString() {
try {
return '[' + this.join(",") + ']';
} catch (Exception e) {
return null;
}
}
/**
* Make a prettyprinted JSON text of this JSONArray. Warning: This method
* assumes that the data structure is acyclical.
*
* @param indentFactor The number of spaces to add to each level of
* indentation.
* @return a printable, displayable, transmittable representation of the
* object, beginning with <code>[</code>&nbsp;<small>(left bracket)</small>
* and ending with <code>]</code> &nbsp;<small>(right bracket)</small>.
* @throws JSONException
*/
public String toString(int indentFactor) throws JSONException {
StringWriter sw = new StringWriter();
synchronized (sw.getBuffer()) {
return this.write(sw, indentFactor, 0).toString();
}
}
/**
* Write the contents of the JSONArray as JSON text to a writer. For
* compactness, no whitespace is added.
* <p>
* Warning: This method assumes that the data structure is acyclical.
*
* @return The writer.
* @throws JSONException
*/
public Writer write(Writer writer) throws JSONException {
return this.write(writer, 0, 0);
}
/**
* Write the contents of the JSONArray as JSON text to a writer. For
* compactness, no whitespace is added.
* <p>
* Warning: This method assumes that the data structure is acyclical.
*
* @param indentFactor The number of spaces to add to each level of
* indentation.
* @param indent The indention of the top level.
* @return The writer.
* @throws JSONException
*/
Writer write(Writer writer, int indentFactor, int indent) throws JSONException {
try {
boolean commanate = false;
int length = this.length();
writer.write('[');
if (length == 1) {
JSONObject.writeValue(writer, this.myArrayList.get(0), indentFactor, indent);
} else if (length != 0) {
final int newindent = indent + indentFactor;
for (int i = 0; i < length; i += 1) {
if (commanate) {
writer.write(',');
}
if (indentFactor > 0) {
writer.write('\n');
}
JSONObject.indent(writer, newindent);
JSONObject.writeValue(writer, this.myArrayList.get(i), indentFactor, newindent);
commanate = true;
}
if (indentFactor > 0) {
writer.write('\n');
}
JSONObject.indent(writer, indent);
}
writer.write(']');
return writer;
} catch (IOException e) {
throw new JSONException(e);
}
}
}

View File

@@ -0,0 +1,26 @@
package org.json;
/**
* The JSONException is thrown by the JSON.org classes when things are amiss.
*
* @author JSON.org
* @version 2010-12-24
*/
public class JSONException extends RuntimeException {
private static final long serialVersionUID = 0;
/**
* Constructs a JSONException with an explanatory message.
*
* @param message Detail about the reason for the exception.
*/
public JSONException(String message) {
super(message);
}
public JSONException(Throwable cause) {
super(cause.getMessage());
initCause(cause);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,19 @@
package org.json;
/**
* The <code>JSONString</code> interface allows a <code>toJSONString()</code>
* method so that a class can change the behavior of
* <code>JSONObject.toString()</code>, <code>JSONArray.toString()</code>, and
* <code>JSONWriter.value(</code>Object<code>)</code>. The
* <code>toJSONString</code> method will be used instead of the default behavior
* of using the Object's <code>toString()</code> method and quoting the result.
*/
public interface JSONString {
/**
* The <code>toJSONString</code> method allows a class to produce its own
* JSON serialization.
*
* @return A strictly syntactically correct JSON text.
*/
public String toJSONString();
}

View File

@@ -0,0 +1,434 @@
package org.json;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
/*
Copyright (c) 2002 JSON.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
The Software shall be used for Good, not Evil.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/**
* A JSONTokener takes a source string and extracts characters and tokens from
* it. It is used by the JSONObject and JSONArray constructors to parse JSON
* source strings.
*
* @author JSON.org
* @version 2012-02-16
*/
public class JSONTokener {
private long character;
private boolean eof;
private long index;
private long line;
private char previous;
private Reader reader;
private boolean usePrevious;
/**
* Construct a JSONTokener from a Reader.
*
* @param reader A reader.
*/
public JSONTokener(Reader reader) {
this.reader = reader.markSupported() ? reader : new BufferedReader(reader);
this.eof = false;
this.usePrevious = false;
this.previous = 0;
this.index = 0;
this.character = 1;
this.line = 1;
}
/**
* Construct a JSONTokener from an InputStream.
*/
public JSONTokener(InputStream inputStream) throws JSONException {
this(new InputStreamReader(inputStream));
}
/**
* Construct a JSONTokener from a string.
*
* @param s A source string.
*/
public JSONTokener(String s) {
this(new StringReader(s));
}
/**
* Back up one character. This provides a sort of lookahead capability, so
* that you can test for a digit or letter before attempting to parse the
* next number or identifier.
*/
public void back() throws JSONException {
if (this.usePrevious || this.index <= 0) {
throw new JSONException("Stepping back two steps is not supported");
}
this.index -= 1;
this.character -= 1;
this.usePrevious = true;
this.eof = false;
}
/**
* Get the hex value of a character (base16).
*
* @param c A character between '0' and '9' or between 'A' and 'F' or
* between 'a' and 'f'.
* @return An int between 0 and 15, or -1 if c was not a hex digit.
*/
public static int dehexchar(char c) {
if (c >= '0' && c <= '9') {
return c - '0';
}
if (c >= 'A' && c <= 'F') {
return c - ('A' - 10);
}
if (c >= 'a' && c <= 'f') {
return c - ('a' - 10);
}
return -1;
}
public boolean end() {
return this.eof && !this.usePrevious;
}
/**
* Determine if the source string still contains characters that next() can
* consume.
*
* @return true if not yet at the end of the source.
*/
public boolean more() throws JSONException {
this.next();
if (this.end()) {
return false;
}
this.back();
return true;
}
/**
* Get the next character in the source string.
*
* @return The next character, or 0 if past the end of the source string.
*/
public char next() throws JSONException {
int c;
if (this.usePrevious) {
this.usePrevious = false;
c = this.previous;
} else {
try {
c = this.reader.read();
} catch (IOException exception) {
throw new JSONException(exception);
}
if (c <= 0) { // End of stream
this.eof = true;
c = 0;
}
}
this.index += 1;
if (this.previous == '\r') {
this.line += 1;
this.character = c == '\n' ? 0 : 1;
} else if (c == '\n') {
this.line += 1;
this.character = 0;
} else {
this.character += 1;
}
this.previous = (char) c;
return this.previous;
}
/**
* Consume the next character, and check that it matches a specified
* character.
*
* @param c The character to match.
* @return The character.
* @throws JSONException if the character does not match.
*/
public char next(char c) throws JSONException {
char n = this.next();
if (n != c) {
throw this.syntaxError("Expected '" + c + "' and instead saw '" + n + "'");
}
return n;
}
/**
* Get the next n characters.
*
* @param n The number of characters to take.
* @return A string of n characters.
* @throws JSONException Substring bounds error if there are not n
* characters remaining in the source string.
*/
public String next(int n) throws JSONException {
if (n == 0) {
return "";
}
char[] chars = new char[n];
int pos = 0;
while (pos < n) {
chars[pos] = this.next();
if (this.end()) {
throw this.syntaxError("Substring bounds error");
}
pos += 1;
}
return new String(chars);
}
/**
* Get the next char in the string, skipping whitespace.
*
* @throws JSONException
* @return A character, or 0 if there are no more characters.
*/
public char nextClean() throws JSONException {
for (;;) {
char c = this.next();
if (c == 0 || c > ' ') {
return c;
}
}
}
/**
* Return the characters up to the next close quote character. Backslash
* processing is done. The formal JSON format does not allow strings in
* single quotes, but an implementation is allowed to accept them.
*
* @param quote The quoting character, either <code>"</code>
* &nbsp;<small>(double quote)</small> or <code>'</code>
* &nbsp;<small>(single quote)</small>.
* @return A String.
* @throws JSONException Unterminated string.
*/
public String nextString(char quote) throws JSONException {
char c;
StringBuffer sb = new StringBuffer();
for (;;) {
c = this.next();
switch (c) {
case 0:
case '\n':
case '\r':
throw this.syntaxError("Unterminated string");
case '\\':
c = this.next();
switch (c) {
case 'b':
sb.append('\b');
break;
case 't':
sb.append('\t');
break;
case 'n':
sb.append('\n');
break;
case 'f':
sb.append('\f');
break;
case 'r':
sb.append('\r');
break;
case 'u':
sb.append((char) Integer.parseInt(this.next(4), 16));
break;
case '"':
case '\'':
case '\\':
case '/':
sb.append(c);
break;
default:
throw this.syntaxError("Illegal escape.");
}
break;
default:
if (c == quote) {
return sb.toString();
}
sb.append(c);
}
}
}
/**
* Get the text up but not including the specified character or the end of
* line, whichever comes first.
*
* @param delimiter A delimiter character.
* @return A string.
*/
public String nextTo(char delimiter) throws JSONException {
StringBuffer sb = new StringBuffer();
for (;;) {
char c = this.next();
if (c == delimiter || c == 0 || c == '\n' || c == '\r') {
if (c != 0) {
this.back();
}
return sb.toString().trim();
}
sb.append(c);
}
}
/**
* Get the text up but not including one of the specified delimiter
* characters or the end of line, whichever comes first.
*
* @param delimiters A set of delimiter characters.
* @return A string, trimmed.
*/
public String nextTo(String delimiters) throws JSONException {
char c;
StringBuffer sb = new StringBuffer();
for (;;) {
c = this.next();
if (delimiters.indexOf(c) >= 0 || c == 0 || c == '\n' || c == '\r') {
if (c != 0) {
this.back();
}
return sb.toString().trim();
}
sb.append(c);
}
}
/**
* Get the next value. The value can be a Boolean, Double, Integer,
* JSONArray, JSONObject, Long, or String, or the JSONObject.NULL object.
*
* @throws JSONException If syntax error.
*
* @return An object.
*/
public Object nextValue() throws JSONException {
char c = this.nextClean();
String string;
switch (c) {
case '"':
case '\'':
return this.nextString(c);
case '{':
this.back();
return new JSONObject(this);
case '[':
this.back();
return new JSONArray(this);
}
/*
* Handle unquoted text. This could be the values true, false, or null,
* or it can be a number. An implementation (such as this one) is
* allowed to also accept non-standard forms.
*
* Accumulate characters until we reach the end of the text or a
* formatting character.
*/
StringBuffer sb = new StringBuffer();
while (c >= ' ' && ",:]}/\\\"[{;=#".indexOf(c) < 0) {
sb.append(c);
c = this.next();
}
this.back();
string = sb.toString().trim();
if ("".equals(string)) {
throw this.syntaxError("Missing value");
}
return JSONObject.stringToValue(string);
}
/**
* Skip characters until the next character is the requested character. If
* the requested character is not found, no characters are skipped.
*
* @param to A character to skip to.
* @return The requested character, or zero if the requested character is
* not found.
*/
public char skipTo(char to) throws JSONException {
char c;
try {
long startIndex = this.index;
long startCharacter = this.character;
long startLine = this.line;
this.reader.mark(1000000);
do {
c = this.next();
if (c == 0) {
this.reader.reset();
this.index = startIndex;
this.character = startCharacter;
this.line = startLine;
return c;
}
} while (c != to);
} catch (IOException exc) {
throw new JSONException(exc);
}
this.back();
return c;
}
/**
* Make a JSONException to signal a syntax error.
*
* @param message The error message.
* @return A JSONException object, suitable for throwing
*/
public JSONException syntaxError(String message) {
return new JSONException(message + this.toString());
}
/**
* Make a printable string of this JSONTokener.
*
* @return " at {index} [character {character} line {line}]"
*/
@Override
public String toString() {
return " at " + this.index + " [character " + this.character + " line " + this.line + "]";
}
}

View File

@@ -0,0 +1,57 @@
package org.json;
import java.util.Map.Entry;
public class JSONUtil {
public static JSONObject toJSON(JSONEntry... entries) {
return o(entries);
}
public static JSONObject o(JSONEntry... entries) {
JSONObject object = new JSONObject();
for (int i = 0; i < entries.length; ++i) {
JSONEntry e = entries[i];
object.put(e.getKey(), e.getValue());
}
return object;
}
public static JSONArray a(Object... values) {
return new JSONArray(values);
}
public static JSONEntry e(String k, Object v) {
return new JSONEntry(k, v);
}
public static class JSONEntry implements Entry<String, Object> {
private String key;
private Object value;
public JSONEntry(String key, Object value) {
this.key = key;
this.value = value;
}
@Override
public String getKey() {
return this.key;
}
@Override
public Object getValue() {
return this.value;
}
@Override
public Object setValue(Object value) {
Object oldValue = this.value;
this.value = value;
return oldValue;
}
}
}

1
protobuf_update.bat Normal file
View File

@@ -0,0 +1 @@
protoc --proto_path=../SlimeVR-OpenVR-Driver/src/bridge --java_out=./src/main/java ProtobufMessages.proto

21
resources/LICENSE.txt Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021 Eiren Rain, SlimeVR
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

File diff suppressed because it is too large Load Diff

17
resources/firewall.bat Normal file
View File

@@ -0,0 +1,17 @@
@echo off
echo Installing firewall rules...
rem Discovery default port
netsh advfirewall firewall add rule name="SlimeVR UDP 35903 incoming" dir=in action=allow protocol=UDP localport=35903
netsh advfirewall firewall add rule name="SlimeVR UDP 35903 outgoing" dir=out action=allow protocol=UDP localport=35903
rem Rotational data default port
netsh advfirewall firewall add rule name="SlimeVR UDP 6969 incoming" dir=in action=allow protocol=UDP localport=6969
netsh advfirewall firewall add rule name="SlimeVR UDP 6969 outgoing" dir=out action=allow protocol=UDP localport=6969
rem WebSocket server default port
netsh advfirewall firewall add rule name="SlimeVR TCP 21110 incoming" dir=in action=allow protocol=TCP localport=21110
netsh advfirewall firewall add rule name="SlimeVR TCP 21110 outgoing" dir=out action=allow protocol=TCP localport=21110
echo Done!
pause

View File

@@ -0,0 +1,17 @@
@echo off
echo Uninstalling firewall rules...
rem Discovery defauly port
netsh advfirewall firewall delete rule name="SlimeVR UDP 35903 incoming"
netsh advfirewall firewall delete rule name="SlimeVR UDP 35903 outgoing"
rem Rotational data default port
netsh advfirewall firewall delete rule name="SlimeVR UDP 6969 incoming"
netsh advfirewall firewall delete rule name="SlimeVR UDP 6969 outgoing"
rem WebSocket server default port
netsh advfirewall firewall delete rule name="SlimeVR TCP 21110 incoming"
netsh advfirewall firewall delete rule name="SlimeVR TCP 21110 outgoing"
echo Done!
pause

19
resources/run.bat Normal file
View File

@@ -0,0 +1,19 @@
@echo off
setlocal enableextensions
cd /d "%~dp0"
where java >nul 2>&1
if %errorlevel% EQU 0 (
java -Xmx512M -jar slimevr.jar
) else (
echo Java was not found in your system.
echo.
echo Either use SlimeVR Installer to install the server by following this link:
echo https://github.com/SlimeVR/SlimeVR-Installer/releases/latest/download/slimevr_web_installer.exe
echo.
echo Or download Java 11 by following this link:
echo https://adoptium.net/releases.html?variant=openjdk11^&jvmVariant=hotspot
)
if %errorlevel% NEQ 0 (
pause
)

View File

@@ -8,4 +8,8 @@
*/
rootProject.name = 'SlimeVR Server'
include('Slime Java Commons')
include ':slime-java-commons'
include ':solarxr-protocol'
project(':solarxr-protocol').projectDir = new File('solarxr-protocol/protocol/java')

1
solarxr-protocol Submodule

Submodule solarxr-protocol added at 86eb9aa6b5

400
spotless.xml Normal file
View File

@@ -0,0 +1,400 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<profiles version="22">
<profile kind="CodeFormatterProfile" name="Spotless" version="22">
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment" value="separate_lines_if_wrapped"/>
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_javadoc_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.indentation.size" value="8"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration" value="separate_lines_if_wrapped"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.align_with_spaces" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_before_code_block" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_switch_case_expressions" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_package" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant" value="48"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_root_tags" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.enabling_tag" value="@formatter:on"/>
<setting id="org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_record_components" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_logical_operator" value="48"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_annotation_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_record_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_abstract_method" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line" value="one_line_if_empty"/>
<setting id="org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause" value="separate_lines_if_wrapped"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="48"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator" value="48"/>
<setting id="org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line" value="one_line_if_empty"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_switch_case_expressions" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_shift_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_code_block" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="48"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_parameters" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_loops" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_switch_case_arrow_operator" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation" value="separate_lines_if_wrapped"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_enum_constant" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.text_block_indentation" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.align_type_members_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assignment" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_module_statements" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line" value="one_line_if_empty"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_permitted_types" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_header" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_annotations" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="48"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_assertion_message_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_method_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines" value="2147483647"/>
<setting id="org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator" value="48"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_resources_in_try" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation" value="48"/>
<setting id="org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_source_code" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_field" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_method" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_not_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_html" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration" value="separate_lines_if_wrapped"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_compact_if" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.indent_empty_lines" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_type_arguments" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_package" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_label" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_record_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_tag_description" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_record_constructor" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_string_concatenation" value="48"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_multiple_fields" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_shift_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_shift_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_record_components" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_additive_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.join_lines_in_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_record_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_relational_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_import_groups" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_logical_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation" value="separate_lines_if_wrapped"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_imports" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_record_declaration" value="separate_lines_if_wrapped"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement" value="separate_lines_if_wrapped"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_default" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.disabling_tag" value="@formatter:off"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_enum_constants" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_imports" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_method_body" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement" value="separate_lines_if_wrapped"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.keep_switch_body_block_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_block" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_switch_case_with_arrow" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.comment.line_length" value="80"/>
<setting id="org.eclipse.jdt.core.formatter.use_on_off_tags" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_method_body_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line" value="one_line_if_empty"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_additive_operator" value="48"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_constructor" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_relational_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_record_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_lambda_body" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.compact_else_if" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation" value="48"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_parameter" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_relational_operator" value="48"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve" value="2"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_additive_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_line_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.align_selector_in_method_invocation_on_expression_first_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_record_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_switch_case_with_arrow_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_expressions_in_switch_case_with_colon" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_after_code_block" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_conditional_expression" value="48"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_type" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_local_variable" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_default" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.insert_new_line_between_different_tags" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_additive_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.join_wrapped_lines" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_field" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_conditional_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_shift_operator" value="48"/>
<setting id="org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause" value="separate_lines_if_wrapped"/>
<setting id="org.eclipse.jdt.core.formatter.keep_code_block_on_one_line" value="one_line_if_empty"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_record_components" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.tabulation.size" value="4"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_record_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_assignment_operator" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_switch_case_with_arrow" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line" value="one_line_if_empty"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_annotations_on_method" value="49"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_record_constructor_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_assertion_message" value="48"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_member_type" value="1"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_logical_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="48"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_record_declaration" value="16"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_relational_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.comment.format_block_comments" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration" value="48"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_after_last_class_body_declaration" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.indent_statements_compare_to_body" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_logical_operator" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_between_statement_group_in_switch" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration" value="separate_lines_if_wrapped"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_comma_in_permitted_types" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line" value="one_line_never"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.blank_lines_before_package" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
<setting id="org.eclipse.jdt.core.javaFormatter" value="org.eclipse.jdt.core.defaultJavaFormatter"/>
<setting id="org.eclipse.jdt.core.formatter.comment.indent_parameter_description" value="false"/>
<setting id="org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_code_block" value="0"/>
<setting id="org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.tabulation.char" value="tab"/>
<setting id="org.eclipse.jdt.core.formatter.wrap_before_string_concatenation" value="true"/>
<setting id="org.eclipse.jdt.core.formatter.lineSplit" value="100"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
</profile>
</profiles>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,147 @@
/*
* Copyright (c) 2009-2012 jMonkeyEngine
* 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 'jMonkeyEngine' 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.
*/
package com.jme3.math;
/**
* Temporary variables assigned to each thread. Engine classes may access these
* temp variables with TempVars.get(), all retrieved TempVars instances must be
* returned via TempVars.release(). This returns an available instance of the
* TempVar class ensuring this particular instance is never used elsewhere in
* the mean time.
*/
public class TempVars {
/**
* Allow X instances of TempVars in a single thread.
*/
private static final int STACK_SIZE = 5;
/**
* <code>TempVarsStack</code> contains a stack of TempVars. Every time
* TempVars.get() is called, a new entry is added to the stack, and the
* index incremented. When TempVars.release() is called, the entry is
* checked against the current instance and then the index is decremented.
*/
private static class TempVarsStack {
int index = 0;
TempVars[] tempVars = new TempVars[STACK_SIZE];
}
/**
* ThreadLocal to store a TempVarsStack for each thread. This ensures each
* thread has a single TempVarsStack that is used only in method calls in
* that thread.
*/
private static final ThreadLocal<TempVarsStack> varsLocal = new ThreadLocal<TempVarsStack>() {
@Override
public TempVarsStack initialValue() {
return new TempVarsStack();
}
};
/**
* This instance of TempVars has been retrieved but not released yet.
*/
private boolean isUsed = false;
private TempVars() {
}
/**
* Acquire an instance of the TempVar class. You have to release the
* instance after use by calling the release() method. If more than
* STACK_SIZE (currently 5) instances are requested in a single thread then
* an ArrayIndexOutOfBoundsException will be thrown.
*
* @return A TempVar instance
*/
public static TempVars get() {
TempVarsStack stack = varsLocal.get();
TempVars instance = stack.tempVars[stack.index];
if (instance == null) {
// Create new
instance = new TempVars();
// Put it in there
stack.tempVars[stack.index] = instance;
}
stack.index++;
instance.isUsed = true;
return instance;
}
/**
* Releases this instance of TempVars. Once released, the contents of the
* TempVars are undefined. The TempVars must be released in the opposite
* order that they are retrieved, e.g. Acquiring vars1, then acquiring
* vars2, vars2 MUST be released first otherwise an exception will be
* thrown.
*/
public void release() {
if (!isUsed) {
throw new IllegalStateException("This instance of TempVars was already released!");
}
isUsed = false;
TempVarsStack stack = varsLocal.get();
// Return it to the stack
stack.index--;
// Check if it is actually there
if (stack.tempVars[stack.index] != this) {
throw new IllegalStateException(
"An instance of TempVars has not been released in a called method!"
);
}
}
/**
* General vectors.
*/
public final Vector3f vect1 = new Vector3f();
public final Vector3f vect2 = new Vector3f();
public final Vector3f vect3 = new Vector3f();
/**
* General matrices.
*/
public final Matrix4f tempMat4 = new Matrix4f();
public final float[] matrixWrite = new float[16];
}

View File

@@ -0,0 +1,399 @@
/*
* Copyright (c) 2009-2012 jMonkeyEngine
* 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 'jMonkeyEngine' 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.
*/
package com.jme3.math;
/**
* Started Date: Jul 16, 2004<br>
* <br>
* Represents a translation, rotation and scale in one object.
*
* @author Jack Lindamood
* @author Joshua Slack
*/
public final class Transform implements Cloneable, java.io.Serializable {
static final long serialVersionUID = 1;
public static final Transform IDENTITY = new Transform();
private Quaternion rot = new Quaternion();
private Vector3f translation = new Vector3f();
private Vector3f scale = new Vector3f(1, 1, 1);
public Transform(Vector3f translation, Quaternion rot) {
this.translation.set(translation);
this.rot.set(rot);
}
public Transform(Vector3f translation, Quaternion rot, Vector3f scale) {
this(translation, rot);
this.scale.set(scale);
}
public Transform(Vector3f translation) {
this(translation, Quaternion.IDENTITY);
}
public Transform(Quaternion rot) {
this(Vector3f.ZERO, rot);
}
public Transform() {
this(Vector3f.ZERO, Quaternion.IDENTITY);
}
/**
* Sets this rotation to the given Quaternion value.
*
* @param rot The new rotation for this matrix.
* @return this
*/
public Transform setRotation(Quaternion rot) {
this.rot.set(rot);
return this;
}
/**
* Sets this translation to the given value.
*
* @param trans The new translation for this matrix.
* @return this
*/
public Transform setTranslation(Vector3f trans) {
this.translation.set(trans);
return this;
}
/**
* Return the translation vector in this matrix.
*
* @return translation vector.
*/
public Vector3f getTranslation() {
return translation;
}
/**
* Sets this scale to the given value.
*
* @param scale The new scale for this matrix.
* @return this
*/
public Transform setScale(Vector3f scale) {
this.scale.set(scale);
return this;
}
/**
* Sets this scale to the given value.
*
* @param scale The new scale for this matrix.
* @return this
*/
public Transform setScale(float scale) {
this.scale.set(scale, scale, scale);
return this;
}
/**
* Return the scale vector in this matrix.
*
* @return scale vector.
*/
public Vector3f getScale() {
return scale;
}
/**
* Stores this translation value into the given vector3f. If trans is null,
* a new vector3f is created to hold the value. The value, once stored, is
* returned.
*
* @param trans The store location for this matrix's translation.
* @return The value of this matrix's translation.
*/
public Vector3f getTranslation(Vector3f trans) {
if (trans == null)
trans = new Vector3f();
trans.set(this.translation);
return trans;
}
/**
* Stores this rotation value into the given Quaternion. If quat is null, a
* new Quaternion is created to hold the value. The value, once stored, is
* returned.
*
* @param quat The store location for this matrix's rotation.
* @return The value of this matrix's rotation.
*/
public Quaternion getRotation(Quaternion quat) {
if (quat == null)
quat = new Quaternion();
quat.set(rot);
return quat;
}
/**
* Return the rotation quaternion in this matrix.
*
* @return rotation quaternion.
*/
public Quaternion getRotation() {
return rot;
}
/**
* Stores this scale value into the given vector3f. If scale is null, a new
* vector3f is created to hold the value. The value, once stored, is
* returned.
*
* @param scale The store location for this matrix's scale.
* @return The value of this matrix's scale.
*/
public Vector3f getScale(Vector3f scale) {
if (scale == null)
scale = new Vector3f();
scale.set(this.scale);
return scale;
}
/**
* Sets this matrix to the interpolation between the first matrix and the
* second by delta amount.
*
* @param t1 The begining transform.
* @param t2 The ending transform.
* @param delta An amount between 0 and 1 representing how far to
* interpolate from t1 to t2.
*/
public void interpolateTransforms(Transform t1, Transform t2, float delta) {
this.rot.slerp(t1.rot, t2.rot, delta);
this.translation.interpolate(t1.translation, t2.translation, delta);
this.scale.interpolate(t1.scale, t2.scale, delta);
}
/**
* Changes the values of this matrix acording to it's parent. Very similar
* to the concept of Node/Spatial transforms.
*
* @param parent The parent matrix.
* @return This matrix, after combining.
*/
public Transform combineWithParent(Transform parent) {
scale.multLocal(parent.scale);
// rot.multLocal(parent.rot);
parent.rot.mult(rot, rot);
// This here, is evil code
// parent
// .rot
// .multLocal(translation)
// .multLocal(parent.scale)
// .addLocal(parent.translation);
translation.multLocal(parent.scale);
parent.rot
.multLocal(translation)
.addLocal(parent.translation);
return this;
}
/**
* Same as {@link #combineWithParent(Transform)}, but assumes that rotation
* is global, so it's not modified.
*
* @param parent
* @return
*/
public Transform combineWithParentGlobalRotation(Transform parent) {
scale.multLocal(parent.scale);
translation.multLocal(parent.scale);
parent.rot
.multLocal(translation)
.addLocal(parent.translation);
return this;
}
/**
* Sets this matrix's translation to the given x,y,z values.
*
* @param x This matrix's new x translation.
* @param y This matrix's new y translation.
* @param z This matrix's new z translation.
* @return this
*/
public Transform setTranslation(float x, float y, float z) {
translation.set(x, y, z);
return this;
}
/**
* Sets this matrix's scale to the given x,y,z values.
*
* @param x This matrix's new x scale.
* @param y This matrix's new y scale.
* @param z This matrix's new z scale.
* @return this
*/
public Transform setScale(float x, float y, float z) {
scale.set(x, y, z);
return this;
}
public Vector3f transformVector(final Vector3f in, Vector3f store) {
if (store == null)
store = new Vector3f();
// multiply with scale first, then rotate, finally translate (cf.
// Eberly)
return rot.mult(store.set(in).multLocal(scale), store).addLocal(translation);
}
public Vector3f transformInverseVector(final Vector3f in, Vector3f store) {
if (store == null)
store = new Vector3f();
// The author of this code should look above and take the inverse of
// that
// But for some reason, they didnt ..
// in.subtract(translation, store).divideLocal(scale);
// rot.inverse().mult(store, store);
in.subtract(translation, store);
rot.inverse().mult(store, store);
store.divideLocal(scale);
return store;
}
/**
* Loads the identity. Equal to translation=0,0,0 scale=1,1,1 rot=0,0,0,1.
*/
public void loadIdentity() {
translation.set(0, 0, 0);
scale.set(1, 1, 1);
rot.set(0, 0, 0, 1);
}
@Override
public String toString() {
return getClass().getSimpleName()
+ "[ "
+ translation.x
+ ", "
+ translation.y
+ ", "
+ translation.z
+ "]\n"
+ "[ "
+ rot.x
+ ", "
+ rot.y
+ ", "
+ rot.z
+ ", "
+ rot.w
+ "]\n"
+ "[ "
+ scale.x
+ " , "
+ scale.y
+ ", "
+ scale.z
+ "]";
}
/**
* Sets this matrix to be equal to the given matrix.
*
* @param matrixQuat The matrix to be equal to.
* @return this
*/
public Transform set(Transform matrixQuat) {
this.translation.set(matrixQuat.translation);
this.rot.set(matrixQuat.rot);
this.scale.set(matrixQuat.scale);
return this;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((rot == null) ? 0 : rot.hashCode());
result = prime * result + ((scale == null) ? 0 : scale.hashCode());
result = prime * result + ((translation == null) ? 0 : translation.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Transform other = (Transform) obj;
if (rot == null) {
if (other.rot != null)
return false;
} else if (!rot.equals(other.rot))
return false;
if (scale == null) {
if (other.scale != null)
return false;
} else if (!scale.equals(other.scale))
return false;
if (translation == null) {
if (other.translation != null)
return false;
} else if (!translation.equals(other.translation))
return false;
return true;
}
@Override
public Transform clone() {
try {
Transform tq = (Transform) super.clone();
tq.rot = rot.clone();
tq.scale = scale.clone();
tq.translation = translation.clone();
return tq;
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}

View File

@@ -0,0 +1,718 @@
/*
* Copyright (c) 2009-2012 jMonkeyEngine
* 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 'jMonkeyEngine' 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.
*/
package com.jme3.math;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.logging.Logger;
/**
* <code>Vector2f</code> defines a Vector for a two float value vector.
*
* @author Mark Powell
* @author Joshua Slack
*/
public final class Vector2f implements Cloneable, java.io.Serializable {
static final long serialVersionUID = 1;
private static final Logger logger = Logger.getLogger(Vector2f.class.getName());
public static final Vector2f ZERO = new Vector2f(0f, 0f);
public static final Vector2f UNIT_XY = new Vector2f(1f, 1f);
/**
* the x value of the vector.
*/
public float x;
/**
* the y value of the vector.
*/
public float y;
/**
* Creates a Vector2f with the given initial x and y values.
*
* @param x The x value of this Vector2f.
* @param y The y value of this Vector2f.
*/
public Vector2f(float x, float y) {
this.x = x;
this.y = y;
}
/**
* Creates a Vector2f with x and y set to 0. Equivalent to Vector2f(0,0).
*/
public Vector2f() {
x = y = 0;
}
/**
* Creates a new Vector2f that contains the passed vector's information
*
* @param vector2f The vector to copy
*/
public Vector2f(Vector2f vector2f) {
this.x = vector2f.x;
this.y = vector2f.y;
}
/**
* set the x and y values of the vector
*
* @param x the x value of the vector.
* @param y the y value of the vector.
* @return this vector
*/
public Vector2f set(float x, float y) {
this.x = x;
this.y = y;
return this;
}
/**
* set the x and y values of the vector from another vector
*
* @param vec the vector to copy from
* @return this vector
*/
public Vector2f set(Vector2f vec) {
this.x = vec.x;
this.y = vec.y;
return this;
}
/**
* <code>add</code> adds a provided vector to this vector creating a
* resultant vector which is returned. If the provided vector is null, null
* is returned.
*
* @param vec the vector to add to this.
* @return the resultant vector.
*/
public Vector2f add(Vector2f vec) {
if (null == vec) {
logger.warning("Provided vector is null, null returned.");
return null;
}
return new Vector2f(x + vec.x, y + vec.y);
}
/**
* <code>addLocal</code> adds a provided vector to this vector internally,
* and returns a handle to this vector for easy chaining of calls. If the
* provided vector is null, null is returned.
*
* @param vec the vector to add to this vector.
* @return this
*/
public Vector2f addLocal(Vector2f vec) {
if (null == vec) {
logger.warning("Provided vector is null, null returned.");
return null;
}
x += vec.x;
y += vec.y;
return this;
}
/**
* <code>addLocal</code> adds the provided values to this vector internally,
* and returns a handle to this vector for easy chaining of calls.
*
* @param addX value to add to x
* @param addY value to add to y
* @return this
*/
public Vector2f addLocal(float addX, float addY) {
x += addX;
y += addY;
return this;
}
/**
* <code>add</code> adds this vector by <code>vec</code> and stores the
* result in <code>result</code>.
*
* @param vec The vector to add.
* @param result The vector to store the result in.
* @return The result vector, after adding.
*/
public Vector2f add(Vector2f vec, Vector2f result) {
if (null == vec) {
logger.warning("Provided vector is null, null returned.");
return null;
}
if (result == null)
result = new Vector2f();
result.x = x + vec.x;
result.y = y + vec.y;
return result;
}
/**
* <code>dot</code> calculates the dot product of this vector with a
* provided vector. If the provided vector is null, 0 is returned.
*
* @param vec the vector to dot with this vector.
* @return the resultant dot product of this vector and a given vector.
*/
public float dot(Vector2f vec) {
if (null == vec) {
logger.warning("Provided vector is null, 0 returned.");
return 0;
}
return x * vec.x + y * vec.y;
}
/**
* <code>cross</code> calculates the cross product of this vector with a
* parameter vector v.
*
* @param v the vector to take the cross product of with this.
* @return the cross product vector.
*/
public Vector3f cross(Vector2f v) {
return new Vector3f(0, 0, determinant(v));
}
public float determinant(Vector2f v) {
return (x * v.y) - (y * v.x);
}
/**
* Sets this vector to the interpolation by changeAmnt from this to the
* finalVec this=(1-changeAmnt)*this + changeAmnt * finalVec
*
* @param finalVec The final vector to interpolate towards
* @param changeAmnt An amount between 0.0 - 1.0 representing a percentage
* change from this towards finalVec
*/
public Vector2f interpolate(Vector2f finalVec, float changeAmnt) {
this.x = (1 - changeAmnt) * this.x + changeAmnt * finalVec.x;
this.y = (1 - changeAmnt) * this.y + changeAmnt * finalVec.y;
return this;
}
/**
* Sets this vector to the interpolation by changeAmnt from beginVec to
* finalVec this=(1-changeAmnt)*beginVec + changeAmnt * finalVec
*
* @param beginVec The begining vector (delta=0)
* @param finalVec The final vector to interpolate towards (delta=1)
* @param changeAmnt An amount between 0.0 - 1.0 representing a precentage
* change from beginVec towards finalVec
*/
public Vector2f interpolate(
Vector2f beginVec,
Vector2f finalVec,
float changeAmnt
) {
this.x = (1 - changeAmnt) * beginVec.x + changeAmnt * finalVec.x;
this.y = (1 - changeAmnt) * beginVec.y + changeAmnt * finalVec.y;
return this;
}
/**
* Check a vector... if it is null or its floats are NaN or infinite, return
* false. Else return true.
*
* @param vector the vector to check
* @return true or false as stated above.
*/
public static boolean isValidVector(Vector2f vector) {
if (vector == null)
return false;
if (
Float.isNaN(vector.x)
||
Float.isNaN(vector.y)
)
return false;
if (
Float.isInfinite(vector.x)
||
Float.isInfinite(vector.y)
)
return false;
return true;
}
/**
* <code>length</code> calculates the magnitude of this vector.
*
* @return the length or magnitude of the vector.
*/
public float length() {
return FastMath.sqrt(lengthSquared());
}
/**
* <code>lengthSquared</code> calculates the squared value of the magnitude
* of the vector.
*
* @return the magnitude squared of the vector.
*/
public float lengthSquared() {
return x * x + y * y;
}
/**
* <code>distanceSquared</code> calculates the distance squared between this
* vector and vector v.
*
* @param v the second vector to determine the distance squared.
* @return the distance squared between the two vectors.
*/
public float distanceSquared(Vector2f v) {
double dx = x - v.x;
double dy = y - v.y;
return (float) (dx * dx + dy * dy);
}
/**
* <code>distanceSquared</code> calculates the distance squared between this
* vector and vector v.
*
* @param otherX The X coordinate of the v vector
* @param otherY The Y coordinate of the v vector
* @return the distance squared between the two vectors.
*/
public float distanceSquared(float otherX, float otherY) {
double dx = x - otherX;
double dy = y - otherY;
return (float) (dx * dx + dy * dy);
}
/**
* <code>distance</code> calculates the distance between this vector and
* vector v.
*
* @param v the second vector to determine the distance.
* @return the distance between the two vectors.
*/
public float distance(Vector2f v) {
return FastMath.sqrt(distanceSquared(v));
}
/**
* <code>mult</code> multiplies this vector by a scalar. The resultant
* vector is returned.
*
* @param scalar the value to multiply this vector by.
* @return the new vector.
*/
public Vector2f mult(float scalar) {
return new Vector2f(x * scalar, y * scalar);
}
/**
* <code>multLocal</code> multiplies this vector by a scalar internally, and
* returns a handle to this vector for easy chaining of calls.
*
* @param scalar the value to multiply this vector by.
* @return this
*/
public Vector2f multLocal(float scalar) {
x *= scalar;
y *= scalar;
return this;
}
/**
* <code>multLocal</code> multiplies a provided vector to this vector
* internally, and returns a handle to this vector for easy chaining of
* calls. If the provided vector is null, null is returned.
*
* @param vec the vector to mult to this vector.
* @return this
*/
public Vector2f multLocal(Vector2f vec) {
if (null == vec) {
logger.warning("Provided vector is null, null returned.");
return null;
}
x *= vec.x;
y *= vec.y;
return this;
}
/**
* Multiplies this Vector2f's x and y by the scalar and stores the result in
* product. The result is returned for chaining. Similar to
* product=this*scalar;
*
* @param scalar The scalar to multiply by.
* @param product The vector2f to store the result in.
* @return product, after multiplication.
*/
public Vector2f mult(float scalar, Vector2f product) {
if (null == product) {
product = new Vector2f();
}
product.x = x * scalar;
product.y = y * scalar;
return product;
}
/**
* <code>divide</code> divides the values of this vector by a scalar and
* returns the result. The values of this vector remain untouched.
*
* @param scalar the value to divide this vectors attributes by.
* @return the result <code>Vector</code>.
*/
public Vector2f divide(float scalar) {
return new Vector2f(x / scalar, y / scalar);
}
/**
* <code>divideLocal</code> divides this vector by a scalar internally, and
* returns a handle to this vector for easy chaining of calls. Dividing by
* zero will result in an exception.
*
* @param scalar the value to divides this vector by.
* @return this
*/
public Vector2f divideLocal(float scalar) {
x /= scalar;
y /= scalar;
return this;
}
/**
* <code>negate</code> returns the negative of this vector. All values are
* negated and set to a new vector.
*
* @return the negated vector.
*/
public Vector2f negate() {
return new Vector2f(-x, -y);
}
/**
* <code>negateLocal</code> negates the internal values of this vector.
*
* @return this.
*/
public Vector2f negateLocal() {
x = -x;
y = -y;
return this;
}
/**
* <code>subtract</code> subtracts the values of a given vector from those
* of this vector creating a new vector object. If the provided vector is
* null, an exception is thrown.
*
* @param vec the vector to subtract from this vector.
* @return the result vector.
*/
public Vector2f subtract(Vector2f vec) {
return subtract(vec, null);
}
/**
* <code>subtract</code> subtracts the values of a given vector from those
* of this vector storing the result in the given vector object. If the
* provided vector is null, an exception is thrown.
*
* @param vec the vector to subtract from this vector.
* @param store the vector to store the result in. It is safe for this to be
* the same as vec. If null, a new vector is created.
* @return the result vector.
*/
public Vector2f subtract(Vector2f vec, Vector2f store) {
if (store == null)
store = new Vector2f();
store.x = x - vec.x;
store.y = y - vec.y;
return store;
}
/**
* <code>subtract</code> subtracts the given x,y values from those of this
* vector creating a new vector object.
*
* @param valX value to subtract from x
* @param valY value to subtract from y
* @return this
*/
public Vector2f subtract(float valX, float valY) {
return new Vector2f(x - valX, y - valY);
}
/**
* <code>subtractLocal</code> subtracts a provided vector to this vector
* internally, and returns a handle to this vector for easy chaining of
* calls. If the provided vector is null, null is returned.
*
* @param vec the vector to subtract
* @return this
*/
public Vector2f subtractLocal(Vector2f vec) {
if (null == vec) {
logger.warning("Provided vector is null, null returned.");
return null;
}
x -= vec.x;
y -= vec.y;
return this;
}
/**
* <code>subtractLocal</code> subtracts the provided values from this vector
* internally, and returns a handle to this vector for easy chaining of
* calls.
*
* @param valX value to subtract from x
* @param valY value to subtract from y
* @return this
*/
public Vector2f subtractLocal(float valX, float valY) {
x -= valX;
y -= valY;
return this;
}
/**
* <code>normalize</code> returns the unit vector of this vector.
*
* @return unit vector of this vector.
*/
public Vector2f normalize() {
float length = length();
if (length != 0) {
return divide(length);
}
return divide(1);
}
/**
* <code>normalizeLocal</code> makes this vector into a unit vector of
* itself.
*
* @return this.
*/
public Vector2f normalizeLocal() {
float length = length();
if (length != 0) {
return divideLocal(length);
}
return divideLocal(1);
}
/**
* <code>smallestAngleBetween</code> returns (in radians) the minimum angle
* between two vectors. It is assumed that both this vector and the given
* vector are unit vectors (iow, normalized).
*
* @param otherVector a unit vector to find the angle against
* @return the angle in radians.
*/
public float smallestAngleBetween(Vector2f otherVector) {
float dotProduct = dot(otherVector);
float angle = FastMath.acos(dotProduct);
return angle;
}
/**
* <code>angleBetween</code> returns (in radians) the angle required to
* rotate a ray represented by this vector to lie colinear to a ray
* described by the given vector. It is assumed that both this vector and
* the given vector are unit vectors (iow, normalized).
*
* @param otherVector the "destination" unit vector
* @return the angle in radians.
*/
public float angleBetween(Vector2f otherVector) {
float angle = FastMath.atan2(otherVector.y, otherVector.x)
- FastMath.atan2(y, x);
return angle;
}
public float getX() {
return x;
}
public Vector2f setX(float x) {
this.x = x;
return this;
}
public float getY() {
return y;
}
public Vector2f setY(float y) {
this.y = y;
return this;
}
/**
* <code>getAngle</code> returns (in radians) the angle represented by this
* Vector2f as expressed by a conversion from rectangular coordinates
* (<code>x</code>,&nbsp;<code>y</code>) to polar coordinates
* (r,&nbsp;<i>theta</i>).
*
* @return the angle in radians. [-pi, pi)
*/
public float getAngle() {
return FastMath.atan2(y, x);
}
/**
* <code>zero</code> resets this vector's data to zero internally.
*/
public Vector2f zero() {
x = y = 0;
return this;
}
/**
* <code>hashCode</code> returns a unique code for this vector object based
* on it's values. If two vectors are logically equivalent, they will return
* the same hash code value.
*
* @return the hash code value of this vector.
*/
@Override
public int hashCode() {
int hash = 37;
hash += 37 * hash + Float.floatToIntBits(x);
hash += 37 * hash + Float.floatToIntBits(y);
return hash;
}
@Override
public Vector2f clone() {
try {
return (Vector2f) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError(); // can not happen
}
}
/**
* Saves this Vector2f into the given float[] object.
*
* @param floats The float[] to take this Vector2f. If null, a new float[2]
* is created.
* @return The array, with X, Y float values in that order
*/
public float[] toArray(float[] floats) {
if (floats == null) {
floats = new float[2];
}
floats[0] = x;
floats[1] = y;
return floats;
}
/**
* are these two vectors the same? they are is they both have the same x and
* y values.
*
* @param o the object to compare for equality
* @return true if they are equal
*/
@Override
public boolean equals(Object o) {
if (!(o instanceof Vector2f)) {
return false;
}
if (this == o) {
return true;
}
Vector2f comp = (Vector2f) o;
if (Float.compare(x, comp.x) != 0)
return false;
if (Float.compare(y, comp.y) != 0)
return false;
return true;
}
/**
* <code>toString</code> returns the string representation of this vector
* object. The format of the string is such: com.jme.math.Vector2f
* [X=XX.XXXX, Y=YY.YYYY]
*
* @return the string representation of this vector.
*/
@Override
public String toString() {
return "(" + x + ", " + y + ")";
}
/**
* Used with serialization. Not to be called manually.
*
* @param in ObjectInput
* @throws IOException
* @throws ClassNotFoundException
* @see java.io.Externalizable
*/
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
x = in.readFloat();
y = in.readFloat();
}
/**
* Used with serialization. Not to be called manually.
*
* @param out ObjectOutput
* @throws IOException
* @see java.io.Externalizable
*/
public void writeExternal(ObjectOutput out) throws IOException {
out.writeFloat(x);
out.writeFloat(y);
}
public void rotateAroundOrigin(float angle, boolean cw) {
if (cw)
angle = -angle;
float newX = FastMath.cos(angle) * x - FastMath.sin(angle) * y;
float newY = FastMath.sin(angle) * x + FastMath.cos(angle) * y;
x = newX;
y = newY;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,971 @@
/*
* Copyright (c) 2009-2012 jMonkeyEngine
* 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 'jMonkeyEngine' 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.
*/
package com.jme3.math;
import java.util.logging.Logger;
/**
* <code>Vector4f</code> defines a Vector for a four float value tuple.
* <code>Vector4f</code> can represent any four dimensional value, such as a
* vertex, a normal, etc. Utility methods are also included to aid in
* mathematical calculations.
*
* @author Maarten Steur
*/
public final class Vector4f implements Cloneable, java.io.Serializable {
static final long serialVersionUID = 1;
private static final Logger logger = Logger.getLogger(Vector4f.class.getName());
public final static Vector4f ZERO = new Vector4f(0, 0, 0, 0);
public final static Vector4f NAN = new Vector4f(Float.NaN, Float.NaN, Float.NaN, Float.NaN);
public final static Vector4f UNIT_X = new Vector4f(1, 0, 0, 0);
public final static Vector4f UNIT_Y = new Vector4f(0, 1, 0, 0);
public final static Vector4f UNIT_Z = new Vector4f(0, 0, 1, 0);
public final static Vector4f UNIT_W = new Vector4f(0, 0, 0, 1);
public final static Vector4f UNIT_XYZW = new Vector4f(1, 1, 1, 1);
public final static Vector4f POSITIVE_INFINITY = new Vector4f(
Float.POSITIVE_INFINITY,
Float.POSITIVE_INFINITY,
Float.POSITIVE_INFINITY,
Float.POSITIVE_INFINITY
);
public final static Vector4f NEGATIVE_INFINITY = new Vector4f(
Float.NEGATIVE_INFINITY,
Float.NEGATIVE_INFINITY,
Float.NEGATIVE_INFINITY,
Float.NEGATIVE_INFINITY
);
/**
* the x value of the vector.
*/
public float x;
/**
* the y value of the vector.
*/
public float y;
/**
* the z value of the vector.
*/
public float z;
/**
* the w value of the vector.
*/
public float w;
/**
* Constructor instantiates a new <code>Vector3f</code> with default values
* of (0,0,0).
*
*/
public Vector4f() {
x = y = z = w = 0;
}
/**
* Constructor instantiates a new <code>Vector4f</code> with provides
* values.
*
* @param x the x value of the vector.
* @param y the y value of the vector.
* @param z the z value of the vector.
* @param w the w value of the vector.
*/
public Vector4f(float x, float y, float z, float w) {
this.x = x;
this.y = y;
this.z = z;
this.w = w;
}
/**
* Constructor instantiates a new <code>Vector3f</code> that is a copy of
* the provided vector
*
* @param copy The Vector3f to copy
*/
public Vector4f(Vector4f copy) {
this.set(copy);
}
/**
* <code>set</code> sets the x,y,z,w values of the vector based on passed
* parameters.
*
* @param x the x value of the vector.
* @param y the y value of the vector.
* @param z the z value of the vector.
* @param w the w value of the vector.
* @return this vector
*/
public Vector4f set(float x, float y, float z, float w) {
this.x = x;
this.y = y;
this.z = z;
this.w = w;
return this;
}
/**
* <code>set</code> sets the x,y,z values of the vector by copying the
* supplied vector.
*
* @param vect the vector to copy.
* @return this vector
*/
public Vector4f set(Vector4f vect) {
this.x = vect.x;
this.y = vect.y;
this.z = vect.z;
this.w = vect.w;
return this;
}
/**
*
* <code>add</code> adds a provided vector to this vector creating a
* resultant vector which is returned. If the provided vector is null, null
* is returned.
*
* @param vec the vector to add to this.
* @return the resultant vector.
*/
public Vector4f add(Vector4f vec) {
if (null == vec) {
logger.warning("Provided vector is null, null returned.");
return null;
}
return new Vector4f(x + vec.x, y + vec.y, z + vec.z, w + vec.w);
}
/**
*
* <code>add</code> adds the values of a provided vector storing the values
* in the supplied vector.
*
* @param vec the vector to add to this
* @param result the vector to store the result in
* @return result returns the supplied result vector.
*/
public Vector4f add(Vector4f vec, Vector4f result) {
result.x = x + vec.x;
result.y = y + vec.y;
result.z = z + vec.z;
result.w = w + vec.w;
return result;
}
/**
* <code>addLocal</code> adds a provided vector to this vector internally,
* and returns a handle to this vector for easy chaining of calls. If the
* provided vector is null, null is returned.
*
* @param vec the vector to add to this vector.
* @return this
*/
public Vector4f addLocal(Vector4f vec) {
if (null == vec) {
logger.warning("Provided vector is null, null returned.");
return null;
}
x += vec.x;
y += vec.y;
z += vec.z;
w += vec.w;
return this;
}
/**
*
* <code>add</code> adds the provided values to this vector, creating a new
* vector that is then returned.
*
* @param addX the x value to add.
* @param addY the y value to add.
* @param addZ the z value to add.
* @return the result vector.
*/
public Vector4f add(float addX, float addY, float addZ, float addW) {
return new Vector4f(x + addX, y + addY, z + addZ, w + addW);
}
/**
* <code>addLocal</code> adds the provided values to this vector internally,
* and returns a handle to this vector for easy chaining of calls.
*
* @param addX value to add to x
* @param addY value to add to y
* @param addZ value to add to z
* @return this
*/
public Vector4f addLocal(float addX, float addY, float addZ, float addW) {
x += addX;
y += addY;
z += addZ;
w += addW;
return this;
}
/**
*
* <code>scaleAdd</code> multiplies this vector by a scalar then adds the
* given Vector3f.
*
* @param scalar the value to multiply this vector by.
* @param add the value to add
*/
public Vector4f scaleAdd(float scalar, Vector4f add) {
x = x * scalar + add.x;
y = y * scalar + add.y;
z = z * scalar + add.z;
w = w * scalar + add.w;
return this;
}
/**
*
* <code>scaleAdd</code> multiplies the given vector by a scalar then adds
* the given vector.
*
* @param scalar the value to multiply this vector by.
* @param mult the value to multiply the scalar by
* @param add the value to add
*/
public Vector4f scaleAdd(float scalar, Vector4f mult, Vector4f add) {
this.x = mult.x * scalar + add.x;
this.y = mult.y * scalar + add.y;
this.z = mult.z * scalar + add.z;
this.w = mult.w * scalar + add.w;
return this;
}
/**
*
* <code>dot</code> calculates the dot product of this vector with a
* provided vector. If the provided vector is null, 0 is returned.
*
* @param vec the vector to dot with this vector.
* @return the resultant dot product of this vector and a given vector.
*/
public float dot(Vector4f vec) {
if (null == vec) {
logger.warning("Provided vector is null, 0 returned.");
return 0;
}
return x * vec.x + y * vec.y + z * vec.z + w * vec.w;
}
public Vector4f project(Vector4f other) {
float n = this.dot(other); // A . B
float d = other.lengthSquared(); // |B|^2
return new Vector4f(other).normalizeLocal().multLocal(n / d);
}
/**
* Returns true if this vector is a unit vector (length() ~= 1), returns
* false otherwise.
*
* @return true if this vector is a unit vector (length() ~= 1), or false
* otherwise.
*/
public boolean isUnitVector() {
float len = length();
return 0.99f < len && len < 1.01f;
}
/**
* <code>length</code> calculates the magnitude of this vector.
*
* @return the length or magnitude of the vector.
*/
public float length() {
return FastMath.sqrt(lengthSquared());
}
/**
* <code>lengthSquared</code> calculates the squared value of the magnitude
* of the vector.
*
* @return the magnitude squared of the vector.
*/
public float lengthSquared() {
return x * x + y * y + z * z + w * w;
}
/**
* <code>distanceSquared</code> calculates the distance squared between this
* vector and vector v.
*
* @param v the second vector to determine the distance squared.
* @return the distance squared between the two vectors.
*/
public float distanceSquared(Vector4f v) {
double dx = x - v.x;
double dy = y - v.y;
double dz = z - v.z;
double dw = w - v.w;
return (float) (dx * dx + dy * dy + dz * dz + dw * dw);
}
/**
* <code>distance</code> calculates the distance between this vector and
* vector v.
*
* @param v the second vector to determine the distance.
* @return the distance between the two vectors.
*/
public float distance(Vector4f v) {
return FastMath.sqrt(distanceSquared(v));
}
/**
*
* <code>mult</code> multiplies this vector by a scalar. The resultant
* vector is returned.
*
* @param scalar the value to multiply this vector by.
* @return the new vector.
*/
public Vector4f mult(float scalar) {
return new Vector4f(x * scalar, y * scalar, z * scalar, w * scalar);
}
/**
*
* <code>mult</code> multiplies this vector by a scalar. The resultant
* vector is supplied as the second parameter and returned.
*
* @param scalar the scalar to multiply this vector by.
* @param product the product to store the result in.
* @return product
*/
public Vector4f mult(float scalar, Vector4f product) {
if (null == product) {
product = new Vector4f();
}
product.x = x * scalar;
product.y = y * scalar;
product.z = z * scalar;
product.w = w * scalar;
return product;
}
/**
* <code>multLocal</code> multiplies this vector by a scalar internally, and
* returns a handle to this vector for easy chaining of calls.
*
* @param scalar the value to multiply this vector by.
* @return this
*/
public Vector4f multLocal(float scalar) {
x *= scalar;
y *= scalar;
z *= scalar;
w *= scalar;
return this;
}
/**
* <code>multLocal</code> multiplies a provided vector to this vector
* internally, and returns a handle to this vector for easy chaining of
* calls. If the provided vector is null, null is returned.
*
* @param vec the vector to mult to this vector.
* @return this
*/
public Vector4f multLocal(Vector4f vec) {
if (null == vec) {
logger.warning("Provided vector is null, null returned.");
return null;
}
x *= vec.x;
y *= vec.y;
z *= vec.z;
w *= vec.w;
return this;
}
/**
* <code>multLocal</code> multiplies this vector by 3 scalars internally,
* and returns a handle to this vector for easy chaining of calls.
*
* @param x
* @param y
* @param z
* @param w
* @return this
*/
public Vector4f multLocal(float x, float y, float z, float w) {
this.x *= x;
this.y *= y;
this.z *= z;
this.w *= w;
return this;
}
/**
* <code>multLocal</code> multiplies a provided vector to this vector
* internally, and returns a handle to this vector for easy chaining of
* calls. If the provided vector is null, null is returned.
*
* @param vec the vector to mult to this vector.
* @return this
*/
public Vector4f mult(Vector4f vec) {
if (null == vec) {
logger.warning("Provided vector is null, null returned.");
return null;
}
return mult(vec, null);
}
/**
* <code>multLocal</code> multiplies a provided vector to this vector
* internally, and returns a handle to this vector for easy chaining of
* calls. If the provided vector is null, null is returned.
*
* @param vec the vector to mult to this vector.
* @param store result vector (null to create a new vector)
* @return this
*/
public Vector4f mult(Vector4f vec, Vector4f store) {
if (null == vec) {
logger.warning("Provided vector is null, null returned.");
return null;
}
if (store == null)
store = new Vector4f();
return store.set(x * vec.x, y * vec.y, z * vec.z, w * vec.w);
}
/**
* <code>divide</code> divides the values of this vector by a scalar and
* returns the result. The values of this vector remain untouched.
*
* @param scalar the value to divide this vectors attributes by.
* @return the result <code>Vector</code>.
*/
public Vector4f divide(float scalar) {
scalar = 1f / scalar;
return new Vector4f(x * scalar, y * scalar, z * scalar, w * scalar);
}
/**
* <code>divideLocal</code> divides this vector by a scalar internally, and
* returns a handle to this vector for easy chaining of calls. Dividing by
* zero will result in an exception.
*
* @param scalar the value to divides this vector by.
* @return this
*/
public Vector4f divideLocal(float scalar) {
scalar = 1f / scalar;
x *= scalar;
y *= scalar;
z *= scalar;
w *= scalar;
return this;
}
/**
* <code>divide</code> divides the values of this vector by a scalar and
* returns the result. The values of this vector remain untouched.
*
* @param scalar the value to divide this vectors attributes by.
* @return the result <code>Vector</code>.
*/
public Vector4f divide(Vector4f scalar) {
return new Vector4f(x / scalar.x, y / scalar.y, z / scalar.z, w / scalar.w);
}
/**
* <code>divideLocal</code> divides this vector by a scalar internally, and
* returns a handle to this vector for easy chaining of calls. Dividing by
* zero will result in an exception.
*
* @param scalar the value to divides this vector by.
* @return this
*/
public Vector4f divideLocal(Vector4f scalar) {
x /= scalar.x;
y /= scalar.y;
z /= scalar.z;
w /= scalar.w;
return this;
}
/**
*
* <code>negate</code> returns the negative of this vector. All values are
* negated and set to a new vector.
*
* @return the negated vector.
*/
public Vector4f negate() {
return new Vector4f(-x, -y, -z, -w);
}
/**
*
* <code>negateLocal</code> negates the internal values of this vector.
*
* @return this.
*/
public Vector4f negateLocal() {
x = -x;
y = -y;
z = -z;
w = -w;
return this;
}
/**
*
* <code>subtract</code> subtracts the values of a given vector from those
* of this vector creating a new vector object. If the provided vector is
* null, null is returned.
*
* @param vec the vector to subtract from this vector.
* @return the result vector.
*/
public Vector4f subtract(Vector4f vec) {
return new Vector4f(x - vec.x, y - vec.y, z - vec.z, w - vec.w);
}
/**
* <code>subtractLocal</code> subtracts a provided vector to this vector
* internally, and returns a handle to this vector for easy chaining of
* calls. If the provided vector is null, null is returned.
*
* @param vec the vector to subtract
* @return this
*/
public Vector4f subtractLocal(Vector4f vec) {
if (null == vec) {
logger.warning("Provided vector is null, null returned.");
return null;
}
x -= vec.x;
y -= vec.y;
z -= vec.z;
w -= vec.w;
return this;
}
/**
*
* <code>subtract</code>
*
* @param vec the vector to subtract from this
* @param result the vector to store the result in
* @return result
*/
public Vector4f subtract(Vector4f vec, Vector4f result) {
if (result == null) {
result = new Vector4f();
}
result.x = x - vec.x;
result.y = y - vec.y;
result.z = z - vec.z;
result.w = w - vec.w;
return result;
}
/**
*
* <code>subtract</code> subtracts the provided values from this vector,
* creating a new vector that is then returned.
*
* @param subtractX the x value to subtract.
* @param subtractY the y value to subtract.
* @param subtractZ the z value to subtract.
* @param subtractW the w value to subtract.
* @return the result vector.
*/
public Vector4f subtract(float subtractX, float subtractY, float subtractZ, float subtractW) {
return new Vector4f(x - subtractX, y - subtractY, z - subtractZ, w - subtractW);
}
/**
* <code>subtractLocal</code> subtracts the provided values from this vector
* internally, and returns a handle to this vector for easy chaining of
* calls.
*
* @param subtractX the x value to subtract.
* @param subtractY the y value to subtract.
* @param subtractZ the z value to subtract.
* @param subtractW the w value to subtract.
* @return this
*/
public Vector4f subtractLocal(
float subtractX,
float subtractY,
float subtractZ,
float subtractW
) {
x -= subtractX;
y -= subtractY;
z -= subtractZ;
w -= subtractW;
return this;
}
/**
* <code>normalize</code> returns the unit vector of this vector.
*
* @return unit vector of this vector.
*/
public Vector4f normalize() {
// float length = length();
// if (length != 0) {
// return divide(length);
// }
//
// return divide(1);
float length = x * x + y * y + z * z + w * w;
if (length != 1f && length != 0f) {
length = 1.0f / FastMath.sqrt(length);
return new Vector4f(x * length, y * length, z * length, w * length);
}
return clone();
}
/**
* <code>normalizeLocal</code> makes this vector into a unit vector of
* itself.
*
* @return this.
*/
public Vector4f normalizeLocal() {
// NOTE: this implementation is more optimized
// than the old jme normalize as this method
// is commonly used.
float length = x * x + y * y + z * z + w * w;
if (length != 1f && length != 0f) {
length = 1.0f / FastMath.sqrt(length);
x *= length;
y *= length;
z *= length;
w *= length;
}
return this;
}
/**
* <code>maxLocal</code> computes the maximum value for each component in
* this and <code>other</code> vector. The result is stored in this vector.
*
* @param other
*/
public Vector4f maxLocal(Vector4f other) {
x = other.x > x ? other.x : x;
y = other.y > y ? other.y : y;
z = other.z > z ? other.z : z;
w = other.w > w ? other.w : w;
return this;
}
/**
* <code>minLocal</code> computes the minimum value for each component in
* this and <code>other</code> vector. The result is stored in this vector.
*
* @param other
*/
public Vector4f minLocal(Vector4f other) {
x = other.x < x ? other.x : x;
y = other.y < y ? other.y : y;
z = other.z < z ? other.z : z;
w = other.w < w ? other.w : w;
return this;
}
/**
* <code>zero</code> resets this vector's data to zero internally.
*/
public Vector4f zero() {
x = y = z = w = 0;
return this;
}
/**
* <code>angleBetween</code> returns (in radians) the angle between two
* vectors. It is assumed that both this vector and the given vector are
* unit vectors (iow, normalized).
*
* @param otherVector a unit vector to find the angle against
* @return the angle in radians.
*/
public float angleBetween(Vector4f otherVector) {
float dotProduct = dot(otherVector);
float angle = FastMath.acos(dotProduct);
return angle;
}
/**
* Sets this vector to the interpolation by changeAmnt from this to the
* finalVec this=(1-changeAmnt)*this + changeAmnt * finalVec
*
* @param finalVec The final vector to interpolate towards
* @param changeAmnt An amount between 0.0 - 1.0 representing a precentage
* change from this towards finalVec
*/
public Vector4f interpolate(Vector4f finalVec, float changeAmnt) {
this.x = (1 - changeAmnt) * this.x + changeAmnt * finalVec.x;
this.y = (1 - changeAmnt) * this.y + changeAmnt * finalVec.y;
this.z = (1 - changeAmnt) * this.z + changeAmnt * finalVec.z;
this.w = (1 - changeAmnt) * this.w + changeAmnt * finalVec.w;
return this;
}
/**
* Sets this vector to the interpolation by changeAmnt from beginVec to
* finalVec this=(1-changeAmnt)*beginVec + changeAmnt * finalVec
*
* @param beginVec the beging vector (changeAmnt=0)
* @param finalVec The final vector to interpolate towards
* @param changeAmnt An amount between 0.0 - 1.0 representing a precentage
* change from beginVec towards finalVec
*/
public Vector4f interpolate(Vector4f beginVec, Vector4f finalVec, float changeAmnt) {
this.x = (1 - changeAmnt) * beginVec.x + changeAmnt * finalVec.x;
this.y = (1 - changeAmnt) * beginVec.y + changeAmnt * finalVec.y;
this.z = (1 - changeAmnt) * beginVec.z + changeAmnt * finalVec.z;
this.w = (1 - changeAmnt) * beginVec.w + changeAmnt * finalVec.w;
return this;
}
/**
* Check a vector... if it is null or its floats are NaN or infinite, return
* false. Else return true.
*
* @param vector the vector to check
* @return true or false as stated above.
*/
public static boolean isValidVector(Vector4f vector) {
if (vector == null)
return false;
if (
Float.isNaN(vector.x)
||
Float.isNaN(vector.y)
||
Float.isNaN(vector.z)
||
Float.isNaN(vector.w)
)
return false;
if (
Float.isInfinite(vector.x)
||
Float.isInfinite(vector.y)
||
Float.isInfinite(vector.z)
||
Float.isInfinite(vector.w)
)
return false;
return true;
}
@Override
public Vector4f clone() {
try {
return (Vector4f) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError(); // can not happen
}
}
/**
* Saves this Vector3f into the given float[] object.
*
* @param floats The float[] to take this Vector3f. If null, a new float[3]
* is created.
* @return The array, with X, Y, Z float values in that order
*/
public float[] toArray(float[] floats) {
if (floats == null) {
floats = new float[4];
}
floats[0] = x;
floats[1] = y;
floats[2] = z;
floats[3] = w;
return floats;
}
/**
* are these two vectors the same? they are is they both have the same x,y,
* and z values.
*
* @param o the object to compare for equality
* @return true if they are equal
*/
@Override
public boolean equals(Object o) {
if (!(o instanceof Vector4f)) {
return false;
}
if (this == o) {
return true;
}
Vector4f comp = (Vector4f) o;
if (Float.compare(x, comp.x) != 0)
return false;
if (Float.compare(y, comp.y) != 0)
return false;
if (Float.compare(z, comp.z) != 0)
return false;
if (Float.compare(w, comp.w) != 0)
return false;
return true;
}
/**
* <code>hashCode</code> returns a unique code for this vector object based
* on it's values. If two vectors are logically equivalent, they will return
* the same hash code value.
*
* @return the hash code value of this vector.
*/
@Override
public int hashCode() {
int hash = 37;
hash += 37 * hash + Float.floatToIntBits(x);
hash += 37 * hash + Float.floatToIntBits(y);
hash += 37 * hash + Float.floatToIntBits(z);
hash += 37 * hash + Float.floatToIntBits(w);
return hash;
}
/**
* <code>toString</code> returns the string representation of this vector.
* The format is:
*
* org.jme.math.Vector3f [X=XX.XXXX, Y=YY.YYYY, Z=ZZ.ZZZZ, W=WW.WWWW]
*
* @return the string representation of this vector.
*/
@Override
public String toString() {
return "(" + x + ", " + y + ", " + z + ", " + w + ")";
}
public float getX() {
return x;
}
public Vector4f setX(float x) {
this.x = x;
return this;
}
public float getY() {
return y;
}
public Vector4f setY(float y) {
this.y = y;
return this;
}
public float getZ() {
return z;
}
public Vector4f setZ(float z) {
this.z = z;
return this;
}
public float getW() {
return w;
}
public Vector4f setW(float w) {
this.w = w;
return this;
}
/**
* @param index
* @return x value if index == 0, y value if index == 1 or z value if index
* == 2
* @throws IllegalArgumentException if index is not one of 0, 1, 2.
*/
public float get(int index) {
switch (index) {
case 0:
return x;
case 1:
return y;
case 2:
return z;
case 3:
return w;
}
throw new IllegalArgumentException("index must be either 0, 1, 2 or 3");
}
/**
* @param index which field index in this vector to set.
* @param value to set to one of x, y, z or w.
* @throws IllegalArgumentException if index is not one of 0, 1, 2, 3.
*/
public void set(int index, float value) {
switch (index) {
case 0:
x = value;
return;
case 1:
y = value;
return;
case 2:
z = value;
return;
case 3:
w = value;
return;
}
throw new IllegalArgumentException("index must be either 0, 1, 2 or 3");
}
}

View File

@@ -0,0 +1,105 @@
/*
* Copyright (c) 2009-2012 jMonkeyEngine
* 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 'jMonkeyEngine' 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.
*/
package com.jme3.system;
/**
* <code>NanoTimer</code> is a System.nanoTime implementation of
* <code>Timer</code>. This is primarily useful for headless applications
* running on a server.
*
* @author Matthew D. Hicks
*/
public class NanoTimer extends Timer {
private static final long TIMER_RESOLUTION = 1000000000L;
private static final float INVERSE_TIMER_RESOLUTION = 1f / TIMER_RESOLUTION;
private long startTime;
private long previousTime;
private float tpf;
private float fps;
private long currentTime;
public NanoTimer() {
startTime = System.nanoTime();
}
/**
* Returns the time in seconds. The timer starts at 0.0 seconds.
*
* @return the current time in seconds
*/
protected long getTimeInternal() {
return System.nanoTime() - startTime;
}
@Override
public float getTimeInSeconds() {
return getTime() * INVERSE_TIMER_RESOLUTION;
}
@Override
public long getTime() {
return currentTime;
}
@Override
public long getResolution() {
return TIMER_RESOLUTION;
}
@Override
public float getFrameRate() {
return fps;
}
@Override
public float getTimePerFrame() {
return tpf;
}
@Override
public void update() {
currentTime = getTimeInternal();
tpf = (currentTime - previousTime) * (1.0f / TIMER_RESOLUTION);
fps = 1.0f / tpf;
previousTime = getTime();
}
@Override
public void reset() {
startTime = System.nanoTime();
currentTime = getTimeInternal();
previousTime = getTime();
}
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright (c) 2009-2012 jMonkeyEngine
* 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 'jMonkeyEngine' 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.
*/
package com.jme3.system;
/**
* <code>Timer</code> is the base class for a high resolution timer. It is
* created from getTimer("display system")
*
* @author Mark Powell
* @version $Id: Timer.java,v 1.18 2007/03/09 10:19:34 rherlitz Exp $
*/
public abstract class Timer {
/**
* Returns the current time in ticks. A tick is an arbitrary measure of time
* defined by the timer implementation. The number of ticks per second is
* given by <code>getResolution()</code>. The timer starts at 0 ticks.
*
* @return a long value representing the current time
*/
public abstract long getTime();
/**
* Returns the time in seconds. The timer starts at 0.0 seconds.
*
* @return the current time in seconds
*/
public float getTimeInSeconds() {
return getTime() / (float) getResolution();
}
/**
* Returns the resolution of the timer.
*
* @return the number of timer ticks per second
*/
public abstract long getResolution();
/**
* Returns the "calls per second". If this is called every frame, then it
* will return the "frames per second".
*
* @return The "calls per second".
*/
public abstract float getFrameRate();
/**
* Returns the time, in seconds, between the last call and the current one.
*
* @return Time between this call and the last one.
*/
public abstract float getTimePerFrame();
/**
* <code>update</code> recalculates the frame rate based on the previous
* call to update. It is assumed that update is called each frame.
*/
public abstract void update();
/**
* Reset the timer to 0. Clear any tpf history.
*/
public abstract void reset();
}

View File

@@ -0,0 +1,117 @@
package dev.slimevr;
import dev.slimevr.gui.Keybinding;
import dev.slimevr.gui.VRServerGUI;
import io.eiren.util.logging.LogManager;
import org.apache.commons.cli.*;
import org.apache.commons.lang3.JavaVersion;
import org.apache.commons.lang3.SystemUtils;
import javax.swing.*;
import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
public class Main {
public static String VERSION = "0.2.1";
public static VRServer vrServer;
public static void main(String[] args) {
System.setProperty("awt.useSystemAAFontSettings", "on");
System.setProperty("swing.aatext", "true");
CommandLineParser parser = new DefaultParser();
HelpFormatter formatter = new HelpFormatter();
CommandLine cmd = null;
Options options = new Options();
Option noGui = new Option(
"g",
"no-gui",
false,
"disable swing gui (allow for other gui to be used)"
);
Option help = new Option("h", "help", false, "Show help");
options.addOption(noGui);
options.addOption(help);
try {
cmd = parser.parse(options, args);
} catch (ParseException e) {
System.out.println(e.getMessage());
formatter.printHelp("slimevr.jar", options);
System.exit(1);
}
if (cmd.hasOption("help")) {
formatter.printHelp("slimevr.jar", options);
System.exit(0);
}
File dir = new File("").getAbsoluteFile();
try {
LogManager.initialize(new File(dir, "logs/"), dir);
} catch (Exception e1) {
e1.printStackTrace();
}
if (!SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_17)) {
LogManager.severe("SlimeVR start-up error! A minimum of Java 17 is required.");
JOptionPane
.showMessageDialog(
null,
"SlimeVR start-up error! A minimum of Java 17 is required.",
"SlimeVR: Java Runtime Mismatch",
JOptionPane.ERROR_MESSAGE
);
return;
}
try {
new ServerSocket(6969).close();
new ServerSocket(35903).close();
new ServerSocket(21110).close();
} catch (IOException e) {
LogManager
.severe(
"SlimeVR start-up error! Required ports are busy. Make sure there is no other instance of SlimeVR Server running."
);
JOptionPane
.showMessageDialog(
null,
"SlimeVR start-up error! Required ports are busy. Make sure there is no other instance of SlimeVR Server running.",
"SlimeVR: Ports are busy",
JOptionPane.ERROR_MESSAGE
);
return;
}
try {
vrServer = new VRServer();
vrServer.start();
new Keybinding(vrServer);
if (!cmd.hasOption("no-gui"))
new VRServerGUI(vrServer);
} catch (Throwable e) {
e.printStackTrace();
try {
Thread.sleep(2000L);
} catch (InterruptedException e2) {
e.printStackTrace();
}
System.exit(1); // Exit in case error happened on init and window
// not appeared, but some thread
// started
} finally {
try {
Thread.sleep(2000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

View File

@@ -0,0 +1,8 @@
package dev.slimevr;
public enum NetworkProtocol {
OWO_LEGACY,
SLIMEVR_RAW,
SLIMEVR_FLATBUFFER,
SLIMEVR_WEBSOCKET
}

View File

@@ -0,0 +1,321 @@
package dev.slimevr;
import com.jme3.system.NanoTimer;
import dev.slimevr.autobone.AutoBoneHandler;
import dev.slimevr.bridge.Bridge;
import dev.slimevr.bridge.VMCBridge;
import dev.slimevr.config.ConfigManager;
import dev.slimevr.platform.windows.WindowsNamedPipeBridge;
import dev.slimevr.poserecorder.BVHRecorder;
import dev.slimevr.protocol.ProtocolAPI;
import dev.slimevr.serial.SerialHandler;
import dev.slimevr.util.ann.VRServerThread;
import dev.slimevr.vr.DeviceManager;
import dev.slimevr.vr.processor.HumanPoseProcessor;
import dev.slimevr.vr.processor.skeleton.Skeleton;
import dev.slimevr.vr.trackers.HMDTracker;
import dev.slimevr.vr.trackers.ShareableTracker;
import dev.slimevr.vr.trackers.Tracker;
import dev.slimevr.vr.trackers.udp.TrackersUDPServer;
import dev.slimevr.websocketapi.WebSocketVRBridge;
import io.eiren.util.OperatingSystem;
import io.eiren.util.ann.ThreadSafe;
import io.eiren.util.ann.ThreadSecure;
import io.eiren.util.collections.FastList;
import solarxr_protocol.datatypes.TrackerIdT;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.function.Consumer;
public class VRServer extends Thread {
public final HumanPoseProcessor humanPoseProcessor;
public final HMDTracker hmdTracker;
private final List<Tracker> trackers = new FastList<>();
private final TrackersUDPServer trackersServer;
private final List<Bridge> bridges = new FastList<>();
private final Queue<Runnable> tasks = new LinkedBlockingQueue<>();
private final List<Consumer<Tracker>> newTrackersConsumers = new FastList<>();
private final List<Runnable> onTick = new FastList<>();
private final List<? extends ShareableTracker> shareTrackers;
private final DeviceManager deviceManager;
private final BVHRecorder bvhRecorder;
private final SerialHandler serialHandler;
private final AutoBoneHandler autoBoneHandler;
private final ProtocolAPI protocolAPI;
private final ConfigManager configManager;
private final NanoTimer fpsTimer = new NanoTimer();
/**
* This function is used by VRWorkout, do not remove!
*/
public VRServer() {
this("vrconfig.yml");
}
public VRServer(String configPath) {
super("VRServer");
this.configManager = new ConfigManager(configPath);
this.configManager.loadConfig();
deviceManager = new DeviceManager(this);
serialHandler = new SerialHandler();
autoBoneHandler = new AutoBoneHandler(this);
protocolAPI = new ProtocolAPI(this);
hmdTracker = new HMDTracker("HMD");
hmdTracker.position.set(0, 1.8f, 0); // Set starting position for easier
// debugging
// TODO Multiple processors
humanPoseProcessor = new HumanPoseProcessor(this, hmdTracker);
shareTrackers = humanPoseProcessor.getComputedTrackers();
// Start server for SlimeVR trackers
trackersServer = new TrackersUDPServer(6969, "Sensors UDP server", this::registerTracker);
// OpenVR bridge currently only supports Windows
if (OperatingSystem.getCurrentPlatform() == OperatingSystem.WINDOWS) {
// Create named pipe bridge for SteamVR driver
WindowsNamedPipeBridge driverBridge = new WindowsNamedPipeBridge(
this,
hmdTracker,
"steamvr",
"SteamVR Driver Bridge",
"\\\\.\\pipe\\SlimeVRDriver",
shareTrackers
);
tasks.add(driverBridge::startBridge);
bridges.add(driverBridge);
// Create named pipe bridge for SteamVR input
// TODO: how do we want to handle HMD input from the feeder app?
WindowsNamedPipeBridge feederBridge = new WindowsNamedPipeBridge(
this,
null,
"steamvr_feeder",
"SteamVR Feeder Bridge",
"\\\\.\\pipe\\SlimeVRInput",
new FastList<>()
);
tasks.add(feederBridge::startBridge);
bridges.add(feederBridge);
}
// Create WebSocket server
WebSocketVRBridge wsBridge = new WebSocketVRBridge(hmdTracker, shareTrackers, this);
tasks.add(wsBridge::startBridge);
bridges.add(wsBridge);
// Create VMCBridge
try {
VMCBridge vmcBridge = new VMCBridge(39539, 39540, InetAddress.getLocalHost());
tasks.add(vmcBridge::startBridge);
bridges.add(vmcBridge);
} catch (UnknownHostException e) {
e.printStackTrace();
}
bvhRecorder = new BVHRecorder(this);
registerTracker(hmdTracker);
for (Tracker tracker : shareTrackers) {
registerTracker(tracker);
}
}
public boolean hasBridge(Class<? extends Bridge> bridgeClass) {
for (Bridge bridge : bridges) {
if (bridgeClass.isAssignableFrom(bridge.getClass())) {
return true;
}
}
return false;
}
@ThreadSafe
public <E extends Bridge> E getVRBridge(Class<E> bridgeClass) {
for (Bridge bridge : bridges) {
if (bridgeClass.isAssignableFrom(bridge.getClass())) {
return bridgeClass.cast(bridge);
}
}
return null;
}
public void addOnTick(Runnable runnable) {
this.onTick.add(runnable);
}
@ThreadSafe
public void addNewTrackerConsumer(Consumer<Tracker> consumer) {
queueTask(() -> {
newTrackersConsumers.add(consumer);
for (Tracker tracker : trackers) {
consumer.accept(tracker);
}
});
}
@ThreadSafe
public void trackerUpdated(Tracker tracker) {
queueTask(() -> {
humanPoseProcessor.trackerUpdated(tracker);
this.getConfigManager().getVrConfig().writeTrackerConfig(tracker);
this.getConfigManager().saveConfig();
});
}
@ThreadSafe
public void addSkeletonUpdatedCallback(Consumer<Skeleton> consumer) {
queueTask(() -> humanPoseProcessor.addSkeletonUpdatedCallback(consumer));
}
@Override
@VRServerThread
public void run() {
trackersServer.start();
while (true) {
fpsTimer.update();
// final long start = System.currentTimeMillis();
do {
Runnable task = tasks.poll();
if (task == null)
break;
task.run();
} while (true);
for (Runnable task : onTick) {
task.run();
}
for (Bridge bridge : bridges) {
bridge.dataRead();
}
for (Tracker tracker : trackers) {
tracker.tick();
}
humanPoseProcessor.update();
for (Bridge bridge : bridges) {
bridge.dataWrite();
}
// final long time = System.currentTimeMillis() - start;
try {
Thread.sleep(1); // 1000Hz
} catch (InterruptedException ignored) {}
}
}
@ThreadSafe
public void queueTask(Runnable r) {
tasks.add(r);
}
@VRServerThread
private void trackerAdded(Tracker tracker) {
humanPoseProcessor.trackerAdded(tracker);
}
@ThreadSecure
public void registerTracker(Tracker tracker) {
this.getConfigManager().getVrConfig().readTrackerConfig(tracker);
queueTask(() -> {
trackers.add(tracker);
trackerAdded(tracker);
for (Consumer<Tracker> tc : newTrackersConsumers) {
tc.accept(tracker);
}
});
}
public void resetTrackers() {
queueTask(humanPoseProcessor::resetTrackers);
}
public void resetTrackersYaw() {
queueTask(humanPoseProcessor::resetTrackersYaw);
}
public void setLegTweaksEnabled(boolean value) {
queueTask(() -> humanPoseProcessor.setLegTweaksEnabled(value));
}
public void setSkatingReductionEnabled(boolean value) {
queueTask(() -> humanPoseProcessor.setSkatingCorrectionEnabled(value));
}
public void setFloorClipEnabled(boolean value) {
queueTask(() -> humanPoseProcessor.setFloorClipEnabled(value));
}
public int getTrackersCount() {
return trackers.size();
}
public List<Tracker> getAllTrackers() {
return new FastList<>(trackers);
}
public Tracker getTrackerById(TrackerIdT id) {
for (Tracker tracker : trackers) {
if (tracker.getTrackerNum() != id.getTrackerNum()) {
continue;
}
// Handle synthetic devices
if (id.getDeviceId() == null && tracker.getDevice() == null) {
return tracker;
}
if (
tracker.getDevice() != null
&& id.getDeviceId() != null
&& id.getDeviceId().getId() == tracker.getDevice().getId()
) {
// This is a physical tracker, and both device id and the
// tracker num match
return tracker;
}
}
return null;
}
public BVHRecorder getBvhRecorder() {
return this.bvhRecorder;
}
public SerialHandler getSerialHandler() {
return this.serialHandler;
}
public AutoBoneHandler getAutoBoneHandler() {
return this.autoBoneHandler;
}
public ProtocolAPI getProtocolAPI() {
return protocolAPI;
}
public TrackersUDPServer getTrackersServer() {
return trackersServer;
}
public DeviceManager getDeviceManager() {
return deviceManager;
}
public ConfigManager getConfigManager() {
return configManager;
}
public NanoTimer getFpsTimer() {
return fpsTimer;
}
}

View File

@@ -0,0 +1,908 @@
package dev.slimevr.autobone;
import com.jme3.math.FastMath;
import com.jme3.math.Vector3f;
import dev.slimevr.VRServer;
import dev.slimevr.autobone.errors.*;
import dev.slimevr.config.AutoBoneConfig;
import dev.slimevr.poserecorder.PoseFrameIO;
import dev.slimevr.poserecorder.PoseFrameSkeleton;
import dev.slimevr.poserecorder.PoseFrameTracker;
import dev.slimevr.poserecorder.PoseFrames;
import dev.slimevr.vr.processor.HumanPoseProcessor;
import dev.slimevr.vr.processor.TransformNode;
import dev.slimevr.vr.processor.skeleton.*;
import dev.slimevr.vr.trackers.TrackerRole;
import io.eiren.util.StringUtils;
import io.eiren.util.collections.FastList;
import io.eiren.util.logging.LogManager;
import org.apache.commons.lang3.tuple.Pair;
import java.io.File;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
public class AutoBone {
private static final File saveDir = new File("AutoBone Recordings");
private static final File loadDir = new File("Load AutoBone Recordings");
// This is filled by reloadConfigValues()
public final EnumMap<BoneType, Float> offsets = new EnumMap<>(
BoneType.class
);
public final FastList<BoneType> adjustOffsets = new FastList<>(
new BoneType[] {
BoneType.HEAD,
BoneType.NECK,
BoneType.CHEST,
BoneType.WAIST,
BoneType.HIP,
// This now works when using body proportion error! It's not the
// best still but it is somewhat functional
BoneType.LEFT_HIP,
BoneType.LEFT_UPPER_LEG,
BoneType.LEFT_LOWER_LEG,
}
);
public final FastList<BoneType> heightOffsets = new FastList<>(
new BoneType[] {
BoneType.NECK,
BoneType.CHEST,
BoneType.WAIST,
BoneType.HIP,
BoneType.LEFT_UPPER_LEG,
BoneType.RIGHT_UPPER_LEG,
BoneType.LEFT_LOWER_LEG,
BoneType.RIGHT_LOWER_LEG,
}
);
public final FastList<SkeletonConfigOffsets> legacyHeightConfigs = new FastList<>(
new SkeletonConfigOffsets[] {
SkeletonConfigOffsets.NECK,
SkeletonConfigOffsets.TORSO,
SkeletonConfigOffsets.LEGS_LENGTH,
}
);
public final EnumMap<SkeletonConfigOffsets, Float> legacyConfigs = new EnumMap<>(
SkeletonConfigOffsets.class
);
protected final VRServer server;
// #region Error functions
public SlideError slideError = new SlideError();
public OffsetSlideError offsetSlideError = new OffsetSlideError();
public FootHeightOffsetError footHeightOffsetError = new FootHeightOffsetError();
public BodyProportionError bodyProportionError = new BodyProportionError();
public HeightError heightError = new HeightError();
public PositionError positionError = new PositionError();
public PositionOffsetError positionOffsetError = new PositionOffsetError();
// #endregion
private final Random rand = new Random();
private final AutoBoneConfig config;
public AutoBone(VRServer server) {
this.config = server.getConfigManager().getVrConfig().getAutoBone();
this.server = server;
reloadConfigValues();
}
// Mean square error function
protected static float errorFunc(float errorDeriv) {
return 0.5f * (errorDeriv * errorDeriv);
}
public static File getLoadDir() {
return loadDir;
}
public float computeBoneOffset(BoneType bone, SkeletonConfig skeletonConfig) {
return switch (bone) {
case HEAD -> skeletonConfig.getOffset(SkeletonConfigOffsets.HEAD);
case NECK -> skeletonConfig.getOffset(SkeletonConfigOffsets.NECK);
case CHEST -> skeletonConfig.getOffset(SkeletonConfigOffsets.CHEST);
case WAIST -> -skeletonConfig.getOffset(SkeletonConfigOffsets.CHEST)
+ skeletonConfig.getOffset(SkeletonConfigOffsets.TORSO)
- skeletonConfig.getOffset(SkeletonConfigOffsets.WAIST);
case HIP -> skeletonConfig.getOffset(SkeletonConfigOffsets.WAIST);
case LEFT_HIP, RIGHT_HIP -> skeletonConfig.getOffset(SkeletonConfigOffsets.HIPS_WIDTH)
/ 2f;
case LEFT_UPPER_LEG, RIGHT_UPPER_LEG -> skeletonConfig
.getOffset(SkeletonConfigOffsets.LEGS_LENGTH)
- skeletonConfig.getOffset(SkeletonConfigOffsets.KNEE_HEIGHT);
case LEFT_LOWER_LEG, RIGHT_LOWER_LEG -> skeletonConfig
.getOffset(SkeletonConfigOffsets.KNEE_HEIGHT);
default -> -1f;
};
}
public void reloadConfigValues() {
reloadConfigValues(null);
}
public void reloadConfigValues(List<PoseFrameTracker> trackers) {
// Remove all previous values
offsets.clear();
// Get current or default skeleton configs
Skeleton skeleton = getSkeleton();
SkeletonConfig skeletonConfig = skeleton != null
? skeleton.getSkeletonConfig()
: new SkeletonConfig(false);
for (BoneType bone : adjustOffsets) {
float offset = computeBoneOffset(bone, skeletonConfig);
if (offset > 0f) {
offsets.put(bone, offset);
}
}
}
public Vector3f getBoneDirection(
HumanSkeleton skeleton,
BoneType node,
boolean rightSide,
Vector3f buffer
) {
if (buffer == null) {
buffer = new Vector3f();
}
switch (node) {
case LEFT_HIP, RIGHT_HIP -> node = rightSide ? BoneType.RIGHT_HIP : BoneType.LEFT_HIP;
case LEFT_UPPER_LEG, RIGHT_UPPER_LEG -> node = rightSide
? BoneType.RIGHT_UPPER_LEG
: BoneType.LEFT_UPPER_LEG;
case LEFT_LOWER_LEG, RIGHT_LOWER_LEG -> node = rightSide
? BoneType.RIGHT_LOWER_LEG
: BoneType.LEFT_LOWER_LEG;
}
TransformNode relevantTransform = skeleton.getTailNodeOfBone(node);
return relevantTransform.worldTransform
.getTranslation()
.subtract(relevantTransform.getParent().worldTransform.getTranslation(), buffer)
.normalizeLocal();
}
public float getDotProductDiff(
HumanSkeleton skeleton1,
HumanSkeleton skeleton2,
BoneType node,
boolean rightSide,
Vector3f offset
) {
Vector3f normalizedOffset = offset.normalize();
Vector3f boneRotation = new Vector3f();
getBoneDirection(skeleton1, node, rightSide, boneRotation);
float dot1 = normalizedOffset.dot(boneRotation);
getBoneDirection(skeleton2, node, rightSide, boneRotation);
float dot2 = normalizedOffset.dot(boneRotation);
return dot2 - dot1;
}
/**
* A simple utility method to get the {@link Skeleton} from the
* {@link VRServer}
*
* @return The {@link Skeleton} associated with the {@link VRServer}, or
* null if there is none available
* @see {@link VRServer}, {@link Skeleton}
*/
private Skeleton getSkeleton() {
HumanPoseProcessor humanPoseProcessor = server != null ? server.humanPoseProcessor : null;
return humanPoseProcessor != null ? humanPoseProcessor.getSkeleton() : null;
}
public void applyAndSaveConfig() {
if (!applyAndSaveConfig(getSkeleton())) {
// Unable to apply to skeleton, save directly
// saveConfigs();
}
}
public boolean applyConfig(
BiConsumer<SkeletonConfigOffsets, Float> configConsumer,
Map<BoneType, Float> offsets
) {
if (configConsumer == null || offsets == null) {
return false;
}
try {
Float headOffset = offsets.get(BoneType.HEAD);
if (headOffset != null) {
configConsumer.accept(SkeletonConfigOffsets.HEAD, headOffset);
}
Float neckOffset = offsets.get(BoneType.NECK);
if (neckOffset != null) {
configConsumer.accept(SkeletonConfigOffsets.NECK, neckOffset);
}
Float chestOffset = offsets.get(BoneType.CHEST);
Float hipOffset = offsets.get(BoneType.HIP);
Float waistOffset = offsets.get(BoneType.WAIST);
if (chestOffset != null && hipOffset != null && waistOffset != null) {
configConsumer
.accept(SkeletonConfigOffsets.TORSO, chestOffset + hipOffset + waistOffset);
}
if (chestOffset != null) {
configConsumer.accept(SkeletonConfigOffsets.CHEST, chestOffset);
}
if (hipOffset != null) {
configConsumer.accept(SkeletonConfigOffsets.WAIST, hipOffset);
}
Float hipWidthOffset = offsets.get(BoneType.LEFT_HIP);
if (hipWidthOffset == null) {
hipWidthOffset = offsets.get(BoneType.RIGHT_HIP);
}
if (hipWidthOffset != null) {
configConsumer
.accept(SkeletonConfigOffsets.HIPS_WIDTH, hipWidthOffset * 2f);
}
Float upperLegOffset = offsets.get(BoneType.LEFT_UPPER_LEG);
if (upperLegOffset == null) {
upperLegOffset = offsets.get(BoneType.RIGHT_UPPER_LEG);
}
Float lowerLegOffset = offsets.get(BoneType.LEFT_LOWER_LEG);
if (lowerLegOffset == null) {
lowerLegOffset = offsets.get(BoneType.RIGHT_LOWER_LEG);
}
if (upperLegOffset != null && lowerLegOffset != null) {
configConsumer
.accept(SkeletonConfigOffsets.LEGS_LENGTH, upperLegOffset + lowerLegOffset);
}
if (lowerLegOffset != null) {
configConsumer.accept(SkeletonConfigOffsets.KNEE_HEIGHT, lowerLegOffset);
}
return true;
} catch (Exception e) {
return false;
}
}
public boolean applyConfig(BiConsumer<SkeletonConfigOffsets, Float> configConsumer) {
return applyConfig(configConsumer, offsets);
}
public boolean applyConfig(
Map<SkeletonConfigOffsets, Float> skeletonConfig,
Map<BoneType, Float> offsets
) {
if (skeletonConfig == null) {
return false;
}
return applyConfig(skeletonConfig::put, offsets);
}
public boolean applyConfig(Map<SkeletonConfigOffsets, Float> skeletonConfig) {
return applyConfig(skeletonConfig, offsets);
}
public boolean applyConfig(SkeletonConfig skeletonConfig, Map<BoneType, Float> offsets) {
if (skeletonConfig == null) {
return false;
}
return applyConfig(skeletonConfig::setOffset, offsets);
}
public boolean applyConfig(SkeletonConfig skeletonConfig) {
return applyConfig(skeletonConfig, offsets);
}
public boolean applyAndSaveConfig(Skeleton skeleton) {
if (skeleton == null) {
return false;
}
SkeletonConfig skeletonConfig = skeleton.getSkeletonConfig();
if (!applyConfig(skeletonConfig))
return false;
skeletonConfig.save();
server.getConfigManager().saveConfig();
LogManager.info("[AutoBone] Configured skeleton bone lengths");
return true;
}
public Float getConfig(BoneType config) {
return offsets.get(config);
}
public <T> float sumSelectConfigs(
List<T> selection,
Function<T, Float> configs
) {
float sum = 0f;
for (T config : selection) {
Float length = configs.apply(config);
if (length != null) {
sum += length;
}
}
return sum;
}
public <T> float sumSelectConfigs(
List<T> selection,
Map<T, Float> configs
) {
return sumSelectConfigs(selection, configs::get);
}
public float sumSelectConfigs(
List<SkeletonConfigOffsets> selection,
SkeletonConfig config
) {
return sumSelectConfigs(selection, config::getOffset);
}
public float getLengthSum(Map<BoneType, Float> configs) {
return getLengthSum(configs, null);
}
public float getLengthSum(
Map<BoneType, Float> configs,
Map<BoneType, Float> configsAlt
) {
float length = 0f;
if (configsAlt != null) {
for (Entry<BoneType, Float> config : configsAlt.entrySet()) {
// If there isn't a duplicate config
if (!configs.containsKey(config.getKey())) {
length += config.getValue();
}
}
}
for (Float boneLength : configs.values()) {
length += boneLength;
}
return length;
}
public float getTargetHeight(PoseFrames frames) {
float targetHeight;
// Get the current skeleton from the server
Skeleton skeleton = getSkeleton();
if (skeleton != null) {
// If there is a skeleton available, calculate the target height
// from its configs
targetHeight = sumSelectConfigs(legacyHeightConfigs, skeleton.getSkeletonConfig());
LogManager
.warning(
"[AutoBone] Target height loaded from skeleton (Make sure you reset before running!): "
+ targetHeight
);
} else {
// Otherwise if there is no skeleton available, attempt to get the
// max HMD height from the recording
float hmdHeight = frames.getMaxHmdHeight();
if (hmdHeight <= 0.50f) {
LogManager
.warning(
"[AutoBone] Max headset height detected (Value seems too low, did you not stand up straight while measuring?): "
+ hmdHeight
);
} else {
LogManager.info("[AutoBone] Max headset height detected: " + hmdHeight);
}
// Estimate target height from HMD height
targetHeight = hmdHeight;
}
return targetHeight;
}
public AutoBoneResults processFrames(PoseFrames frames, Consumer<Epoch> epochCallback)
throws AutoBoneException {
return processFrames(frames, -1f, epochCallback);
}
public AutoBoneResults processFrames(
PoseFrames frames,
float targetHeight,
Consumer<Epoch> epochCallback
) throws AutoBoneException {
return processFrames(frames, true, targetHeight, epochCallback);
}
public AutoBoneResults processFrames(
PoseFrames frames,
boolean calcInitError,
float targetHeight,
Consumer<Epoch> epochCallback
) throws AutoBoneException {
final int frameCount = frames.getMaxFrameCount();
List<PoseFrameTracker> trackers = frames.getTrackers();
reloadConfigValues(trackers); // Reload configs and detect chest tracker
// from the first frame
final PoseFrameSkeleton skeleton1 = new PoseFrameSkeleton(
trackers,
null
);
final PoseFrameSkeleton skeleton2 = new PoseFrameSkeleton(
trackers,
null
);
EnumMap<BoneType, Float> intermediateOffsets = new EnumMap<>(
offsets
);
AutoBoneTrainingStep trainingStep = new AutoBoneTrainingStep(
targetHeight,
skeleton1,
skeleton2,
frames,
intermediateOffsets
);
skeleton1.setLegTweaksEnabled(false);
skeleton2.setLegTweaksEnabled(false);
// If target height isn't specified, auto-detect
if (targetHeight < 0f) {
targetHeight = getTargetHeight(frames);
}
StatsCalculator errorStats = new StatsCalculator();
// Epoch loop, each epoch is one full iteration over the full dataset
for (int epoch = calcInitError ? -1 : 0; epoch < this.config.numEpochs; epoch++) {
float adjustRate = epoch >= 0
? (this.config.initialAdjustRate
* FastMath.pow(this.config.adjustRateMultiplier, epoch))
: 0f;
int[] randomFrameIndices = null;
if (config.randomizeFrameOrder) {
randomFrameIndices = new int[frameCount];
int zeroPos = -1;
for (int i = 0; i < frameCount; i++) {
int index = rand.nextInt(frameCount);
if (i > 0) {
while (index == zeroPos || randomFrameIndices[index] > 0) {
index = rand.nextInt(frameCount);
}
} else {
zeroPos = index;
}
randomFrameIndices[index] = i;
}
}
// Iterate over the frames using a cursor and an offset for
// comparing frames a
// certain number of frames apart
for (
int cursorOffset = this.config.minDataDistance;
cursorOffset <= this.config.maxDataDistance
&& cursorOffset < frameCount;
cursorOffset++
) {
for (
int frameCursor = 0; frameCursor < frameCount - cursorOffset;
frameCursor += config.cursorIncrement
) {
int frameCursor2 = frameCursor + cursorOffset;
applyConfig(skeleton1.skeletonConfig);
skeleton2.skeletonConfig.setConfigs(skeleton1.skeletonConfig);
if (config.randomizeFrameOrder) {
trainingStep
.setCursors(
randomFrameIndices[frameCursor],
randomFrameIndices[frameCursor2]
);
} else {
trainingStep.setCursors(frameCursor, frameCursor2);
}
skeleton1.setCursor(trainingStep.getCursor1());
skeleton2.setCursor(trainingStep.getCursor2());
skeleton1.updatePose();
skeleton2.updatePose();
float totalLength = getLengthSum(offsets);
float curHeight = sumSelectConfigs(heightOffsets, offsets);
trainingStep.setCurrentHeight(curHeight);
float errorDeriv = getErrorDeriv(trainingStep);
float error = errorFunc(errorDeriv);
// In case of fire
if (Float.isNaN(error) || Float.isInfinite(error)) {
// Extinguish
LogManager
.warning(
"[AutoBone] Error value is invalid, resetting variables to recover"
);
reloadConfigValues(trackers);
// Reset error sum values
errorStats.reset();
// Continue on new data
continue;
}
// Store the error count for logging purposes
errorStats.addValue(errorDeriv);
float adjustVal = error * adjustRate;
// If there is no adjustment whatsoever, skip this
if (adjustVal == 0f) {
continue;
}
Vector3f slideLeft = skeleton2
.getComputedTracker(TrackerRole.LEFT_FOOT).position
.subtract(
skeleton1.getComputedTracker(TrackerRole.LEFT_FOOT).position
);
Vector3f slideRight = skeleton2
.getComputedTracker(TrackerRole.RIGHT_FOOT).position
.subtract(
skeleton1
.getComputedTracker(TrackerRole.RIGHT_FOOT).position
);
intermediateOffsets.putAll(offsets);
for (Entry<BoneType, Float> entry : offsets.entrySet()) {
// Skip adjustment if the epoch is before starting (for
// logging only)
if (epoch < 0) {
break;
}
float originalLength = entry.getValue();
boolean isHeightVar = heightOffsets.contains(entry.getKey());
float leftDotProduct = getDotProductDiff(
skeleton1,
skeleton2,
entry.getKey(),
false,
slideLeft
);
float rightDotProduct = getDotProductDiff(
skeleton1,
skeleton2,
entry.getKey(),
true,
slideRight
);
float dotLength = originalLength
* ((leftDotProduct + rightDotProduct) / 2f);
// Scale by the ratio for smooth adjustment and more
// stable results
float curAdjustVal = (adjustVal * -dotLength) / totalLength;
float newLength = originalLength + curAdjustVal;
// No small or negative numbers!!! Bad algorithm!
if (newLength < 0.01f) {
continue;
}
// Apply new offset length
intermediateOffsets.put(entry.getKey(), newLength);
applyConfig(skeleton1.skeletonConfig, intermediateOffsets);
skeleton2.skeletonConfig.setConfigs(skeleton1.skeletonConfig);
// Update the skeleton poses for the new offset length
skeleton1.updatePose();
skeleton2.updatePose();
float newHeight = isHeightVar ? curHeight + curAdjustVal : curHeight;
trainingStep.setCurrentHeight(newHeight);
float newErrorDeriv = getErrorDeriv(trainingStep);
if (newErrorDeriv < errorDeriv) {
entry.setValue(newLength);
}
// Reset the length to minimize bias in other variables,
// it's applied later
intermediateOffsets.put(entry.getKey(), originalLength);
applyConfig(skeleton1.skeletonConfig, intermediateOffsets);
skeleton2.skeletonConfig.setConfigs(skeleton1.skeletonConfig);
}
if (config.scaleEachStep) {
float stepHeight = sumSelectConfigs(heightOffsets, offsets);
if (stepHeight > 0f) {
float stepHeightDiff = targetHeight - stepHeight;
for (Entry<BoneType, Float> entry : offsets.entrySet()) {
// Only height variables
if (
entry.getKey() == BoneType.NECK
|| !heightOffsets.contains(entry.getKey())
)
continue;
float length = entry.getValue();
// Multiply the diff by the length to height
// ratio
float adjVal = stepHeightDiff * (length / stepHeight);
// Scale the length to fit the target height
entry.setValue(Math.max(length + (adjVal / 2f), 0.01f));
}
}
}
}
}
// Calculate average error over the epoch
if (
epoch <= 0
|| epoch >= (config.numEpochs - 1)
|| (epoch + 1) % config.printEveryNumEpochs == 0
) {
LogManager
.info(
"[AutoBone] Epoch "
+ (epoch + 1)
+ " average error: "
+ errorStats.getMean()
+ " (SD "
+ errorStats.getStandardDeviation()
+ ")"
);
}
applyConfig(legacyConfigs);
if (epochCallback != null) {
epochCallback
.accept(new Epoch(epoch + 1, this.config.numEpochs, errorStats, legacyConfigs));
}
}
float finalHeight = sumSelectConfigs(heightOffsets, offsets);
LogManager
.info(
"[AutoBone] Target height: "
+ targetHeight
+ " New height: "
+ finalHeight
);
return new AutoBoneResults(finalHeight, targetHeight, errorStats, legacyConfigs);
}
protected float getErrorDeriv(AutoBoneTrainingStep trainingStep) throws AutoBoneException {
float sumError = 0f;
if (this.config.slideErrorFactor > 0f) {
sumError += slideError.getStepError(trainingStep) * this.config.slideErrorFactor;
}
if (this.config.offsetSlideErrorFactor > 0f) {
sumError += offsetSlideError.getStepError(trainingStep)
* this.config.offsetSlideErrorFactor;
}
if (this.config.footHeightOffsetErrorFactor > 0f) {
sumError += footHeightOffsetError.getStepError(trainingStep)
* this.config.footHeightOffsetErrorFactor;
}
if (this.config.bodyProportionErrorFactor > 0f) {
sumError += bodyProportionError.getStepError(trainingStep)
* this.config.bodyProportionErrorFactor;
}
if (this.config.heightErrorFactor > 0f) {
sumError += heightError.getStepError(trainingStep) * this.config.heightErrorFactor;
}
if (this.config.positionErrorFactor > 0f) {
sumError += positionError.getStepError(trainingStep)
* this.config.positionErrorFactor;
}
if (this.config.positionOffsetErrorFactor > 0f) {
sumError += positionOffsetError.getStepError(trainingStep)
* this.config.positionOffsetErrorFactor;
}
return sumError;
}
public String getLengthsString() {
final StringBuilder configInfo = new StringBuilder();
this.offsets.forEach((key, value) -> {
if (configInfo.length() > 0) {
configInfo.append(", ");
}
configInfo
.append(key.toString())
.append(": ")
.append(StringUtils.prettyNumber(value * 100f, 2));
});
return configInfo.toString();
}
public void saveRecording(PoseFrames frames, File recordingFile) {
if (saveDir.isDirectory() || saveDir.mkdirs()) {
LogManager
.info("[AutoBone] Exporting frames to \"" + recordingFile.getPath() + "\"...");
if (PoseFrameIO.writeToFile(recordingFile, frames)) {
LogManager
.info(
"[AutoBone] Done exporting! Recording can be found at \""
+ recordingFile.getPath()
+ "\"."
);
} else {
LogManager
.severe(
"[AutoBone] Failed to export the recording to \""
+ recordingFile.getPath()
+ "\"."
);
}
} else {
LogManager
.severe(
"[AutoBone] Failed to create the recording directory \""
+ saveDir.getPath()
+ "\"."
);
}
}
public void saveRecording(PoseFrames frames, String recordingFileName) {
saveRecording(frames, new File(saveDir, recordingFileName));
}
public void saveRecording(PoseFrames frames) {
File recordingFile;
int recordingIndex = 1;
do {
recordingFile = new File(saveDir, "ABRecording" + recordingIndex++ + ".pfr");
} while (recordingFile.exists());
saveRecording(frames, recordingFile);
}
public List<Pair<String, PoseFrames>> loadRecordings() {
List<Pair<String, PoseFrames>> recordings = new FastList<>();
if (loadDir.isDirectory()) {
File[] files = loadDir.listFiles();
if (files != null) {
for (File file : files) {
if (
file.isFile()
&& org.apache.commons.lang3.StringUtils
.endsWithIgnoreCase(file.getName(), ".pfr")
) {
LogManager
.info(
"[AutoBone] Detected recording at \""
+ file.getPath()
+ "\", loading frames..."
);
PoseFrames frames = PoseFrameIO.readFromFile(file);
if (frames == null) {
LogManager
.severe("Reading frames from \"" + file.getPath() + "\" failed...");
} else {
recordings.add(Pair.of(file.getName(), frames));
}
}
}
}
}
return recordings;
}
public AutoBoneConfig getConfig() {
return config;
}
public class Epoch {
public final int epoch;
public final int totalEpochs;
public final StatsCalculator epochError;
public final EnumMap<SkeletonConfigOffsets, Float> configValues;
public Epoch(
int epoch,
int totalEpochs,
StatsCalculator epochError,
EnumMap<SkeletonConfigOffsets, Float> configValues
) {
this.epoch = epoch;
this.totalEpochs = totalEpochs;
this.epochError = epochError;
this.configValues = configValues;
}
@Override
public String toString() {
return "Epoch: " + epoch + ", Epoch Error: " + epochError;
}
}
public class AutoBoneResults {
public final float finalHeight;
public final float targetHeight;
public final StatsCalculator epochError;
public final EnumMap<SkeletonConfigOffsets, Float> configValues;
public AutoBoneResults(
float finalHeight,
float targetHeight,
StatsCalculator epochError,
EnumMap<SkeletonConfigOffsets, Float> configValues
) {
this.finalHeight = finalHeight;
this.targetHeight = targetHeight;
this.epochError = epochError;
this.configValues = configValues;
}
public float getHeightDifference() {
return FastMath.abs(targetHeight - finalHeight);
}
}
}

View File

@@ -0,0 +1,434 @@
package dev.slimevr.autobone;
import dev.slimevr.VRServer;
import dev.slimevr.autobone.AutoBone.AutoBoneResults;
import dev.slimevr.autobone.errors.AutoBoneException;
import dev.slimevr.poserecorder.PoseFrameTracker;
import dev.slimevr.poserecorder.PoseFrames;
import dev.slimevr.poserecorder.PoseRecorder;
import dev.slimevr.poserecorder.TrackerFrame;
import dev.slimevr.poserecorder.TrackerFrameData;
import dev.slimevr.vr.processor.skeleton.SkeletonConfig;
import dev.slimevr.vr.processor.skeleton.SkeletonConfigOffsets;
import io.eiren.util.StringUtils;
import io.eiren.util.logging.LogManager;
import org.apache.commons.lang3.tuple.Pair;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Future;
import java.util.concurrent.locks.ReentrantLock;
public class AutoBoneHandler {
private final VRServer server;
private final PoseRecorder poseRecorder;
private final AutoBone autoBone;
private ReentrantLock recordingLock = new ReentrantLock();
private Thread recordingThread = null;
private ReentrantLock saveRecordingLock = new ReentrantLock();
private Thread saveRecordingThread = null;
private ReentrantLock autoBoneLock = new ReentrantLock();
private Thread autoBoneThread = null;
private List<AutoBoneListener> listeners = new CopyOnWriteArrayList<>();
public AutoBoneHandler(VRServer server) {
this.server = server;
this.poseRecorder = new PoseRecorder(server);
this.autoBone = new AutoBone(server);
}
public void addListener(AutoBoneListener listener) {
this.listeners.add(listener);
}
public void removeListener(AutoBoneListener listener) {
this.listeners.removeIf(l -> listener == l);
}
private void announceProcessStatus(
AutoBoneProcessType processType,
String message,
long current,
long total,
boolean completed,
boolean success
) {
listeners
.forEach(
listener -> listener
.onAutoBoneProcessStatus(
processType,
message,
current,
total,
completed,
success
)
);
}
private void announceProcessStatus(
AutoBoneProcessType processType,
String message,
boolean completed,
boolean success
) {
announceProcessStatus(processType, message, 0, 0, completed, success);
}
private void announceProcessStatus(AutoBoneProcessType processType, String message) {
announceProcessStatus(processType, message, false, true);
}
private void announceProcessStatus(AutoBoneProcessType processType, long current, long total) {
announceProcessStatus(processType, null, current, total, false, true);
}
public String getLengthsString() {
return autoBone.getLengthsString();
}
private AutoBoneResults processFrames(PoseFrames frames) throws AutoBoneException {
return autoBone
.processFrames(
frames,
autoBone.getConfig().calcInitError,
autoBone.getConfig().targetHeight,
(epoch) -> listeners.forEach(listener -> listener.onAutoBoneEpoch(epoch))
);
}
public boolean startProcessByType(AutoBoneProcessType processType) {
switch (processType) {
case RECORD:
startRecording();
break;
case SAVE:
saveRecording();
break;
case PROCESS:
processRecording();
break;
case APPLY:
applyValues();
break;
default:
return false;
}
return true;
}
public void startRecording() {
recordingLock.lock();
try {
// Prevent running multiple times
if (recordingThread != null) {
return;
}
Thread thread = new Thread(this::startRecordingThread);
recordingThread = thread;
thread.start();
} finally {
recordingLock.unlock();
}
}
private void startRecordingThread() {
try {
if (poseRecorder.isReadyToRecord()) {
announceProcessStatus(AutoBoneProcessType.RECORD, "Recording...");
// 1000 samples at 20 ms per sample is 20 seconds
int sampleCount = this.autoBone.getConfig().sampleCount;
long sampleRate = this.autoBone.getConfig().sampleRateMs;
Future<PoseFrames> framesFuture = poseRecorder
.startFrameRecording(
sampleCount,
sampleRate,
progress -> announceProcessStatus(
AutoBoneProcessType.RECORD,
progress.frame,
progress.totalFrames
)
);
PoseFrames frames = framesFuture.get();
LogManager.info("[AutoBone] Done recording!");
// Save a recurring recording for users to send as debug info
announceProcessStatus(AutoBoneProcessType.RECORD, "Saving recording...");
autoBone.saveRecording(frames, "LastABRecording.pfr");
if (this.autoBone.getConfig().saveRecordings) {
announceProcessStatus(
AutoBoneProcessType.RECORD,
"Saving recording (from config option)..."
);
autoBone.saveRecording(frames);
}
listeners.forEach(listener -> listener.onAutoBoneRecordingEnd(frames));
announceProcessStatus(AutoBoneProcessType.RECORD, "Done recording!", true, true);
} else {
announceProcessStatus(
AutoBoneProcessType.RECORD,
"The server is not ready to record",
true,
false
);
LogManager.severe("[AutoBone] Unable to record...");
return;
}
} catch (Exception e) {
announceProcessStatus(
AutoBoneProcessType.RECORD,
String.format("Recording failed: %s", e.getMessage()),
true,
false
);
LogManager.severe("[AutoBone] Failed recording!", e);
} finally {
recordingThread = null;
}
}
public void saveRecording() {
saveRecordingLock.lock();
try {
// Prevent running multiple times
if (saveRecordingThread != null) {
return;
}
Thread thread = new Thread(this::saveRecordingThread);
saveRecordingThread = thread;
thread.start();
} finally {
saveRecordingLock.unlock();
}
}
private void saveRecordingThread() {
try {
Future<PoseFrames> framesFuture = poseRecorder.getFramesAsync();
if (framesFuture != null) {
announceProcessStatus(AutoBoneProcessType.SAVE, "Waiting for recording...");
PoseFrames frames = framesFuture.get();
if (frames.getTrackerCount() <= 0) {
throw new IllegalStateException("Recording has no trackers");
}
if (frames.getMaxFrameCount() <= 0) {
throw new IllegalStateException("Recording has no frames");
}
announceProcessStatus(AutoBoneProcessType.SAVE, "Saving recording...");
autoBone.saveRecording(frames);
announceProcessStatus(AutoBoneProcessType.SAVE, "Recording saved!", true, true);
} else {
announceProcessStatus(AutoBoneProcessType.SAVE, "No recording found", true, false);
LogManager.severe("[AutoBone] Unable to save, no recording was done...");
return;
}
} catch (Exception e) {
announceProcessStatus(
AutoBoneProcessType.SAVE,
String.format("Failed to save recording: %s", e.getMessage()),
true,
false
);
LogManager.severe("[AutoBone] Failed to save recording!", e);
} finally {
saveRecordingThread = null;
}
}
public void processRecording() {
autoBoneLock.lock();
try {
// Prevent running multiple times
if (autoBoneThread != null) {
return;
}
Thread thread = new Thread(this::processRecordingThread);
autoBoneThread = thread;
thread.start();
} finally {
autoBoneLock.unlock();
}
}
private void processRecordingThread() {
try {
announceProcessStatus(AutoBoneProcessType.PROCESS, "Loading recordings...");
List<Pair<String, PoseFrames>> frameRecordings = autoBone.loadRecordings();
if (!frameRecordings.isEmpty()) {
LogManager.info("[AutoBone] Done loading frames!");
} else {
Future<PoseFrames> framesFuture = poseRecorder.getFramesAsync();
if (framesFuture != null) {
announceProcessStatus(AutoBoneProcessType.PROCESS, "Waiting for recording...");
PoseFrames frames = framesFuture.get();
if (frames.getTrackerCount() <= 0) {
throw new IllegalStateException("Recording has no trackers");
}
if (frames.getMaxFrameCount() <= 0) {
throw new IllegalStateException("Recording has no frames");
}
frameRecordings.add(Pair.of("<Recording>", frames));
} else {
announceProcessStatus(
AutoBoneProcessType.PROCESS,
"No recordings found...",
true,
false
);
LogManager
.severe(
"[AutoBone] No recordings found in \""
+ AutoBone.getLoadDir().getPath()
+ "\" and no recording was done..."
);
return;
}
}
announceProcessStatus(AutoBoneProcessType.PROCESS, "Processing recording(s)...");
LogManager.info("[AutoBone] Processing frames...");
StatsCalculator errorStats = new StatsCalculator();
SkeletonConfig skeletonConfigBuffer = new SkeletonConfig(false);
for (Pair<String, PoseFrames> recording : frameRecordings) {
LogManager
.info("[AutoBone] Processing frames from \"" + recording.getKey() + "\"...");
List<PoseFrameTracker> trackers = recording.getValue().getTrackers();
StringBuilder trackerInfo = new StringBuilder();
for (PoseFrameTracker tracker : trackers) {
if (tracker == null)
continue;
TrackerFrame frame = tracker.safeGetFrame(0);
if (frame == null || !frame.hasData(TrackerFrameData.DESIGNATION))
continue;
if (trackerInfo.length() > 0) {
trackerInfo.append(", ");
}
trackerInfo.append(frame.designation.designation);
if (frame.hasData(TrackerFrameData.POSITION)) {
trackerInfo.append(" (P)");
}
}
LogManager
.info(
"[AutoBone] ("
+ trackers.size()
+ " trackers) ["
+ trackerInfo
+ "]"
);
AutoBoneResults autoBoneResults = processFrames(recording.getValue());
errorStats.addValue(autoBoneResults.getHeightDifference());
LogManager.info("[AutoBone] Done processing!");
// #region Stats/Values
skeletonConfigBuffer.setConfigs(autoBoneResults.configValues, null, null);
float neckLength = skeletonConfigBuffer.getOffset(SkeletonConfigOffsets.NECK);
float chestDistance = skeletonConfigBuffer
.getOffset(SkeletonConfigOffsets.CHEST);
float torsoLength = skeletonConfigBuffer
.getOffset(SkeletonConfigOffsets.TORSO);
float hipWidth = skeletonConfigBuffer
.getOffset(SkeletonConfigOffsets.HIPS_WIDTH);
float legsLength = skeletonConfigBuffer
.getOffset(SkeletonConfigOffsets.LEGS_LENGTH);
float kneeHeight = skeletonConfigBuffer
.getOffset(SkeletonConfigOffsets.KNEE_HEIGHT);
float neckTorso = neckLength / torsoLength;
float chestTorso = chestDistance / torsoLength;
float torsoWaist = hipWidth / torsoLength;
float legTorso = legsLength / torsoLength;
float legBody = legsLength / (torsoLength + neckLength);
float kneeLeg = kneeHeight / legsLength;
LogManager
.info(
"[AutoBone] Ratios: [{Neck-Torso: "
+ StringUtils.prettyNumber(neckTorso)
+ "}, {Chest-Torso: "
+ StringUtils.prettyNumber(chestTorso)
+ "}, {Torso-Waist: "
+ StringUtils.prettyNumber(torsoWaist)
+ "}, {Leg-Torso: "
+ StringUtils.prettyNumber(legTorso)
+ "}, {Leg-Body: "
+ StringUtils.prettyNumber(legBody)
+ "}, {Knee-Leg: "
+ StringUtils.prettyNumber(kneeLeg)
+ "}]"
);
LogManager.info("[AutoBone] Length values: " + autoBone.getLengthsString());
}
LogManager
.info(
"[AutoBone] Average height error: "
+ StringUtils.prettyNumber(errorStats.getMean(), 6)
+ " (SD "
+ StringUtils.prettyNumber(errorStats.getStandardDeviation(), 6)
+ ")"
);
// #endregion
listeners.forEach(listener -> listener.onAutoBoneEnd(autoBone.legacyConfigs));
announceProcessStatus(AutoBoneProcessType.PROCESS, "Done processing!", true, true);
} catch (Exception e) {
announceProcessStatus(
AutoBoneProcessType.PROCESS,
String.format("Processing failed: %s", e.getMessage()),
true,
false
);
LogManager.severe("[AutoBone] Failed adjustment!", e);
} finally {
autoBoneThread = null;
}
}
public void applyValues() {
autoBone.applyAndSaveConfig();
announceProcessStatus(AutoBoneProcessType.APPLY, "Adjusted values applied!", true, true);
// TODO Update GUI values after applying? Is that needed here?
}
}

View File

@@ -0,0 +1,26 @@
package dev.slimevr.autobone;
import java.util.EnumMap;
import dev.slimevr.autobone.AutoBone.Epoch;
import dev.slimevr.poserecorder.PoseFrames;
import dev.slimevr.vr.processor.skeleton.SkeletonConfigOffsets;
public interface AutoBoneListener {
void onAutoBoneProcessStatus(
AutoBoneProcessType processType,
String message,
long current,
long total,
boolean completed,
boolean success
);
void onAutoBoneRecordingEnd(PoseFrames recording);
void onAutoBoneEpoch(Epoch epoch);
void onAutoBoneEnd(EnumMap<SkeletonConfigOffsets, Float> configValues);
}

View File

@@ -0,0 +1,31 @@
package dev.slimevr.autobone;
import java.util.HashMap;
import java.util.Map;
public enum AutoBoneProcessType {
NONE(0),
RECORD(1),
SAVE(2),
PROCESS(3),
APPLY(4);
public final int id;
private static final Map<Integer, AutoBoneProcessType> byId = new HashMap<>();
AutoBoneProcessType(int id) {
this.id = id;
}
public static AutoBoneProcessType getById(int id) {
return byId.get(id);
}
static {
for (AutoBoneProcessType abpt : values()) {
byId.put(abpt.id, abpt);
}
}
}

View File

@@ -0,0 +1,108 @@
package dev.slimevr.autobone;
import java.util.Map;
import dev.slimevr.poserecorder.PoseFrameSkeleton;
import dev.slimevr.poserecorder.PoseFrames;
import dev.slimevr.vr.processor.skeleton.BoneType;
public class AutoBoneTrainingStep {
private int cursor1 = 0;
private int cursor2 = 0;
private float currentHeight;
private final float targetHeight;
private final PoseFrameSkeleton skeleton1;
private final PoseFrameSkeleton skeleton2;
private final PoseFrames trainingFrames;
private final Map<BoneType, Float> intermediateOffsets;
public AutoBoneTrainingStep(
int cursor1,
int cursor2,
float targetHeight,
PoseFrameSkeleton skeleton1,
PoseFrameSkeleton skeleton2,
PoseFrames trainingFrames,
Map<BoneType, Float> intermediateOffsets
) {
this.cursor1 = cursor1;
this.cursor2 = cursor2;
this.targetHeight = targetHeight;
this.skeleton1 = skeleton1;
this.skeleton2 = skeleton2;
this.trainingFrames = trainingFrames;
this.intermediateOffsets = intermediateOffsets;
}
public AutoBoneTrainingStep(
float targetHeight,
PoseFrameSkeleton skeleton1,
PoseFrameSkeleton skeleton2,
PoseFrames trainingFrames,
Map<BoneType, Float> intermediateOffsets
) {
this.targetHeight = targetHeight;
this.skeleton1 = skeleton1;
this.skeleton2 = skeleton2;
this.trainingFrames = trainingFrames;
this.intermediateOffsets = intermediateOffsets;
}
public int getCursor1() {
return cursor1;
}
public void setCursor1(int cursor1) {
this.cursor1 = cursor1;
}
public int getCursor2() {
return cursor2;
}
public void setCursor2(int cursor2) {
this.cursor2 = cursor2;
}
public void setCursors(int cursor1, int cursor2) {
this.cursor1 = cursor1;
this.cursor2 = cursor2;
}
public float getCurrentHeight() {
return currentHeight;
}
public void setCurrentHeight(float currentHeight) {
this.currentHeight = currentHeight;
}
public float getTargetHeight() {
return targetHeight;
}
public PoseFrameSkeleton getSkeleton1() {
return skeleton1;
}
public PoseFrameSkeleton getSkeleton2() {
return skeleton2;
}
public PoseFrames getTrainingFrames() {
return trainingFrames;
}
public Map<BoneType, Float> getIntermediateOffsets() {
return intermediateOffsets;
}
public float getHeightOffset() {
return getTargetHeight() - getCurrentHeight();
}
}

View File

@@ -0,0 +1,51 @@
package dev.slimevr.autobone;
import com.jme3.math.FastMath;
/// This is a stat calculator based on Welford's online algorithm
/// https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Welford's_online_algorithm
public class StatsCalculator {
private int count = 0;
private float mean = 0f;
private float M2 = 0f;
public void reset() {
count = 0;
mean = 0f;
M2 = 0f;
}
public void addValue(float newValue) {
count += 1;
float delta = newValue - mean;
mean += delta / count;
float delta2 = newValue - mean;
M2 += delta * delta2;
}
public float getMean() {
return mean;
}
public float getVariance() {
if (count < 1) {
return Float.NaN;
}
return M2 / count;
}
public float getSampleVariance() {
if (count < 2) {
return Float.NaN;
}
return M2 / (count - 1);
}
public float getStandardDeviation() {
return FastMath.sqrt(getVariance());
}
}

View File

@@ -0,0 +1,28 @@
package dev.slimevr.autobone.errors;
public class AutoBoneException extends Exception {
public AutoBoneException() {
}
public AutoBoneException(String message) {
super(message);
}
public AutoBoneException(Throwable cause) {
super(cause);
}
public AutoBoneException(String message, Throwable cause) {
super(message, cause);
}
public AutoBoneException(
String message,
Throwable cause,
boolean enableSuppression,
boolean writableStackTrace
) {
super(message, cause, enableSuppression, writableStackTrace);
}
}

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