Compare commits
21 Commits
b76d533739
...
v0.0.34-SN
| Author | SHA1 | Date | |
|---|---|---|---|
| d500c130ed | |||
| b4d390cd9b | |||
| 97ea610f34 | |||
| e2e1f24e3e | |||
| a09b956b48 | |||
| c92a4517b3 | |||
| 1260a38bb7 | |||
| 20772da813 | |||
| 8898d6e8ee | |||
| cb0ee9c842 | |||
| 3cf7871591 | |||
| c7e1eb11ed | |||
| 9fc58b05b6 | |||
| 8de3c41b9b | |||
| 15e47ceaa8 | |||
| 9f28786ab6 | |||
| 18b5c50016 | |||
| 03945e029a | |||
| 90e043e5f8 | |||
| 559459aef6 | |||
| 8d0bd2bc99 |
@@ -6,6 +6,14 @@ und diese Projekt folgt [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
## Version 0.0.34
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- `Faker`-Klasse zur Erzeugung von Fake-Daten hinzugefügt.
|
||||||
|
- Dokumentation unter [zeichenmaschine.xyz](https://zeichenmaschine.xyz) mit
|
||||||
|
[MkDocs](https://www.mkdocs.org) und [Material for MkDocs](https://squidfunk.github.io/mkdocs-material/).
|
||||||
|
- Neue `image` methoden im `DrawingLayer`.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- `FilledShape` und `StrokedShape` durch `Fillable` und `Strokeable` Interfaces ersetzt.
|
- `FilledShape` und `StrokedShape` durch `Fillable` und `Strokeable` Interfaces ersetzt.
|
||||||
- `Shape` erweitert nun `BasisDrawable` als abstrakte Grundlage.
|
- `Shape` erweitert nun `BasisDrawable` als abstrakte Grundlage.
|
||||||
|
|||||||
@@ -4,7 +4,12 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
group 'schule.ngb'
|
group 'schule.ngb'
|
||||||
version '0.0.32-SNAPSHOT'
|
version '0.0.34-SNAPSHOT'
|
||||||
|
|
||||||
|
java {
|
||||||
|
withSourcesJar()
|
||||||
|
withJavadocJar()
|
||||||
|
}
|
||||||
|
|
||||||
compileJava {
|
compileJava {
|
||||||
options.release = 11
|
options.release = 11
|
||||||
@@ -39,4 +44,5 @@ tasks.register('jarMP3SPI', Jar) {
|
|||||||
from {
|
from {
|
||||||
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
|
configurations.runtimeClasspath.collect { it.isDirectory() ? it : zipTree(it) }
|
||||||
}
|
}
|
||||||
|
with jar
|
||||||
}
|
}
|
||||||
|
|||||||
634
docs/assets/quickstart/AblaufMoleActive.excalidraw
Normal file
@@ -0,0 +1,634 @@
|
|||||||
|
{
|
||||||
|
"type": "excalidraw",
|
||||||
|
"version": 2,
|
||||||
|
"source": "https://excalidraw.com",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"type": "rectangle",
|
||||||
|
"version": 152,
|
||||||
|
"versionNonce": 1288225375,
|
||||||
|
"isDeleted": false,
|
||||||
|
"id": "fxk8rHocjpTteICJMa6n8",
|
||||||
|
"fillStyle": "hachure",
|
||||||
|
"strokeWidth": 1,
|
||||||
|
"strokeStyle": "solid",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"angle": 0,
|
||||||
|
"x": 619.9963325816416,
|
||||||
|
"y": 186.9758066195945,
|
||||||
|
"strokeColor": "#000000",
|
||||||
|
"backgroundColor": "#fab005",
|
||||||
|
"width": 150,
|
||||||
|
"height": 46,
|
||||||
|
"seed": 1339263918,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "sharp",
|
||||||
|
"boundElements": [
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"id": "JmEBjNProPAgJQZUvMGGa"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "eryKwAzIMcBMQP0Ybl1Mm",
|
||||||
|
"type": "arrow"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"updated": 1670307372550,
|
||||||
|
"link": null,
|
||||||
|
"locked": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "rectangle",
|
||||||
|
"version": 264,
|
||||||
|
"versionNonce": 1267158257,
|
||||||
|
"isDeleted": false,
|
||||||
|
"id": "wZLPkORf755_2Vp0J6I0V",
|
||||||
|
"fillStyle": "hachure",
|
||||||
|
"strokeWidth": 1,
|
||||||
|
"strokeStyle": "solid",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"angle": 0,
|
||||||
|
"x": 566.9963325816416,
|
||||||
|
"y": 289.9758066195945,
|
||||||
|
"strokeColor": "#000000",
|
||||||
|
"backgroundColor": "#fab005",
|
||||||
|
"width": 248,
|
||||||
|
"height": 50,
|
||||||
|
"seed": 359801906,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "sharp",
|
||||||
|
"boundElements": [
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"id": "9TujIdwDvtinylO3z50y6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "eryKwAzIMcBMQP0Ybl1Mm",
|
||||||
|
"type": "arrow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Zc9GOJ8DsIQYo4WaGvvHy",
|
||||||
|
"type": "arrow"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"updated": 1670307379131,
|
||||||
|
"link": null,
|
||||||
|
"locked": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "rectangle",
|
||||||
|
"version": 376,
|
||||||
|
"versionNonce": 1807861489,
|
||||||
|
"isDeleted": false,
|
||||||
|
"id": "290mWFx31fA5smc5FqPUr",
|
||||||
|
"fillStyle": "hachure",
|
||||||
|
"strokeWidth": 1,
|
||||||
|
"strokeStyle": "solid",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"angle": 0,
|
||||||
|
"x": 607.9963325816416,
|
||||||
|
"y": 392.9758066195945,
|
||||||
|
"strokeColor": "#000000",
|
||||||
|
"backgroundColor": "#228be6",
|
||||||
|
"width": 143,
|
||||||
|
"height": 50,
|
||||||
|
"seed": 1948766318,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "sharp",
|
||||||
|
"boundElements": [
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"id": "_ISR-LCZm2Hu2G57R_uxN"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Zc9GOJ8DsIQYo4WaGvvHy",
|
||||||
|
"type": "arrow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "hUUqTCXva-vZjnBeM-PR3",
|
||||||
|
"type": "arrow"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"updated": 1670307888001,
|
||||||
|
"link": null,
|
||||||
|
"locked": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "rectangle",
|
||||||
|
"version": 518,
|
||||||
|
"versionNonce": 2136415391,
|
||||||
|
"isDeleted": false,
|
||||||
|
"id": "kbEG_cCZadugfCPxYedhf",
|
||||||
|
"fillStyle": "hachure",
|
||||||
|
"strokeWidth": 1,
|
||||||
|
"strokeStyle": "solid",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"angle": 0,
|
||||||
|
"x": 614.9963325816416,
|
||||||
|
"y": 589.9758066195944,
|
||||||
|
"strokeColor": "#000000",
|
||||||
|
"backgroundColor": "#228be6",
|
||||||
|
"width": 131,
|
||||||
|
"height": 50,
|
||||||
|
"seed": 97599794,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "sharp",
|
||||||
|
"boundElements": [
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"id": "6KjVrNy_dxGXJtntdqrjY"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "hUUqTCXva-vZjnBeM-PR3",
|
||||||
|
"type": "arrow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "bplSSGA4kyy-Av7nKNK1B",
|
||||||
|
"type": "arrow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "qukSk_W6enSdwERPEPkhZ",
|
||||||
|
"type": "arrow"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"updated": 1670307888001,
|
||||||
|
"link": null,
|
||||||
|
"locked": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"version": 110,
|
||||||
|
"versionNonce": 1350901746,
|
||||||
|
"isDeleted": false,
|
||||||
|
"id": "JmEBjNProPAgJQZUvMGGa",
|
||||||
|
"fillStyle": "hachure",
|
||||||
|
"strokeWidth": 1,
|
||||||
|
"strokeStyle": "solid",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"angle": 0,
|
||||||
|
"x": 643.9963325816416,
|
||||||
|
"y": 199.9758066195945,
|
||||||
|
"strokeColor": "#000000",
|
||||||
|
"backgroundColor": "transparent",
|
||||||
|
"width": 102,
|
||||||
|
"height": 20,
|
||||||
|
"seed": 1889147762,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "sharp",
|
||||||
|
"boundElements": [],
|
||||||
|
"updated": 1670164406970,
|
||||||
|
"link": null,
|
||||||
|
"locked": false,
|
||||||
|
"fontSize": 16,
|
||||||
|
"fontFamily": 1,
|
||||||
|
"text": "new Shapes()",
|
||||||
|
"baseline": 14,
|
||||||
|
"textAlign": "center",
|
||||||
|
"verticalAlign": "middle",
|
||||||
|
"containerId": "fxk8rHocjpTteICJMa6n8",
|
||||||
|
"originalText": "new Shapes()"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"version": 245,
|
||||||
|
"versionNonce": 1536101230,
|
||||||
|
"isDeleted": false,
|
||||||
|
"id": "9TujIdwDvtinylO3z50y6",
|
||||||
|
"fillStyle": "hachure",
|
||||||
|
"strokeWidth": 1,
|
||||||
|
"strokeStyle": "solid",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"angle": 0,
|
||||||
|
"x": 584.9963325816416,
|
||||||
|
"y": 304.9758066195945,
|
||||||
|
"strokeColor": "#000000",
|
||||||
|
"backgroundColor": "transparent",
|
||||||
|
"width": 212,
|
||||||
|
"height": 20,
|
||||||
|
"seed": 1297543150,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "sharp",
|
||||||
|
"boundElements": [],
|
||||||
|
"updated": 1670164433291,
|
||||||
|
"link": null,
|
||||||
|
"locked": false,
|
||||||
|
"fontSize": 16,
|
||||||
|
"fontFamily": 1,
|
||||||
|
"text": "super(800, 800, \"Shapes\")",
|
||||||
|
"baseline": 14,
|
||||||
|
"textAlign": "center",
|
||||||
|
"verticalAlign": "middle",
|
||||||
|
"containerId": "wZLPkORf755_2Vp0J6I0V",
|
||||||
|
"originalText": "super(800, 800, \"Shapes\")"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"version": 362,
|
||||||
|
"versionNonce": 2069822514,
|
||||||
|
"isDeleted": false,
|
||||||
|
"id": "_ISR-LCZm2Hu2G57R_uxN",
|
||||||
|
"fillStyle": "hachure",
|
||||||
|
"strokeWidth": 1,
|
||||||
|
"strokeStyle": "solid",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"angle": 0,
|
||||||
|
"x": 650.4963325816416,
|
||||||
|
"y": 407.9758066195945,
|
||||||
|
"strokeColor": "#000000",
|
||||||
|
"backgroundColor": "transparent",
|
||||||
|
"width": 58,
|
||||||
|
"height": 20,
|
||||||
|
"seed": 525219186,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "sharp",
|
||||||
|
"boundElements": [],
|
||||||
|
"updated": 1670164509204,
|
||||||
|
"link": null,
|
||||||
|
"locked": false,
|
||||||
|
"fontSize": 16,
|
||||||
|
"fontFamily": 1,
|
||||||
|
"text": "setup()",
|
||||||
|
"baseline": 14,
|
||||||
|
"textAlign": "center",
|
||||||
|
"verticalAlign": "middle",
|
||||||
|
"containerId": "290mWFx31fA5smc5FqPUr",
|
||||||
|
"originalText": "setup()"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"version": 502,
|
||||||
|
"versionNonce": 747611665,
|
||||||
|
"isDeleted": false,
|
||||||
|
"id": "6KjVrNy_dxGXJtntdqrjY",
|
||||||
|
"fillStyle": "hachure",
|
||||||
|
"strokeWidth": 1,
|
||||||
|
"strokeStyle": "solid",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"angle": 0,
|
||||||
|
"x": 655.4963325816416,
|
||||||
|
"y": 604.9758066195944,
|
||||||
|
"strokeColor": "#000000",
|
||||||
|
"backgroundColor": "transparent",
|
||||||
|
"width": 50,
|
||||||
|
"height": 20,
|
||||||
|
"seed": 1808016110,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "sharp",
|
||||||
|
"boundElements": [],
|
||||||
|
"updated": 1670307245844,
|
||||||
|
"link": null,
|
||||||
|
"locked": false,
|
||||||
|
"fontSize": 16,
|
||||||
|
"fontFamily": 1,
|
||||||
|
"text": "draw()",
|
||||||
|
"baseline": 14,
|
||||||
|
"textAlign": "center",
|
||||||
|
"verticalAlign": "middle",
|
||||||
|
"containerId": "kbEG_cCZadugfCPxYedhf",
|
||||||
|
"originalText": "draw()"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "arrow",
|
||||||
|
"version": 31,
|
||||||
|
"versionNonce": 101778865,
|
||||||
|
"isDeleted": false,
|
||||||
|
"id": "eryKwAzIMcBMQP0Ybl1Mm",
|
||||||
|
"fillStyle": "hachure",
|
||||||
|
"strokeWidth": 1,
|
||||||
|
"strokeStyle": "solid",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"angle": 0,
|
||||||
|
"x": 694.9963325816416,
|
||||||
|
"y": 242.9758066195945,
|
||||||
|
"strokeColor": "#000000",
|
||||||
|
"backgroundColor": "#12b886",
|
||||||
|
"width": 0,
|
||||||
|
"height": 38,
|
||||||
|
"seed": 1130444978,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "round",
|
||||||
|
"boundElements": [],
|
||||||
|
"updated": 1670307364549,
|
||||||
|
"link": null,
|
||||||
|
"locked": false,
|
||||||
|
"startBinding": {
|
||||||
|
"elementId": "fxk8rHocjpTteICJMa6n8",
|
||||||
|
"focus": 0,
|
||||||
|
"gap": 10
|
||||||
|
},
|
||||||
|
"endBinding": {
|
||||||
|
"elementId": "wZLPkORf755_2Vp0J6I0V",
|
||||||
|
"focus": 0.03225806451612903,
|
||||||
|
"gap": 9
|
||||||
|
},
|
||||||
|
"lastCommittedPoint": null,
|
||||||
|
"startArrowhead": null,
|
||||||
|
"endArrowhead": "arrow",
|
||||||
|
"points": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
38
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "arrow",
|
||||||
|
"version": 160,
|
||||||
|
"versionNonce": 1672489439,
|
||||||
|
"isDeleted": false,
|
||||||
|
"id": "Zc9GOJ8DsIQYo4WaGvvHy",
|
||||||
|
"fillStyle": "hachure",
|
||||||
|
"strokeWidth": 1,
|
||||||
|
"strokeStyle": "solid",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"angle": 0,
|
||||||
|
"x": 686.7253597961584,
|
||||||
|
"y": 347.9758066195945,
|
||||||
|
"strokeColor": "#000000",
|
||||||
|
"backgroundColor": "#12b886",
|
||||||
|
"width": 0.3256260532492661,
|
||||||
|
"height": 36,
|
||||||
|
"seed": 1138446962,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "round",
|
||||||
|
"boundElements": [],
|
||||||
|
"updated": 1670307364549,
|
||||||
|
"link": null,
|
||||||
|
"locked": false,
|
||||||
|
"startBinding": {
|
||||||
|
"elementId": "wZLPkORf755_2Vp0J6I0V",
|
||||||
|
"gap": 8,
|
||||||
|
"focus": 0.03595554587056003
|
||||||
|
},
|
||||||
|
"endBinding": {
|
||||||
|
"elementId": "290mWFx31fA5smc5FqPUr",
|
||||||
|
"gap": 9,
|
||||||
|
"focus": 0.09195903246894747
|
||||||
|
},
|
||||||
|
"lastCommittedPoint": null,
|
||||||
|
"startArrowhead": null,
|
||||||
|
"endArrowhead": "arrow",
|
||||||
|
"points": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
-0.3256260532492661,
|
||||||
|
36
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "arrow",
|
||||||
|
"version": 616,
|
||||||
|
"versionNonce": 447594705,
|
||||||
|
"isDeleted": false,
|
||||||
|
"id": "hUUqTCXva-vZjnBeM-PR3",
|
||||||
|
"fillStyle": "hachure",
|
||||||
|
"strokeWidth": 1,
|
||||||
|
"strokeStyle": "solid",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"angle": 0,
|
||||||
|
"x": 684.6524492354905,
|
||||||
|
"y": 452.97580661959455,
|
||||||
|
"strokeColor": "#000000",
|
||||||
|
"backgroundColor": "#228be6",
|
||||||
|
"width": 1.5815106831711319,
|
||||||
|
"height": 29.000000000000114,
|
||||||
|
"seed": 1169930546,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "round",
|
||||||
|
"boundElements": [],
|
||||||
|
"updated": 1670307888001,
|
||||||
|
"link": null,
|
||||||
|
"locked": false,
|
||||||
|
"startBinding": {
|
||||||
|
"elementId": "290mWFx31fA5smc5FqPUr",
|
||||||
|
"focus": -0.044553538542618336,
|
||||||
|
"gap": 10
|
||||||
|
},
|
||||||
|
"endBinding": {
|
||||||
|
"elementId": "5cG3FjQlYuIGPZMsIMgJU",
|
||||||
|
"focus": 0.08947713014192164,
|
||||||
|
"gap": 6.999999999999915
|
||||||
|
},
|
||||||
|
"lastCommittedPoint": null,
|
||||||
|
"startArrowhead": null,
|
||||||
|
"endArrowhead": "arrow",
|
||||||
|
"points": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1.5815106831711319,
|
||||||
|
29.000000000000114
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "rectangle",
|
||||||
|
"version": 560,
|
||||||
|
"versionNonce": 11022527,
|
||||||
|
"isDeleted": false,
|
||||||
|
"id": "5cG3FjQlYuIGPZMsIMgJU",
|
||||||
|
"fillStyle": "hachure",
|
||||||
|
"strokeWidth": 1,
|
||||||
|
"strokeStyle": "solid",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"angle": 0,
|
||||||
|
"x": 616.4963325816416,
|
||||||
|
"y": 488.9758066195945,
|
||||||
|
"strokeColor": "#000000",
|
||||||
|
"backgroundColor": "#228be6",
|
||||||
|
"width": 131,
|
||||||
|
"height": 50,
|
||||||
|
"seed": 609895569,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "sharp",
|
||||||
|
"boundElements": [
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"id": "-Fl205ZvyGaxHAHt7C3r3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "hUUqTCXva-vZjnBeM-PR3",
|
||||||
|
"type": "arrow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "bplSSGA4kyy-Av7nKNK1B",
|
||||||
|
"type": "arrow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "qukSk_W6enSdwERPEPkhZ",
|
||||||
|
"type": "arrow"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"updated": 1670307888001,
|
||||||
|
"link": null,
|
||||||
|
"locked": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "text",
|
||||||
|
"version": 551,
|
||||||
|
"versionNonce": 685087263,
|
||||||
|
"isDeleted": false,
|
||||||
|
"id": "-Fl205ZvyGaxHAHt7C3r3",
|
||||||
|
"fillStyle": "hachure",
|
||||||
|
"strokeWidth": 1,
|
||||||
|
"strokeStyle": "solid",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"angle": 0,
|
||||||
|
"x": 647.4963325816416,
|
||||||
|
"y": 503.97580661959455,
|
||||||
|
"strokeColor": "#000000",
|
||||||
|
"backgroundColor": "transparent",
|
||||||
|
"width": 69,
|
||||||
|
"height": 20,
|
||||||
|
"seed": 415680767,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "sharp",
|
||||||
|
"boundElements": null,
|
||||||
|
"updated": 1670307260196,
|
||||||
|
"link": null,
|
||||||
|
"locked": false,
|
||||||
|
"fontSize": 16,
|
||||||
|
"fontFamily": 1,
|
||||||
|
"text": "update()",
|
||||||
|
"baseline": 14,
|
||||||
|
"textAlign": "center",
|
||||||
|
"verticalAlign": "middle",
|
||||||
|
"containerId": "5cG3FjQlYuIGPZMsIMgJU",
|
||||||
|
"originalText": "update()"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "bplSSGA4kyy-Av7nKNK1B",
|
||||||
|
"type": "arrow",
|
||||||
|
"x": 683.9963325816416,
|
||||||
|
"y": 546.9758066195944,
|
||||||
|
"width": 1,
|
||||||
|
"height": 34,
|
||||||
|
"angle": 0,
|
||||||
|
"strokeColor": "#000000",
|
||||||
|
"backgroundColor": "#228be6",
|
||||||
|
"fillStyle": "hachure",
|
||||||
|
"strokeWidth": 1,
|
||||||
|
"strokeStyle": "solid",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "sharp",
|
||||||
|
"seed": 1937189809,
|
||||||
|
"version": 33,
|
||||||
|
"versionNonce": 1639939761,
|
||||||
|
"isDeleted": false,
|
||||||
|
"boundElements": null,
|
||||||
|
"updated": 1670307888001,
|
||||||
|
"link": null,
|
||||||
|
"locked": false,
|
||||||
|
"points": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
-1,
|
||||||
|
34
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"lastCommittedPoint": null,
|
||||||
|
"startBinding": {
|
||||||
|
"elementId": "5cG3FjQlYuIGPZMsIMgJU",
|
||||||
|
"focus": -0.044849023090586096,
|
||||||
|
"gap": 7.999999999999858
|
||||||
|
},
|
||||||
|
"endBinding": {
|
||||||
|
"elementId": "kbEG_cCZadugfCPxYedhf",
|
||||||
|
"focus": 0.022646536412078155,
|
||||||
|
"gap": 9
|
||||||
|
},
|
||||||
|
"startArrowhead": null,
|
||||||
|
"endArrowhead": "arrow"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "qukSk_W6enSdwERPEPkhZ",
|
||||||
|
"type": "arrow",
|
||||||
|
"x": 758.9963325816416,
|
||||||
|
"y": 615.9758066195944,
|
||||||
|
"width": 87,
|
||||||
|
"height": 107.99999999999994,
|
||||||
|
"angle": 0,
|
||||||
|
"strokeColor": "#000000",
|
||||||
|
"backgroundColor": "transparent",
|
||||||
|
"fillStyle": "hachure",
|
||||||
|
"strokeWidth": 1,
|
||||||
|
"strokeStyle": "solid",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "sharp",
|
||||||
|
"seed": 677436529,
|
||||||
|
"version": 170,
|
||||||
|
"versionNonce": 377207327,
|
||||||
|
"isDeleted": false,
|
||||||
|
"boundElements": [],
|
||||||
|
"updated": 1670307315516,
|
||||||
|
"link": null,
|
||||||
|
"locked": false,
|
||||||
|
"points": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
86,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
83,
|
||||||
|
-106.99999999999994
|
||||||
|
],
|
||||||
|
[
|
||||||
|
-1,
|
||||||
|
-107.99999999999994
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"lastCommittedPoint": null,
|
||||||
|
"startBinding": {
|
||||||
|
"elementId": "kbEG_cCZadugfCPxYedhf",
|
||||||
|
"focus": 0.04,
|
||||||
|
"gap": 13
|
||||||
|
},
|
||||||
|
"endBinding": {
|
||||||
|
"elementId": "5cG3FjQlYuIGPZMsIMgJU",
|
||||||
|
"focus": -0.26783652736088875,
|
||||||
|
"gap": 10.5
|
||||||
|
},
|
||||||
|
"startArrowhead": null,
|
||||||
|
"endArrowhead": "arrow"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"appState": {
|
||||||
|
"gridSize": null,
|
||||||
|
"viewBackgroundColor": "#ffffff"
|
||||||
|
},
|
||||||
|
"files": {}
|
||||||
|
}
|
||||||
BIN
docs/assets/quickstart/AblaufMoleActive.png
Normal file
|
After Width: | Height: | Size: 258 KiB |
1030
docs/assets/quickstart/AblaufMoleStatic.excalidraw
Normal file
BIN
docs/assets/quickstart/AblaufMoleStatic.png
Normal file
|
After Width: | Height: | Size: 411 KiB |
375
docs/assets/quickstart/CircleMouseCollision.excalidraw
Normal file
@@ -0,0 +1,375 @@
|
|||||||
|
{
|
||||||
|
"type": "excalidraw",
|
||||||
|
"version": 2,
|
||||||
|
"source": "https://excalidraw.com",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"id": "Mhp-wQ2wZxCI4BYWvTvV2",
|
||||||
|
"type": "ellipse",
|
||||||
|
"x": 420,
|
||||||
|
"y": 233,
|
||||||
|
"width": 385.99999999999994,
|
||||||
|
"height": 385.99999999999994,
|
||||||
|
"angle": 0,
|
||||||
|
"strokeColor": "#000000",
|
||||||
|
"backgroundColor": "#ffdf22",
|
||||||
|
"fillStyle": "cross-hatch",
|
||||||
|
"strokeWidth": 1,
|
||||||
|
"strokeStyle": "solid",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "sharp",
|
||||||
|
"seed": 474299186,
|
||||||
|
"version": 149,
|
||||||
|
"versionNonce": 2072559726,
|
||||||
|
"isDeleted": false,
|
||||||
|
"boundElements": null,
|
||||||
|
"updated": 1670158627399,
|
||||||
|
"link": null,
|
||||||
|
"locked": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "SqZRA75ACKHo0lB799wpS",
|
||||||
|
"type": "line",
|
||||||
|
"x": 605,
|
||||||
|
"y": 426,
|
||||||
|
"width": 173.02018127597637,
|
||||||
|
"height": 99.89324823492274,
|
||||||
|
"angle": 0,
|
||||||
|
"strokeColor": "#087f5b",
|
||||||
|
"backgroundColor": "#ffdf22",
|
||||||
|
"fillStyle": "cross-hatch",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"strokeStyle": "dashed",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "round",
|
||||||
|
"seed": 484805038,
|
||||||
|
"version": 286,
|
||||||
|
"versionNonce": 1603007154,
|
||||||
|
"isDeleted": false,
|
||||||
|
"boundElements": null,
|
||||||
|
"updated": 1670159044301,
|
||||||
|
"link": null,
|
||||||
|
"locked": false,
|
||||||
|
"points": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
173.02018127597637,
|
||||||
|
-99.89324823492274
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"lastCommittedPoint": null,
|
||||||
|
"startBinding": null,
|
||||||
|
"endBinding": null,
|
||||||
|
"startArrowhead": null,
|
||||||
|
"endArrowhead": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "7XVC5Wqiy62pN9Vc92bgl",
|
||||||
|
"type": "text",
|
||||||
|
"x": 645.7358370304399,
|
||||||
|
"y": 356.14958623820286,
|
||||||
|
"width": 86,
|
||||||
|
"height": 20,
|
||||||
|
"angle": 5.766085793504818,
|
||||||
|
"strokeColor": "#087f5b",
|
||||||
|
"backgroundColor": "#ffdf22",
|
||||||
|
"fillStyle": "cross-hatch",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"strokeStyle": "solid",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "sharp",
|
||||||
|
"seed": 1883808110,
|
||||||
|
"version": 228,
|
||||||
|
"versionNonce": 627525998,
|
||||||
|
"isDeleted": false,
|
||||||
|
"boundElements": null,
|
||||||
|
"updated": 1670159044301,
|
||||||
|
"link": null,
|
||||||
|
"locked": false,
|
||||||
|
"text": "moleRadius",
|
||||||
|
"fontSize": 16,
|
||||||
|
"fontFamily": 1,
|
||||||
|
"textAlign": "left",
|
||||||
|
"verticalAlign": "top",
|
||||||
|
"baseline": 14,
|
||||||
|
"containerId": null,
|
||||||
|
"originalText": "moleRadius"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "zp9JMFpfOA6ZEWCCiqwW0",
|
||||||
|
"type": "line",
|
||||||
|
"x": 606,
|
||||||
|
"y": 427,
|
||||||
|
"width": 128.012747010704,
|
||||||
|
"height": 208.02230726873202,
|
||||||
|
"angle": 0,
|
||||||
|
"strokeColor": "#000000",
|
||||||
|
"backgroundColor": "#fa5252",
|
||||||
|
"fillStyle": "solid",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"strokeStyle": "dashed",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "round",
|
||||||
|
"seed": 224372978,
|
||||||
|
"version": 81,
|
||||||
|
"versionNonce": 563971630,
|
||||||
|
"isDeleted": false,
|
||||||
|
"boundElements": null,
|
||||||
|
"updated": 1670158772553,
|
||||||
|
"link": null,
|
||||||
|
"locked": false,
|
||||||
|
"points": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
-128.012747010704,
|
||||||
|
-208.02230726873202
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"lastCommittedPoint": null,
|
||||||
|
"startBinding": null,
|
||||||
|
"endBinding": null,
|
||||||
|
"startArrowhead": null,
|
||||||
|
"endArrowhead": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "LIDFlKBUhkbZdzTUFT1qp",
|
||||||
|
"type": "line",
|
||||||
|
"x": 604.8240237554426,
|
||||||
|
"y": 425.10629955596494,
|
||||||
|
"width": 127.71248741178238,
|
||||||
|
"height": 4.628286654564533,
|
||||||
|
"angle": 0,
|
||||||
|
"strokeColor": "#000000",
|
||||||
|
"backgroundColor": "#fa5252",
|
||||||
|
"fillStyle": "solid",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"strokeStyle": "dashed",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "round",
|
||||||
|
"seed": 34525358,
|
||||||
|
"version": 124,
|
||||||
|
"versionNonce": 1131380590,
|
||||||
|
"isDeleted": false,
|
||||||
|
"boundElements": null,
|
||||||
|
"updated": 1670158787752,
|
||||||
|
"link": null,
|
||||||
|
"locked": false,
|
||||||
|
"points": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
-127.71248741178238,
|
||||||
|
-4.628286654564533
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"lastCommittedPoint": null,
|
||||||
|
"startBinding": null,
|
||||||
|
"endBinding": null,
|
||||||
|
"startArrowhead": null,
|
||||||
|
"endArrowhead": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "-Of3RfHAvJDq0qHIwjm2U",
|
||||||
|
"type": "ellipse",
|
||||||
|
"x": 471.96813247323996,
|
||||||
|
"y": 413.60866976111646,
|
||||||
|
"width": 12,
|
||||||
|
"height": 12,
|
||||||
|
"angle": 0,
|
||||||
|
"strokeColor": "#000000",
|
||||||
|
"backgroundColor": "#4c6ef5",
|
||||||
|
"fillStyle": "solid",
|
||||||
|
"strokeWidth": 1,
|
||||||
|
"strokeStyle": "solid",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "sharp",
|
||||||
|
"seed": 1050702514,
|
||||||
|
"version": 384,
|
||||||
|
"versionNonce": 1957432558,
|
||||||
|
"isDeleted": false,
|
||||||
|
"boundElements": null,
|
||||||
|
"updated": 1670158970885,
|
||||||
|
"link": null,
|
||||||
|
"locked": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "NB4WGe-lT7XqLrsKX8jN8",
|
||||||
|
"type": "ellipse",
|
||||||
|
"x": 472,
|
||||||
|
"y": 212,
|
||||||
|
"width": 12,
|
||||||
|
"height": 12,
|
||||||
|
"angle": 0,
|
||||||
|
"strokeColor": "#000000",
|
||||||
|
"backgroundColor": "#fa5252",
|
||||||
|
"fillStyle": "solid",
|
||||||
|
"strokeWidth": 1,
|
||||||
|
"strokeStyle": "solid",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "sharp",
|
||||||
|
"seed": 2059751730,
|
||||||
|
"version": 277,
|
||||||
|
"versionNonce": 756441010,
|
||||||
|
"isDeleted": false,
|
||||||
|
"boundElements": null,
|
||||||
|
"updated": 1670158769939,
|
||||||
|
"link": null,
|
||||||
|
"locked": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "T1vEWEHV-1FM4A7Q7dTzy",
|
||||||
|
"type": "ellipse",
|
||||||
|
"x": 601.6117120882632,
|
||||||
|
"y": 422.28007605476614,
|
||||||
|
"width": 6.436664554034337,
|
||||||
|
"height": 6.436664554034337,
|
||||||
|
"angle": 0,
|
||||||
|
"strokeColor": "#000000",
|
||||||
|
"backgroundColor": "#000000",
|
||||||
|
"fillStyle": "solid",
|
||||||
|
"strokeWidth": 1,
|
||||||
|
"strokeStyle": "solid",
|
||||||
|
"roughness": 0,
|
||||||
|
"opacity": 100,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "sharp",
|
||||||
|
"seed": 1983845742,
|
||||||
|
"version": 376,
|
||||||
|
"versionNonce": 1202556466,
|
||||||
|
"isDeleted": false,
|
||||||
|
"boundElements": null,
|
||||||
|
"updated": 1670158984089,
|
||||||
|
"link": null,
|
||||||
|
"locked": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "eEMIGJ2Ag5HqWZLPmjXMM",
|
||||||
|
"type": "text",
|
||||||
|
"x": 551.4695946462369,
|
||||||
|
"y": 432.8553279772536,
|
||||||
|
"width": 108,
|
||||||
|
"height": 20,
|
||||||
|
"angle": 0,
|
||||||
|
"strokeColor": "#000000",
|
||||||
|
"backgroundColor": "#000000",
|
||||||
|
"fillStyle": "solid",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"strokeStyle": "dashed",
|
||||||
|
"roughness": 0,
|
||||||
|
"opacity": 100,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "sharp",
|
||||||
|
"seed": 817794290,
|
||||||
|
"version": 97,
|
||||||
|
"versionNonce": 1438641266,
|
||||||
|
"isDeleted": false,
|
||||||
|
"boundElements": null,
|
||||||
|
"updated": 1670158980419,
|
||||||
|
"link": null,
|
||||||
|
"locked": false,
|
||||||
|
"text": "(moleX, moleY)",
|
||||||
|
"fontSize": 16,
|
||||||
|
"fontFamily": 1,
|
||||||
|
"textAlign": "left",
|
||||||
|
"verticalAlign": "top",
|
||||||
|
"baseline": 14,
|
||||||
|
"containerId": null,
|
||||||
|
"originalText": "(moleX, moleY)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6cMmrU3lcJbub8sEKnVWe",
|
||||||
|
"type": "text",
|
||||||
|
"x": 409.80838527586974,
|
||||||
|
"y": 186.48331271464983,
|
||||||
|
"width": 135,
|
||||||
|
"height": 20,
|
||||||
|
"angle": 0,
|
||||||
|
"strokeColor": "#c92a2a",
|
||||||
|
"backgroundColor": "#000000",
|
||||||
|
"fillStyle": "solid",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"strokeStyle": "dashed",
|
||||||
|
"roughness": 0,
|
||||||
|
"opacity": 100,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "sharp",
|
||||||
|
"seed": 399694958,
|
||||||
|
"version": 166,
|
||||||
|
"versionNonce": 1571185842,
|
||||||
|
"isDeleted": false,
|
||||||
|
"boundElements": null,
|
||||||
|
"updated": 1670158939920,
|
||||||
|
"link": null,
|
||||||
|
"locked": false,
|
||||||
|
"text": "(mouseX, mouseY)",
|
||||||
|
"fontSize": 16,
|
||||||
|
"fontFamily": 1,
|
||||||
|
"textAlign": "left",
|
||||||
|
"verticalAlign": "top",
|
||||||
|
"baseline": 14,
|
||||||
|
"containerId": null,
|
||||||
|
"originalText": "(mouseX, mouseY)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "0idmv_zLtEYyctHB4lSWV",
|
||||||
|
"type": "text",
|
||||||
|
"x": 406.7568976895149,
|
||||||
|
"y": 390.3911282833501,
|
||||||
|
"width": 135,
|
||||||
|
"height": 20,
|
||||||
|
"angle": 0,
|
||||||
|
"strokeColor": "#364fc7",
|
||||||
|
"backgroundColor": "#000000",
|
||||||
|
"fillStyle": "solid",
|
||||||
|
"strokeWidth": 2,
|
||||||
|
"strokeStyle": "dashed",
|
||||||
|
"roughness": 0,
|
||||||
|
"opacity": 100,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "sharp",
|
||||||
|
"seed": 1008210350,
|
||||||
|
"version": 224,
|
||||||
|
"versionNonce": 968376110,
|
||||||
|
"isDeleted": false,
|
||||||
|
"boundElements": null,
|
||||||
|
"updated": 1670158975919,
|
||||||
|
"link": null,
|
||||||
|
"locked": false,
|
||||||
|
"text": "(mouseX, mouseY)",
|
||||||
|
"fontSize": 16,
|
||||||
|
"fontFamily": 1,
|
||||||
|
"textAlign": "left",
|
||||||
|
"verticalAlign": "top",
|
||||||
|
"baseline": 14,
|
||||||
|
"containerId": null,
|
||||||
|
"originalText": "(mouseX, mouseY)"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"appState": {
|
||||||
|
"gridSize": null,
|
||||||
|
"viewBackgroundColor": "#ffffff"
|
||||||
|
},
|
||||||
|
"files": {}
|
||||||
|
}
|
||||||
BIN
docs/assets/quickstart/CircleMouseCollision.png
Normal file
|
After Width: | Height: | Size: 821 KiB |
375
docs/assets/quickstart/Layers.excalidraw
Normal file
@@ -0,0 +1,375 @@
|
|||||||
|
{
|
||||||
|
"type": "excalidraw",
|
||||||
|
"version": 2,
|
||||||
|
"source": "https://excalidraw.com",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"id": "HPGP8tbAPy0aGI3_MCE3D",
|
||||||
|
"type": "diamond",
|
||||||
|
"x": 491.42779164474496,
|
||||||
|
"y": 479.56091158550765,
|
||||||
|
"width": 432.03244941244884,
|
||||||
|
"height": 55.325188818463005,
|
||||||
|
"angle": 0,
|
||||||
|
"strokeColor": "#000000",
|
||||||
|
"backgroundColor": "#868e96",
|
||||||
|
"fillStyle": "hachure",
|
||||||
|
"strokeWidth": 1,
|
||||||
|
"strokeStyle": "solid",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "round",
|
||||||
|
"seed": 1103578030,
|
||||||
|
"version": 138,
|
||||||
|
"versionNonce": 1150893294,
|
||||||
|
"isDeleted": false,
|
||||||
|
"boundElements": null,
|
||||||
|
"updated": 1670163691142,
|
||||||
|
"link": null,
|
||||||
|
"locked": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "vhIFJN3-91oJXV-RfFsBf",
|
||||||
|
"type": "diamond",
|
||||||
|
"x": 491.42779164474496,
|
||||||
|
"y": 435.8942449188411,
|
||||||
|
"width": 432.03244941244884,
|
||||||
|
"height": 55.325188818463005,
|
||||||
|
"angle": 0,
|
||||||
|
"strokeColor": "#000000",
|
||||||
|
"backgroundColor": "#fab005",
|
||||||
|
"fillStyle": "hachure",
|
||||||
|
"strokeWidth": 1,
|
||||||
|
"strokeStyle": "solid",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "round",
|
||||||
|
"seed": 618886834,
|
||||||
|
"version": 198,
|
||||||
|
"versionNonce": 1515704562,
|
||||||
|
"isDeleted": false,
|
||||||
|
"boundElements": null,
|
||||||
|
"updated": 1670163691142,
|
||||||
|
"link": null,
|
||||||
|
"locked": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "B2pM-4m_3Dk2w_6dY-hVJ",
|
||||||
|
"type": "diamond",
|
||||||
|
"x": 491.42779164474496,
|
||||||
|
"y": 392.2275782521744,
|
||||||
|
"width": 432.03244941244884,
|
||||||
|
"height": 55.325188818463005,
|
||||||
|
"angle": 0,
|
||||||
|
"strokeColor": "#000000",
|
||||||
|
"backgroundColor": "#228be6",
|
||||||
|
"fillStyle": "hachure",
|
||||||
|
"strokeWidth": 1,
|
||||||
|
"strokeStyle": "solid",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "round",
|
||||||
|
"seed": 844345582,
|
||||||
|
"version": 231,
|
||||||
|
"versionNonce": 1954508590,
|
||||||
|
"isDeleted": false,
|
||||||
|
"boundElements": null,
|
||||||
|
"updated": 1670163691142,
|
||||||
|
"link": null,
|
||||||
|
"locked": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Ge28XC9PqD26hknNaFXKP",
|
||||||
|
"type": "diamond",
|
||||||
|
"x": 491.42779164474496,
|
||||||
|
"y": 348.5609115855077,
|
||||||
|
"width": 432.03244941244884,
|
||||||
|
"height": 55.325188818463005,
|
||||||
|
"angle": 0,
|
||||||
|
"strokeColor": "#000000",
|
||||||
|
"backgroundColor": "#40c057",
|
||||||
|
"fillStyle": "hachure",
|
||||||
|
"strokeWidth": 1,
|
||||||
|
"strokeStyle": "solid",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "round",
|
||||||
|
"seed": 1326657902,
|
||||||
|
"version": 249,
|
||||||
|
"versionNonce": 685040306,
|
||||||
|
"isDeleted": false,
|
||||||
|
"boundElements": null,
|
||||||
|
"updated": 1670163691142,
|
||||||
|
"link": null,
|
||||||
|
"locked": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "PLU80s2TkyMEAOVgolwJx",
|
||||||
|
"type": "line",
|
||||||
|
"x": 506.9963325816415,
|
||||||
|
"y": 507.9758066195945,
|
||||||
|
"width": 0,
|
||||||
|
"height": 132.00000000000006,
|
||||||
|
"angle": 0,
|
||||||
|
"strokeColor": "#000000",
|
||||||
|
"backgroundColor": "#40c057",
|
||||||
|
"fillStyle": "hachure",
|
||||||
|
"strokeWidth": 1,
|
||||||
|
"strokeStyle": "dotted",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "round",
|
||||||
|
"seed": 1246804466,
|
||||||
|
"version": 39,
|
||||||
|
"versionNonce": 627399022,
|
||||||
|
"isDeleted": false,
|
||||||
|
"boundElements": null,
|
||||||
|
"updated": 1670163691142,
|
||||||
|
"link": null,
|
||||||
|
"locked": false,
|
||||||
|
"points": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
-132.00000000000006
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"lastCommittedPoint": null,
|
||||||
|
"startBinding": null,
|
||||||
|
"endBinding": null,
|
||||||
|
"startArrowhead": null,
|
||||||
|
"endArrowhead": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "xDdJ5FnbMLIbN95FE3Iyc",
|
||||||
|
"type": "line",
|
||||||
|
"x": 910.9963325816416,
|
||||||
|
"y": 506.9758066195945,
|
||||||
|
"width": 0,
|
||||||
|
"height": 132,
|
||||||
|
"angle": 0,
|
||||||
|
"strokeColor": "#000000",
|
||||||
|
"backgroundColor": "#40c057",
|
||||||
|
"fillStyle": "hachure",
|
||||||
|
"strokeWidth": 1,
|
||||||
|
"strokeStyle": "dotted",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "round",
|
||||||
|
"seed": 1034568050,
|
||||||
|
"version": 77,
|
||||||
|
"versionNonce": 1363786866,
|
||||||
|
"isDeleted": false,
|
||||||
|
"boundElements": null,
|
||||||
|
"updated": 1670163691142,
|
||||||
|
"link": null,
|
||||||
|
"locked": false,
|
||||||
|
"points": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
-132
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"lastCommittedPoint": null,
|
||||||
|
"startBinding": null,
|
||||||
|
"endBinding": null,
|
||||||
|
"startArrowhead": null,
|
||||||
|
"endArrowhead": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "clYu5Q9GzKbAfohBEsGwn",
|
||||||
|
"type": "line",
|
||||||
|
"x": 709.9963325816416,
|
||||||
|
"y": 532.9758066195944,
|
||||||
|
"width": 0,
|
||||||
|
"height": 129.99999999999994,
|
||||||
|
"angle": 0,
|
||||||
|
"strokeColor": "#000000",
|
||||||
|
"backgroundColor": "#40c057",
|
||||||
|
"fillStyle": "hachure",
|
||||||
|
"strokeWidth": 1,
|
||||||
|
"strokeStyle": "dotted",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "round",
|
||||||
|
"seed": 1798035246,
|
||||||
|
"version": 42,
|
||||||
|
"versionNonce": 244579246,
|
||||||
|
"isDeleted": false,
|
||||||
|
"boundElements": null,
|
||||||
|
"updated": 1670163691142,
|
||||||
|
"link": null,
|
||||||
|
"locked": false,
|
||||||
|
"points": [
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
0
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0,
|
||||||
|
-129.99999999999994
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"lastCommittedPoint": null,
|
||||||
|
"startBinding": null,
|
||||||
|
"endBinding": null,
|
||||||
|
"startArrowhead": null,
|
||||||
|
"endArrowhead": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "v2nD7_bvWJYHqs82XDWO2",
|
||||||
|
"type": "text",
|
||||||
|
"x": 935.9963325816416,
|
||||||
|
"y": 497.2235059947391,
|
||||||
|
"width": 159,
|
||||||
|
"height": 20,
|
||||||
|
"angle": 0,
|
||||||
|
"strokeColor": "#343a40",
|
||||||
|
"backgroundColor": "#40c057",
|
||||||
|
"fillStyle": "hachure",
|
||||||
|
"strokeWidth": 1,
|
||||||
|
"strokeStyle": "dotted",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "round",
|
||||||
|
"seed": 1323260978,
|
||||||
|
"version": 111,
|
||||||
|
"versionNonce": 372528622,
|
||||||
|
"isDeleted": false,
|
||||||
|
"boundElements": null,
|
||||||
|
"updated": 1670163691142,
|
||||||
|
"link": null,
|
||||||
|
"locked": false,
|
||||||
|
"text": "Ebene 0: ColorLayer",
|
||||||
|
"fontSize": 16,
|
||||||
|
"fontFamily": 1,
|
||||||
|
"textAlign": "left",
|
||||||
|
"verticalAlign": "top",
|
||||||
|
"baseline": 14,
|
||||||
|
"containerId": null,
|
||||||
|
"originalText": "Ebene 0: ColorLayer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "c6T2j8hozL3RZcNYGMgRl",
|
||||||
|
"type": "text",
|
||||||
|
"x": 935.9963325816416,
|
||||||
|
"y": 453.55683932807256,
|
||||||
|
"width": 172,
|
||||||
|
"height": 20,
|
||||||
|
"angle": 0,
|
||||||
|
"strokeColor": "#e67700",
|
||||||
|
"backgroundColor": "#40c057",
|
||||||
|
"fillStyle": "hachure",
|
||||||
|
"strokeWidth": 1,
|
||||||
|
"strokeStyle": "dotted",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "round",
|
||||||
|
"seed": 1949746930,
|
||||||
|
"version": 111,
|
||||||
|
"versionNonce": 1448527858,
|
||||||
|
"isDeleted": false,
|
||||||
|
"boundElements": null,
|
||||||
|
"updated": 1670163691142,
|
||||||
|
"link": null,
|
||||||
|
"locked": false,
|
||||||
|
"text": "Ebene 1: DrawingLayer",
|
||||||
|
"fontSize": 16,
|
||||||
|
"fontFamily": 1,
|
||||||
|
"textAlign": "left",
|
||||||
|
"verticalAlign": "top",
|
||||||
|
"baseline": 14,
|
||||||
|
"containerId": null,
|
||||||
|
"originalText": "Ebene 1: DrawingLayer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "bAmGMg2i4Hvv7t_obXSKn",
|
||||||
|
"type": "text",
|
||||||
|
"x": 935.9963325816416,
|
||||||
|
"y": 409.8901726614059,
|
||||||
|
"width": 174,
|
||||||
|
"height": 20,
|
||||||
|
"angle": 0,
|
||||||
|
"strokeColor": "#1864ab",
|
||||||
|
"backgroundColor": "#40c057",
|
||||||
|
"fillStyle": "hachure",
|
||||||
|
"strokeWidth": 1,
|
||||||
|
"strokeStyle": "dotted",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "round",
|
||||||
|
"seed": 706529710,
|
||||||
|
"version": 144,
|
||||||
|
"versionNonce": 1968138286,
|
||||||
|
"isDeleted": false,
|
||||||
|
"boundElements": null,
|
||||||
|
"updated": 1670163691142,
|
||||||
|
"link": null,
|
||||||
|
"locked": false,
|
||||||
|
"text": "Ebene 2: ShapesLayer",
|
||||||
|
"fontSize": 16,
|
||||||
|
"fontFamily": 1,
|
||||||
|
"textAlign": "left",
|
||||||
|
"verticalAlign": "top",
|
||||||
|
"baseline": 14,
|
||||||
|
"containerId": null,
|
||||||
|
"originalText": "Ebene 2: ShapesLayer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "9-QieQt-nVqGCe5D4r15j",
|
||||||
|
"type": "text",
|
||||||
|
"x": 935.9963325816416,
|
||||||
|
"y": 366.2235059947392,
|
||||||
|
"width": 65,
|
||||||
|
"height": 20,
|
||||||
|
"angle": 0,
|
||||||
|
"strokeColor": "#2b8a3e",
|
||||||
|
"backgroundColor": "#40c057",
|
||||||
|
"fillStyle": "hachure",
|
||||||
|
"strokeWidth": 1,
|
||||||
|
"strokeStyle": "dotted",
|
||||||
|
"roughness": 1,
|
||||||
|
"opacity": 100,
|
||||||
|
"groupIds": [],
|
||||||
|
"strokeSharpness": "round",
|
||||||
|
"seed": 2100742126,
|
||||||
|
"version": 179,
|
||||||
|
"versionNonce": 289074606,
|
||||||
|
"isDeleted": false,
|
||||||
|
"boundElements": null,
|
||||||
|
"updated": 1670163697684,
|
||||||
|
"link": null,
|
||||||
|
"locked": false,
|
||||||
|
"text": "Ebene 3",
|
||||||
|
"fontSize": 16,
|
||||||
|
"fontFamily": 1,
|
||||||
|
"textAlign": "left",
|
||||||
|
"verticalAlign": "top",
|
||||||
|
"baseline": 14,
|
||||||
|
"containerId": null,
|
||||||
|
"originalText": "Ebene 3"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"appState": {
|
||||||
|
"gridSize": null,
|
||||||
|
"viewBackgroundColor": "#ffffff"
|
||||||
|
},
|
||||||
|
"files": {}
|
||||||
|
}
|
||||||
BIN
docs/assets/quickstart/Layers.png
Normal file
|
After Width: | Height: | Size: 289 KiB |
BIN
docs/assets/quickstart/shapes_5.1.png
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
docs/assets/quickstart/shapes_5.3.gif
Normal file
|
After Width: | Height: | Size: 408 KiB |
BIN
docs/assets/quickstart/shapes_6.1.gif
Normal file
|
After Width: | Height: | Size: 207 KiB |
BIN
docs/assets/quickstart/shapes_6.2.png
Normal file
|
After Width: | Height: | Size: 457 KiB |
@@ -30,7 +30,7 @@ und für Schülerinnen und Schüler im Unterricht nutzbar machen.
|
|||||||
|
|
||||||
## Dokumentation
|
## Dokumentation
|
||||||
* [Schnellstart](quickstart.md)
|
* [Schnellstart](quickstart.md)
|
||||||
* [Installation](install.md)
|
* [Installation](installation.md)
|
||||||
* {{ javadoc_link() }}
|
* {{ javadoc_link() }}
|
||||||
|
|
||||||
## Über die Zeichenmaschine
|
## Über die Zeichenmaschine
|
||||||
@@ -42,3 +42,5 @@ Projekt in den Classpath aufgenommen werden:
|
|||||||
### NetBeans
|
### NetBeans
|
||||||
|
|
||||||
> Coming soon
|
> Coming soon
|
||||||
|
|
||||||
|
## Unterstützung für MP3
|
||||||
@@ -14,14 +14,33 @@ def define_env(env):
|
|||||||
if "javadoc_default_package" in env.variables and not clazz.startswith(env.variables['javadoc_default_package']):
|
if "javadoc_default_package" in env.variables and not clazz.startswith(env.variables['javadoc_default_package']):
|
||||||
clazz = f"{env.variables['javadoc_default_package'].rstrip('.')}.{clazz}"
|
clazz = f"{env.variables['javadoc_default_package'].rstrip('.')}.{clazz}"
|
||||||
javadoc_url = env.variables["javadoc_url"].rstrip("/")
|
javadoc_url = env.variables["javadoc_url"].rstrip("/")
|
||||||
path = clazz.replace(".", "/") + ".html"
|
|
||||||
|
path = list()
|
||||||
|
name = list()
|
||||||
|
|
||||||
|
for p in clazz.split('.'):
|
||||||
|
if p[0].islower():
|
||||||
|
path.append(p)
|
||||||
|
else:
|
||||||
|
name.append(p)
|
||||||
|
|
||||||
|
path = '/'.join(path) + '/' + '.'.join(name) + ".html"
|
||||||
if target:
|
if target:
|
||||||
path = f"{path}#{target}"
|
path = f"{path}#{target}"
|
||||||
return f"{javadoc_url}/{path}"
|
return f"{javadoc_url}/{path}"
|
||||||
|
|
||||||
|
@env.macro
|
||||||
|
def jd(cl: str = None, t: str = None) -> str:
|
||||||
|
return javadoc(cl, t)
|
||||||
|
|
||||||
@env.macro
|
@env.macro
|
||||||
def javadoc_link(
|
def javadoc_link(
|
||||||
clazz: str = None, target: str = None, strip_package: bool = True, strip_clazz: bool = False, title: str = None
|
clazz: str = None,
|
||||||
|
target: str = None,
|
||||||
|
strip_package: bool = True,
|
||||||
|
strip_clazz: bool = False,
|
||||||
|
strip_params: bool = True,
|
||||||
|
title: str = None
|
||||||
) -> str:
|
) -> str:
|
||||||
name = clazz or "Javadoc"
|
name = clazz or "Javadoc"
|
||||||
if strip_package:
|
if strip_package:
|
||||||
@@ -30,6 +49,17 @@ def define_env(env):
|
|||||||
if target:
|
if target:
|
||||||
# _target = re.sub(r"([^(][^,]*?\.)*?([^)]+)", lambda m: m.group(2), target)
|
# _target = re.sub(r"([^(][^,]*?\.)*?([^)]+)", lambda m: m.group(2), target)
|
||||||
_target = target
|
_target = target
|
||||||
|
|
||||||
|
if m := re.match(r'^(.+?)\((.*)\)$', _target):
|
||||||
|
if strip_params and m.group(2):
|
||||||
|
params = m.group(2).split(',')
|
||||||
|
for i, param in enumerate(params):
|
||||||
|
dot = param.rfind('.')
|
||||||
|
if dot >= 0:
|
||||||
|
params[i] = param[dot+1:].strip()
|
||||||
|
params = ", ".join(params)
|
||||||
|
_target = f'{m.group(1)}({params})'
|
||||||
|
|
||||||
if strip_clazz:
|
if strip_clazz:
|
||||||
name = _target
|
name = _target
|
||||||
else:
|
else:
|
||||||
@@ -37,7 +67,33 @@ def define_env(env):
|
|||||||
if title:
|
if title:
|
||||||
name = title
|
name = title
|
||||||
|
|
||||||
return f"[{name}]({javadoc(clazz, target)})"
|
return f"[`{name}`]({javadoc(clazz, target)})"
|
||||||
|
|
||||||
|
@env.macro
|
||||||
|
def jdl(
|
||||||
|
cl: str = None,
|
||||||
|
t: str = None,
|
||||||
|
p: bool = False,
|
||||||
|
c: bool = True,
|
||||||
|
title: str = None
|
||||||
|
) -> str:
|
||||||
|
return javadoc_link(cl, t, strip_package=not p, strip_clazz=not c, strip_params=True, title=title)
|
||||||
|
|
||||||
|
@env.macro
|
||||||
|
def jdc(
|
||||||
|
cl: str,
|
||||||
|
p: bool = False
|
||||||
|
) -> str:
|
||||||
|
return javadoc_link(cl, strip_package=not p)
|
||||||
|
|
||||||
|
@env.macro
|
||||||
|
def jdm(
|
||||||
|
cl: str,
|
||||||
|
t: str,
|
||||||
|
p: bool = False,
|
||||||
|
c: bool = False
|
||||||
|
) -> str:
|
||||||
|
return javadoc_link(cl, t, strip_package=not p, strip_clazz=not c)
|
||||||
|
|
||||||
@env.macro
|
@env.macro
|
||||||
def javadoc_signature(
|
def javadoc_signature(
|
||||||
@@ -57,6 +113,15 @@ def define_env(env):
|
|||||||
|
|
||||||
return sig
|
return sig
|
||||||
|
|
||||||
|
@env.macro
|
||||||
|
def jds(
|
||||||
|
cl: str = None,
|
||||||
|
m: str = None,
|
||||||
|
pkg: str = None,
|
||||||
|
params: List[str] = list(),
|
||||||
|
) -> str:
|
||||||
|
javadoc_signature(cl, m, pkg, params)
|
||||||
|
|
||||||
|
|
||||||
# schule/ngb/zm/Zeichenmaschine.html#setCursor(java.awt.Image,int,int)
|
# schule/ngb/zm/Zeichenmaschine.html#setCursor(java.awt.Image,int,int)
|
||||||
# schule/ngb/zm/Zeichenmaschine.html#getLayer(java.lang.Class)
|
# schule/ngb/zm/Zeichenmaschine.html#getLayer(java.lang.Class)
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
# Schnellstart mit der Zeichenmaschine
|
# Schnellstart mit der Zeichenmaschine
|
||||||
|
|
||||||
Um ein einfaches Projekt mit der **Zeichenmaschine** aufzusetzen ist nicht mehr
|
Um die **Zeichenmaschine** in einem Projekt zu nutzen ist nicht mehr nötig, als
|
||||||
nötig, als die [JAR-Datei der aktuellen Version](https://github.com/jneug/zeichenmaschine/release/latest)
|
die [JAR-Datei der aktuellen Version](https://github.com/jneug/zeichenmaschine/release/latest)
|
||||||
herunterzuladen und dem *Classpath* des Projekts hinzuzufügen. Eine Beschreibung
|
herunterzuladen und
|
||||||
für verschiedene Entwicklungsumgebungen findet sich im Abschnitt [Installation](install.md).
|
dem [Classpath](https://www.delftstack.com/de/howto/java/java-classpath-/)
|
||||||
|
hinzuzufügen. Eine Beschreibung für verschiedene Entwicklungsumgebungen findet
|
||||||
|
sich im Abschnitt [Installation](installation.md).
|
||||||
|
|
||||||
## Die Basisklasse
|
## Die Basisklasse
|
||||||
|
|
||||||
Um eine Zeichenmaschine zu erstellen, muss eine Unterklasse von {{ javadoc_link("schule.ngb.zm.Zeichenmaschine") }}
|
Eine _Zeichenmaschine_ wird immer als Unterklasse von {{ javadoc_link("
|
||||||
erstellt werden.
|
schule.ngb.zm.Zeichenmaschine") }} erstellt.
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public class Shapes extends Zeichenmaschine {
|
public class Shapes extends Zeichenmaschine {
|
||||||
@@ -19,9 +21,9 @@ public class Shapes extends Zeichenmaschine {
|
|||||||
Die gezeigte Klasse ist schon eine lauffähige Zeichenmaschine und kann gestartet
|
Die gezeigte Klasse ist schon eine lauffähige Zeichenmaschine und kann gestartet
|
||||||
werden.
|
werden.
|
||||||
|
|
||||||
!!! note
|
!!! note "main Methode"
|
||||||
|
|
||||||
Bei einigen Entwicklungsumgebungen muss noch eine `main`-Methode erstellt
|
Bei einigen Entwicklungsumgebungen muss noch eine `main` Methode erstellt
|
||||||
werden, um die Zeichenmaschine zu starten:
|
werden, um die Zeichenmaschine zu starten:
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@@ -30,8 +32,11 @@ werden.
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Es öffnet sich ein Zeichenfenster in einer vordefinierten Größe. Um die Größe und
|
Es öffnet sich ein Zeichenfenster in einer vordefinierten Größe. Um die
|
||||||
den Titel des Fensters zu ändern, legen wir einen Konstruktor an.
|
Abmessungen und den Titel des Fensters zu ändern, legen wir einen Konstruktor
|
||||||
|
an.
|
||||||
|
|
||||||
|
???+ example "Quelltext"
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public class Shapes extends Zeichenmaschine {
|
public class Shapes extends Zeichenmaschine {
|
||||||
@@ -43,8 +48,8 @@ public class Shapes extends Zeichenmaschine {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Starten wir das Projekt nun, wird das Zeichenfenster in der Größe 800x800 mit
|
Starten wir das Projekt, wird eine Zeichenfläche in der Größe 800-mal-800 Pixel
|
||||||
dem Titel "Shapes" angezeigt.
|
erstellt und in einem Fenster mit dem Titel „Shapes“ angezeigt.
|
||||||
|
|
||||||
<figure markdown>
|
<figure markdown>
|
||||||
{ width=400 }
|
{ width=400 }
|
||||||
@@ -52,9 +57,12 @@ dem Titel "Shapes" angezeigt.
|
|||||||
|
|
||||||
### Formen zeichnen
|
### Formen zeichnen
|
||||||
|
|
||||||
Eine Zeichenmaschine hat verschiedene Möglichkeiten, Inhalte in das Zeichenfenster
|
Eine Zeichenmaschine hat verschiedene Möglichkeiten, Inhalte in das
|
||||||
zu zeichnen. Um einfach nur ein statisches Bild zu erzeugen, überschreiben wir die
|
Zeichenfenster zu zeichnen. Um ein einfaches statisches Bild zu erzeugen,
|
||||||
{{ javadoc_link("schule.ngb.zm.Zeichenmaschine", "draw()") }} Methode.
|
überschreiben wir die {{ jdl("schule.ngb.zm.Zeichenmaschine", "draw()",
|
||||||
|
c=False) }} Methode.
|
||||||
|
|
||||||
|
???+ example "Quelltext"
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public class Shapes extends Zeichenmaschine {
|
public class Shapes extends Zeichenmaschine {
|
||||||
@@ -65,17 +73,17 @@ public class Shapes extends Zeichenmaschine {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw() {
|
public void draw() {
|
||||||
background.setColor(43, 128, 243);
|
background.setColor(BLUE);
|
||||||
|
|
||||||
drawing.setFillColor(255, 223, 34);
|
drawing.setFillColor(255, 223, 34);
|
||||||
drawing.noStroke();
|
drawing.noStroke();
|
||||||
drawing.circle(400, 400, 100);
|
drawing.circle(400, 400, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Nun haben wir einen gelben Kreis (ohne Konturlinie) auf einem blauen Hintergrund
|
Wir sehen einen gelben Kreis (ohne Konturlinie) auf einem blauen Hintergrund.
|
||||||
gezeichnet.
|
|
||||||
|
|
||||||
<figure markdown>
|
<figure markdown>
|
||||||
{ width=400 }
|
{ width=400 }
|
||||||
@@ -83,10 +91,13 @@ gezeichnet.
|
|||||||
|
|
||||||
### Vorbereitung der Zeichenfläche
|
### Vorbereitung der Zeichenfläche
|
||||||
|
|
||||||
Im Beispiel oben setzen wir die Hintergrundfarbe auf Blau, die Füllfarbe auf Gelb
|
Im Beispiel oben setzen wir die Hintergrundfarbe auf Blau, die Füllfarbe auf
|
||||||
und deaktivieren die Konturlinie. Wenn diese Einstellungen für alle Zeichenobjekte
|
Gelb und deaktivieren die Konturlinie. Wenn diese Einstellungen für alle
|
||||||
gleich sind, können wir sie statt in `draw()` auch in die `setup()` Methode schreiben.
|
Zeichenobjekte gleich bleiben, können wir sie statt in `draw()` auch in die {{
|
||||||
Diese bereitet die Zeichenfläche vor dem ersten Zeichnen vor.
|
jdl('Zeichenmaschine', 'setup()', c=False) }} Methode schreiben. Diese bereitet
|
||||||
|
die Zeichenfläche vor dem ersten Zeichnen vor.
|
||||||
|
|
||||||
|
???+ example "Quelltext"
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public class Shapes extends Zeichenmaschine {
|
public class Shapes extends Zeichenmaschine {
|
||||||
@@ -97,7 +108,7 @@ public class Shapes extends Zeichenmaschine {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setup() {
|
public void setup() {
|
||||||
background.setColor(43, 128, 243);
|
background.setColor(BLUE);
|
||||||
|
|
||||||
drawing.setFillColor(255, 223, 34);
|
drawing.setFillColor(255, 223, 34);
|
||||||
drawing.noStroke();
|
drawing.noStroke();
|
||||||
@@ -113,6 +124,7 @@ public class Shapes extends Zeichenmaschine {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -125,6 +137,470 @@ Im Beispiel setzen wir nun die Grundeinstellungen in der `setup()` Methode. In
|
|||||||
|
|
||||||
!!! tip ""
|
!!! tip ""
|
||||||
|
|
||||||
Mit {{ javadoc_link("Constants", "canvasWidth", title="canvasWidth") }} und {{ javadoc_link("Constants", "canvasHeight", strip_clazz=True) }} kannst du in der Zeichenmaschine auf
|
Mit {{ jdm("Constants", "canvasWidth") }} und
|
||||||
die aktuelle Größe der Zeichenfläche zugreifen. {{ javadoc_link("Constants", "random(int,int)") }}
|
{{ jdm("Constants", "canvasHeight") }} kannst du in der Zeichenmaschine
|
||||||
erzeugt eine Zufallszahl innerhalb der angegebenen Grenzen.
|
auf die aktuelle Größe der Zeichenfläche zugreifen.
|
||||||
|
{{ jdm("Constants", "random(int,int)") }} erzeugt eine Zufallszahl
|
||||||
|
innerhalb der angegebenen Grenzen.
|
||||||
|
|
||||||
|
## Interaktionen mit der Maus: Whack-a-mole
|
||||||
|
|
||||||
|
Mit der Zeichenmaschine lassen sich Interaktionen mit der Maus leicht umsetzen.
|
||||||
|
Wor wollen das Beispielprogramm zu einem
|
||||||
|
[Whac-A-Mole](https://de.wikipedia.org/wiki/Whac-A-Mole) Spiel erweitern.
|
||||||
|
|
||||||
|
Auf der Zeichenfläche wird nur noch ein gelber Kreis an einer zufälligen Stelle
|
||||||
|
angezeigt. Sobald die Spieler:in auf den Kreis klickt, soll dieser an eine neue
|
||||||
|
Position springen.
|
||||||
|
|
||||||
|
Damit wir den Kreis an eine neue Position springen lassen können, müssen wir
|
||||||
|
zufällige `x`- und `y`-Koordinaten generieren. Dazu erstellen wir zunächst zwei
|
||||||
|
_Objektvariablen_ für die Koordinaten, die in der `setup()` Methode mit
|
||||||
|
zufälligen Werte initialisiert werden. Diese benutzen wir, um die `draw
|
||||||
|
()` Methode anzupassen.
|
||||||
|
|
||||||
|
??? example "Quelltext"
|
||||||
|
|
||||||
|
```Java
|
||||||
|
public class Shapes extends Zeichenmaschine {
|
||||||
|
|
||||||
|
private int moleRadius = 20;
|
||||||
|
|
||||||
|
private int moleX;
|
||||||
|
|
||||||
|
private int moleY;
|
||||||
|
|
||||||
|
public Shapes() {
|
||||||
|
super(800, 800, "Shapes");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setup() {
|
||||||
|
background.setColor(BLUE);
|
||||||
|
|
||||||
|
drawing.setFillColor(255, 223, 34);
|
||||||
|
drawing.noStroke();
|
||||||
|
|
||||||
|
moleX = random(50, canvasWidth - 50);
|
||||||
|
moleY = random(50, canvasHeight - 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw() {
|
||||||
|
drawing.clear();
|
||||||
|
drawing.circle(moleX, moleY, moleRadius);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<figure markdown>
|
||||||
|
{ width=600 }
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
Als Nächstes prüfen wir bei jedem Mausklick, ob die Mauskoordinaten innerhalb
|
||||||
|
des gelben Kreises (des Maulwurfs) liegen. Die Mauskoordinaten sind jederzeit
|
||||||
|
über die Variablen `mouseX` und `mouseY` abrufbar. Um zu prüfen, ob diese
|
||||||
|
Koordinaten innerhalb des Kreises liegen, vergleichen wir den Abstand zwischen
|
||||||
|
Kreismittelpunkt `(moleX, moleY)` und den Mauskoordinaten
|
||||||
|
`(mouseX, mouseY)` mit dem Radius des Kreises (im Bild grün). Ist die Entfernung
|
||||||
|
kleiner als der Radius (blauer Kreis), wurde innerhalb des Kreises geklickt.
|
||||||
|
Sonst außerhalb (roter Kreis).
|
||||||
|
|
||||||
|
<figure markdown>
|
||||||
|
{ width=400 }
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
Den Abstand vom Mittelpunkt zur Maus lässt sich mithilfe des Satzes des
|
||||||
|
Pythagoras leicht selber berechnen. Die Zeichenmaschine kann uns diese Arbeit
|
||||||
|
aber auch abnehmen und stellt eine Methode dafür bereit
|
||||||
|
({{ jdm("Constants", "distance(double,double,double,double)") }}).
|
||||||
|
|
||||||
|
Um auf einen Mausklick zu reagieren, ergänzen wir die
|
||||||
|
{{ jdm("Zeichenmaschine", "mouseClicked()") }} Methode:
|
||||||
|
|
||||||
|
```
|
||||||
|
@Override
|
||||||
|
public void mouseClicked() {
|
||||||
|
if( distance(moleX, moleY, mouseX, mouseY) < moleRadius ) {
|
||||||
|
moleX = random(50, canvasWidth - 50);
|
||||||
|
moleY = random(50, canvasHeight - 50);
|
||||||
|
redraw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
??? example "Quelltext"
|
||||||
|
|
||||||
|
```Java
|
||||||
|
public class Shapes extends Zeichenmaschine {
|
||||||
|
|
||||||
|
private int moleRadius = 20;
|
||||||
|
|
||||||
|
private int moleX;
|
||||||
|
|
||||||
|
private int moleY;
|
||||||
|
|
||||||
|
public Shapes() {
|
||||||
|
super(800, 800, "Shapes");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setup() {
|
||||||
|
background.setColor(BLUE);
|
||||||
|
|
||||||
|
drawing.setFillColor(255, 223, 34);
|
||||||
|
drawing.noStroke();
|
||||||
|
|
||||||
|
moleX = random(50, canvasWidth - 50);
|
||||||
|
moleY = random(50, canvasHeight - 50);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw() {
|
||||||
|
drawing.clear();
|
||||||
|
drawing.circle(moleX, moleY, moleRadius);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseClicked() {
|
||||||
|
if( distance(moleX, moleY, mouseX, mouseY) < moleRadius ) {
|
||||||
|
moleX = random(50, canvasWidth - 50);
|
||||||
|
moleY = random(50, canvasHeight - 50);
|
||||||
|
redraw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
!!! warning ""
|
||||||
|
|
||||||
|
Der Aufruf von {{ jdm("Zeichenmaschine", "redraw()") }} zeichnet
|
||||||
|
die Zeichenfläche neu, indem die `draw()` Methode erneut aufgerufen wird.
|
||||||
|
Du solltest `draw()` niemals direkt aufrufen.
|
||||||
|
|
||||||
|
Nun springt der Kreis an eine andere Stelle, wenn er direkt mit der Maus
|
||||||
|
angeklickt wird.
|
||||||
|
|
||||||
|
<figure markdown>
|
||||||
|
{ width=400 }
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
## Ein paar Details zur Zeichenmaschine
|
||||||
|
|
||||||
|
Die _Zeichenmaschine_ wurde stark von der kreativen Programmierumgebung
|
||||||
|
[Processing](https://processing.org) inspiriert. Wenn Du Processing schon
|
||||||
|
kennst, dann werden Dir einige der Konzepte der _Zeichenmaschine_ schon bekannt
|
||||||
|
vorkommen.
|
||||||
|
|
||||||
|
### Farben
|
||||||
|
|
||||||
|
Farben können auf verschiedene Weisen angegeben werden. Unser Beispiel nutzt
|
||||||
|
bisher zwei Arten:
|
||||||
|
|
||||||
|
1. Die einfachste Möglichkeit sind die _Farbkonstanten_
|
||||||
|
wie {{ jdm('Constants', 'BLUE') }} oder {{ jdm('Constants', 'RED') }}. Im
|
||||||
|
Beispiel setzen wir den Hintergrund auf die Farbe `BLUE`.
|
||||||
|
2. Farben werden häufig im RGB-Farbraum definiert. Dazu wird jeweils der Rot-,
|
||||||
|
Grün- und Blauanteil der Farbe als Wert zwischen 0 und 255 angegeben. Im
|
||||||
|
Beispiel setzen wir die Farbe der Kreise auf `255, 223, 34`, also viel Rot
|
||||||
|
und Grün und nur ein wenig Blau.
|
||||||
|
|
||||||
|
### Ebenen
|
||||||
|
|
||||||
|
Die Zeichenfläche besteht aus einzelnen {{ jdl("Layer", title="Ebenen") }}, die
|
||||||
|
auf übereinander liegen. Bis auf die unterste Ebene sind die Ebenen zunächst
|
||||||
|
durchsichtig, wodurch die Zeichnungen unterer Ebenen durchscheinen.
|
||||||
|
|
||||||
|
<figure markdown>
|
||||||
|
{ width=600 }
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
Eine _Zeichenmaschine_ besitzt zu Beginn drei Ebenen:
|
||||||
|
|
||||||
|
1. Die unterste Ebene ist ein {{ jdc("layers.ColorLayer") }}, die nur aus einer
|
||||||
|
Farbe (oder einem Farbverlauf) besteht und keine durchsichtigen Bereiche
|
||||||
|
besitzt. Im Beispiel setzen wir diese Ebene auf die Farbe `BLUE`.
|
||||||
|
2. Die nächste Ebene ist ein {{ jdc("layers.DrawingLayer") }}, auf die wir
|
||||||
|
unsere Formen zeichnen können. Die Ebene ist zunächst komplett durchsichtig.
|
||||||
|
3. Die oberste Ebene ist ein {{ jdc("layers.ShapesLayer") }}, die zur
|
||||||
|
Darstellung von Form-Objekten der Klasse {{ jdc("shapes.Shape") }} genutzt
|
||||||
|
werden kann.
|
||||||
|
|
||||||
|
Du kannst einer Zeichenfläche aber auch beliebige neue oder selbst programmierte
|
||||||
|
Ebenen hinzufügen.
|
||||||
|
|
||||||
|
### Ablauf
|
||||||
|
|
||||||
|
Die _Zeichenmaschine_ ruft nach dem Start die Methoden in einem festen Ablauf
|
||||||
|
auf.
|
||||||
|
|
||||||
|
<figure markdown>
|
||||||
|
{ width=500 }
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
Erstellst Du eine _Zeichenmaschine_ (beziehungsweise ein Objekt einer
|
||||||
|
Unterklasse), dann wird zuerst die {{ jdm('Zeichenmaschine', 'setup()') }}
|
||||||
|
Methode ausgeführt. Danach folgt einmalig die
|
||||||
|
{{ jdm('Zeichenmaschine', 'draw()') }} Methode und dann endet das Hauptprogramm.
|
||||||
|
|
||||||
|
Die Eingaben der Maus werden in einem parallelen Ablauf (einem _Thread_)
|
||||||
|
abgefangen und daraufhin die {{ jdm('Zeichenmaschine', 'mouseClicked()') }}
|
||||||
|
Methode aufgerufen. In unserem Programm prüfen wir, ob mit der Maus
|
||||||
|
innerhalb des Kreises geklickt wurde und rufen dann
|
||||||
|
{{ jdm('Zeichenmaschine', 'redraw()') }} auf, woraufhin ein weiteres Mal
|
||||||
|
`draw()` ausgeführt wird.
|
||||||
|
|
||||||
|
In _Processing_ wird dies der "statische" Modus genannt, weil das Programm nur
|
||||||
|
einmal abläuft und dann stoppt. "Statisch" trifft es nicht ganz, da das Programm
|
||||||
|
ja zum Beispiel durch Mauseingaben auch verändert werden kann. Wichtig ist aber,
|
||||||
|
dass mit `redraw()` die Zeichenfläche manuell neu gezeichnet werden muss, damit
|
||||||
|
sich der Inhalt ändert.
|
||||||
|
|
||||||
|
## Dynamische Programme
|
||||||
|
|
||||||
|
Wir wollen unser kleines Spiel dynamischer machen, indem die Kreise nur drei
|
||||||
|
Sekunden angezeigt werden und dann von selbst an einen neuen Ort springen.
|
||||||
|
Schafft man es, den Kreis in dieser Zeit anzuklicken, bekommt man einen Punkt.
|
||||||
|
|
||||||
|
Als zusätzliche Herausforderung lassen wir jeden Kreis in den drei Sekunden
|
||||||
|
immer kleiner werden.
|
||||||
|
|
||||||
|
### Die Update-Draw-Schleifen
|
||||||
|
|
||||||
|
{ width=200 align=right }
|
||||||
|
|
||||||
|
Bisher hat die _Zeichenmaschine_ einmalig `draw()` aufgerufen und dann nur noch
|
||||||
|
auf Benutzereingaben mit der Maus reagiert. Nun wollen wir das gezeichnete Bild
|
||||||
|
aber laufend anpassen. Der Kreis soll schrumpfen und nach 3 Sekunden
|
||||||
|
verschwinden.
|
||||||
|
|
||||||
|
Dazu ergänzen wir ein {{ jdl('Zeichenmaschine', 'update(double)', c=False) }}
|
||||||
|
Methode in unserem Programm. Nun schaltet die _Zeichenmaschine_ in einen
|
||||||
|
dynamischen Modus und startet die _Update-Draw-Schleife_. Das beduetet, nach
|
||||||
|
Aufruf von `setup()` wird fortlaufend immer wieder zuerst `update()` und dann
|
||||||
|
`draw()` aufgerufen.
|
||||||
|
|
||||||
|
Jeder Aufruf der `draw()` Methode zeichnet nun die Zeichenfläche neu. Jedes
|
||||||
|
Bild, das gezeichnet wird (auch, wenn es genauso aussieht, wie das davor), nennt
|
||||||
|
man ein _Frame_. Von Videospielen kennst Du vielleicht schon den Begriff "
|
||||||
|
_Frames per second_" (Fps, dt. Bilder pro Sekunde). Er bedeutet, wie oft das
|
||||||
|
Spiel neue Frames zeichnet, oder in der _Zeichenmaschine_, wie oft `draw()` pro
|
||||||
|
Sekunde aufgerufen wird. Normalerweise passiert dies genau 60-mal pro Sekunde.
|
||||||
|
|
||||||
|
### Lebenszeit eines Kreises
|
||||||
|
|
||||||
|
Jeder Kreis soll drei Sekunden zu sehen sein. Daher fügen wir eine neue
|
||||||
|
Objektvariable namens `moleTime` ein, die zunächst auf drei steht. Da wir auch
|
||||||
|
Bruchteile von Skeunden abziehen wollen, wählen wir als Datentyp `double`:
|
||||||
|
|
||||||
|
```Java
|
||||||
|
private double moleTime=3.0;
|
||||||
|
```
|
||||||
|
|
||||||
|
Der Parameter `delta`, der `update()` Methode ist der Zeitraum in Sekunden, seit
|
||||||
|
dem letzten Frame. Subtrahieren wir diesen Wert bei jedem Frame von `moleTime`,
|
||||||
|
wird der Wert immer kleiner. Dann müssen wir nur noch prüfen, ob er kleiner Null
|
||||||
|
ist und in dem Fall den Kreis auf eine neue Position springen lassen.
|
||||||
|
|
||||||
|
Die Größe des Kreises passen wir so an, dass der Anteil der vergangenen Zeit die
|
||||||
|
Größe des Kreises bestimmt:
|
||||||
|
|
||||||
|
```Java
|
||||||
|
drawing.circle(moleX,moleY,moleRadius*(moleTime/3.0));
|
||||||
|
```
|
||||||
|
|
||||||
|
??? example "Quelltext"
|
||||||
|
|
||||||
|
```Java
|
||||||
|
import schule.ngb.zm.Zeichenmaschine;
|
||||||
|
|
||||||
|
public class Shapes extends Zeichenmaschine {
|
||||||
|
|
||||||
|
private int moleRadius = 20;
|
||||||
|
|
||||||
|
private int moleX;
|
||||||
|
|
||||||
|
private int moleY;
|
||||||
|
|
||||||
|
private double moleTime;
|
||||||
|
|
||||||
|
public Shapes() {
|
||||||
|
super(800, 800, "Shapes");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setup() {
|
||||||
|
background.setColor(BLUE);
|
||||||
|
|
||||||
|
drawing.setFillColor(255, 223, 34);
|
||||||
|
drawing.noStroke();
|
||||||
|
|
||||||
|
randomizeMole();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void randomizeMole() {
|
||||||
|
moleX = random(moleRadius*2, canvasWidth - moleRadius*2);
|
||||||
|
moleY = random(moleRadius*2, canvasHeight - moleRadius*2);
|
||||||
|
moleTime = 3.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update( double delta ) {
|
||||||
|
moleTime -= delta;
|
||||||
|
|
||||||
|
if( moleTime <= 0 ) {
|
||||||
|
randomizeMole();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw() {
|
||||||
|
drawing.clear();
|
||||||
|
drawing.circle(moleX, moleY, moleRadius * (moleTime / 3.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseClicked() {
|
||||||
|
if( distance(moleX, moleY, mouseX, mouseY) < moleRadius ) {
|
||||||
|
randomizeMole();
|
||||||
|
redraw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main( String[] args ) {
|
||||||
|
new Shapes();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<figure markdown>
|
||||||
|
{ width=400 }
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
!!! tip ""
|
||||||
|
|
||||||
|
Der Maulwurf muss mittlerweile an drei verschiedenen Stellen im Programm
|
||||||
|
auf eine zufällige Position gesetzt werden (am Anfang, wenn er angeklickt
|
||||||
|
wurde und wenn die drei Sekunden abgelaufen sind). Daher wurde das Versetzen
|
||||||
|
in eine eigene Methode `randomizeMole()` ausgelagert.
|
||||||
|
|
||||||
|
### Punktezähler
|
||||||
|
|
||||||
|
Zum Schluss wollen wir noch bei jedem Treffer mit der Maus die Punkte Zählen und
|
||||||
|
als Text auf die Zeichenfläche schreiben.
|
||||||
|
|
||||||
|
Dazu ergänzen wir eine weitere Objektvariable `score`, die in `mouseClicked()`
|
||||||
|
erhöht wird, falls der Kreis getroffen wurde.
|
||||||
|
|
||||||
|
Um den Punktestand anzuzeigen ergänzen wir in `draw()` einen Aufruf von
|
||||||
|
{{ jdl('layers.DrawingLayer', 'text(java.lang.String,double,double,schule.ngb.zm.Options.Direction)') }}
|
||||||
|
mit dem Inhalt von `score` und den Koordinaten, an denen der Text gezeigt
|
||||||
|
werden soll.
|
||||||
|
Die Zeichenebene zeichnet im Moment alle Formen und Text ausgehend vom Zentrum
|
||||||
|
der Form. Damit der Text 10 Pixel vom Rand entfernt links oben angezeigt wird,
|
||||||
|
können wir der Text Methode (und allen anderen, die etwas zeichnen) eine {{
|
||||||
|
jdl('Options.Direction', title='Richtung') }} übergeben, die festlegt, von
|
||||||
|
welchem Ausgangspunkt (oder _Ankerpunkt_) die Form gezeichnet werden soll.
|
||||||
|
|
||||||
|
```Java
|
||||||
|
drawing.setFillColor(BLACK);
|
||||||
|
drawing.text("Punkte: "+score,10,10,NORTHWEST);
|
||||||
|
```
|
||||||
|
|
||||||
|
<figure markdown>
|
||||||
|
{ width=400 }
|
||||||
|
</figure>
|
||||||
|
|
||||||
|
??? example "Quelltext"
|
||||||
|
|
||||||
|
```Java
|
||||||
|
import schule.ngb.zm.Zeichenmaschine;
|
||||||
|
|
||||||
|
public class Shapes extends Zeichenmaschine {
|
||||||
|
|
||||||
|
private int moleRadius = 20;
|
||||||
|
|
||||||
|
private int moleX;
|
||||||
|
|
||||||
|
private int moleY;
|
||||||
|
|
||||||
|
private double moleTime;
|
||||||
|
|
||||||
|
private int score = 0;
|
||||||
|
|
||||||
|
public Shapes() {
|
||||||
|
super(800, 800, "Shapes");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setup() {
|
||||||
|
background.setColor(BLUE);
|
||||||
|
|
||||||
|
drawing.noStroke();
|
||||||
|
drawing.setFontSize(24);
|
||||||
|
|
||||||
|
randomizeMole();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void randomizeMole() {
|
||||||
|
moleX = random(moleRadius*2, canvasWidth - moleRadius*2);
|
||||||
|
moleY = random(moleRadius*2, canvasHeight - moleRadius*2);
|
||||||
|
moleTime = 3.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update( double delta ) {
|
||||||
|
moleTime -= delta;
|
||||||
|
|
||||||
|
if( moleTime <= 0 ) {
|
||||||
|
randomizeMole();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw() {
|
||||||
|
drawing.clear();
|
||||||
|
|
||||||
|
drawing.setFillColor(255, 223, 34);
|
||||||
|
drawing.circle(moleX, moleY, moleRadius * (moleTime / 3.0));
|
||||||
|
|
||||||
|
drawing.setFillColor(BLACK);
|
||||||
|
drawing.text("Punkte: " + score, 10, 10, NORTHWEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void mouseClicked() {
|
||||||
|
if( distance(moleX, moleY, mouseX, mouseY) < moleRadius ) {
|
||||||
|
score += 1;
|
||||||
|
randomizeMole();
|
||||||
|
redraw();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main( String[] args ) {
|
||||||
|
new Shapes();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Wie es weitergehen kann
|
||||||
|
|
||||||
|
In diesem Schnellstart-Tutorial hast Du die Grundlagen der _Zeichenmaschine_
|
||||||
|
gelernt. Um weiterzumachen, kannst Du versuchen, das Whack-a-mole Spiel um diese
|
||||||
|
Funktionen zu erweitern:
|
||||||
|
|
||||||
|
- Mehrere "Maulwürfe" gleichzeitig.
|
||||||
|
- Unterschiedliche Zeiten pro Maulwurf.
|
||||||
|
|
||||||
|
Wenn Du mehr über die Möglichkeiten lernen möchtest, die Dir die
|
||||||
|
_Zeichenmaschine_ bereitstellt, kannst Du Dir die weiteren Tutorials in dieser
|
||||||
|
Dokumentation ansehen. Ein guter Startpunkt ist das
|
||||||
|
[Aquarium](tutorials/aquarium/aquarium1.md).
|
||||||
|
|
||||||
|
Viele verschiedene Beispiele, ohne detailliertes Tutorial, findest Du in der
|
||||||
|
Kategorie Beispiele und auf GitHub im Repository
|
||||||
|
[jneug/zeichenmaschine-examples](https://github.com/jneug/zeichenmaschine-examples).
|
||||||
|
|||||||
19
docs/tutorials/aquarium/aquarium1.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Tutorial: Aquarium
|
||||||
|
|
||||||
|
In diesem Tutorial wollen wir mithilfe der _Zeichenmaschine_ ein (bonbonbuntes)
|
||||||
|
interaktives Aquarium entwickeln. Dabei werden wir in verschiedenen Ausbaustufen
|
||||||
|
zunächst das System Modellieren und dann implementieren.
|
||||||
|
|
||||||
|
!!! info "Mein bonbonbuntes Aquarium"
|
||||||
|
|
||||||
|
Das Projekt [Mein bonbonbuntes Aquarium](http://blog.schockwellenreiter.de/2021/02/2021021201.html)
|
||||||
|
stammt ursprünglich aus dem Blog [Schockwellenreiter](http://blog.schockwellenreiter.de)
|
||||||
|
von [Jörg Kantel](http://cognitiones.kantel-chaos-team.de/cv.html).
|
||||||
|
|
||||||
|
Das Endprodukt soll folgendes umfassen:
|
||||||
|
|
||||||
|
- Darstellung eines hübschen Aquariums mit Fischen, die hin und her schwimmen.
|
||||||
|
- Zur Darstellung wollen wir wie im Original die Sprites aus dem [Fish Pack von Kenny.nl](https://www.kenney.nl/assets/fish-pack) nutzen.
|
||||||
|
- Das Aquarium soll durch passende Geräusche untermalt werden.
|
||||||
|
- Bei einem Klick in das Aquarium soll ein zufälliger Fisch erscheinen.
|
||||||
|
- Bei einem Druck auf die Leertaste soll ein Hai durch das Aquarium schwimmen und alle Fische auf seinem Weg auffressen.
|
||||||
17
mkdocs.yml
@@ -37,9 +37,13 @@ extra_css:
|
|||||||
- assets/zmstyles.css
|
- assets/zmstyles.css
|
||||||
|
|
||||||
nav:
|
nav:
|
||||||
- Einführung: index.md
|
- Einführung: einfuehrung.md
|
||||||
- Schnellstart: quickstart.md
|
- Schnellstart: schnellstart.md
|
||||||
- Installation: install.md
|
- Installation: installation.md
|
||||||
|
- Tutorials:
|
||||||
|
- Aquarium: tutorials/aquarium/aquarium1.md
|
||||||
|
- Beispiele:
|
||||||
|
- Mondrian: beispiele/mondrian.md
|
||||||
|
|
||||||
markdown_extensions:
|
markdown_extensions:
|
||||||
- admonition
|
- admonition
|
||||||
@@ -62,8 +66,11 @@ markdown_extensions:
|
|||||||
- pymdownx.inlinehilite
|
- pymdownx.inlinehilite
|
||||||
- pymdownx.snippets
|
- pymdownx.snippets
|
||||||
- pymdownx.details
|
- pymdownx.details
|
||||||
- pymdownx.superfences
|
- pymdownx.superfences:
|
||||||
|
custom_fences:
|
||||||
|
- name: mermaid
|
||||||
|
class: mermaid
|
||||||
|
format: !!python/name:pymdownx.superfences.fence_code_format
|
||||||
plugins:
|
plugins:
|
||||||
- search:
|
- search:
|
||||||
lang: de
|
lang: de
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package schule.ngb.zm;
|
package schule.ngb.zm;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.Graphics2D;
|
||||||
|
import java.awt.MultipleGradientPaint;
|
||||||
|
import java.awt.Paint;
|
||||||
|
import java.awt.Stroke;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Basisimplementierung der {@link Strokeable} und {@link Fillable} APIs.
|
* Basisimplementierung der {@link Strokeable} und {@link Fillable} APIs.
|
||||||
@@ -90,9 +93,6 @@ public abstract class BasicDrawable extends Constants implements Strokeable, Fil
|
|||||||
|
|
||||||
// Implementierung Fillable Interface
|
// Implementierung Fillable Interface
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void setFill( Paint fill ) {
|
public void setFill( Paint fill ) {
|
||||||
if( fill == null ) {
|
if( fill == null ) {
|
||||||
@@ -106,9 +106,6 @@ public abstract class BasicDrawable extends Constants implements Strokeable, Fil
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Paint getFill() {
|
public Paint getFill() {
|
||||||
if( fill != null ) {
|
if( fill != null ) {
|
||||||
@@ -120,42 +117,27 @@ public abstract class BasicDrawable extends Constants implements Strokeable, Fil
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasFillColor() {
|
public boolean hasFillColor() {
|
||||||
return fillColor != null;
|
return fillColor != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasGradient() {
|
public boolean hasGradient() {
|
||||||
return fill != null;
|
return fill != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Color getFillColor() {
|
public Color getFillColor() {
|
||||||
return fillColor;
|
return fillColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void setFillColor( Color color ) {
|
public void setFillColor( Color color ) {
|
||||||
fillColor = color;
|
fillColor = color;
|
||||||
fill = null;
|
fill = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public MultipleGradientPaint getGradient() {
|
public MultipleGradientPaint getGradient() {
|
||||||
return fill;
|
return fill;
|
||||||
@@ -164,17 +146,11 @@ public abstract class BasicDrawable extends Constants implements Strokeable, Fil
|
|||||||
|
|
||||||
// Implementierung Strokeable Interface
|
// Implementierung Strokeable Interface
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void setStroke( Stroke stroke ) {
|
public void setStroke( Stroke stroke ) {
|
||||||
this.stroke = stroke;
|
this.stroke = stroke;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Stroke getStroke() {
|
public Stroke getStroke() {
|
||||||
if( stroke == null ) {
|
if( stroke == null ) {
|
||||||
@@ -183,50 +159,32 @@ public abstract class BasicDrawable extends Constants implements Strokeable, Fil
|
|||||||
return stroke;
|
return stroke;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Color getStrokeColor() {
|
public Color getStrokeColor() {
|
||||||
return strokeColor;
|
return strokeColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void setStrokeColor( Color color ) {
|
public void setStrokeColor( Color color ) {
|
||||||
strokeColor = color;
|
strokeColor = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public double getStrokeWeight() {
|
public double getStrokeWeight() {
|
||||||
return strokeWeight;
|
return strokeWeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void setStrokeWeight( double weight ) {
|
public void setStrokeWeight( double weight ) {
|
||||||
strokeWeight = weight;
|
strokeWeight = weight;
|
||||||
this.stroke = null;
|
this.stroke = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Options.StrokeType getStrokeType() {
|
public Options.StrokeType getStrokeType() {
|
||||||
return strokeType;
|
return strokeType;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void setStrokeType( Options.StrokeType type ) {
|
public void setStrokeType( Options.StrokeType type ) {
|
||||||
strokeType = type;
|
strokeType = type;
|
||||||
|
|||||||
@@ -52,38 +52,67 @@ public class Color implements Paint {
|
|||||||
* Die Farbe Zeichenmaschinen-Rot.
|
* Die Farbe Zeichenmaschinen-Rot.
|
||||||
*/
|
*/
|
||||||
public static final Color RED = new Color(240, 80, 37);
|
public static final Color RED = new Color(240, 80, 37);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Die Farbe Rot.
|
||||||
|
*/
|
||||||
|
public static final Color PURE_RED = new Color(255, 0, 0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Die Farbe Zeichenmaschinen-Grün.
|
* Die Farbe Zeichenmaschinen-Grün.
|
||||||
*/
|
*/
|
||||||
public static final Color GREEN = new Color(98, 199, 119);
|
public static final Color GREEN = new Color(98, 199, 119);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Die Farbe Grün.
|
||||||
|
*/
|
||||||
|
public static final Color PURE_GREEN = new Color(0, 255, 0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Die Farbe Zeichenmaschinen-Blau.
|
* Die Farbe Zeichenmaschinen-Blau.
|
||||||
*/
|
*/
|
||||||
public static final Color BLUE = new Color(49, 197, 244);
|
public static final Color BLUE = new Color(43, 128, 243); // 49, 197, 244
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Die Farbe Blau.
|
||||||
|
*/
|
||||||
|
public static final Color PURE_BLUE = new Color(0, 0, 255);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Die Farbe Zeichenmaschinen-Gelb.
|
* Die Farbe Zeichenmaschinen-Gelb.
|
||||||
*/
|
*/
|
||||||
public static final Color YELLOW = new Color(248, 239, 34);
|
public static final Color YELLOW = new Color(248, 239, 34);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Die Farbe Gelb.
|
||||||
|
*/
|
||||||
|
public static final Color PURE_YELLOW = new Color(255, 255, 0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Die Farbe Zeichenmaschinen-Orange.
|
* Die Farbe Zeichenmaschinen-Orange.
|
||||||
*/
|
*/
|
||||||
public static final Color ORANGE = new Color(248, 158, 80);
|
public static final Color ORANGE = new Color(248, 158, 80);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Die Farbe Zeichenmaschinen-Türkis.
|
* Die Farbe Zeichenmaschinen-Türkis.
|
||||||
*/
|
*/
|
||||||
public static final Color CYAN = new Color(java.awt.Color.CYAN);
|
public static final Color CYAN = new Color(java.awt.Color.CYAN);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Die Farbe Zeichenmaschinen-Magenta.
|
* Die Farbe Zeichenmaschinen-Magenta.
|
||||||
*/
|
*/
|
||||||
public static final Color MAGENTA = new Color(java.awt.Color.MAGENTA);
|
public static final Color MAGENTA = new Color(java.awt.Color.MAGENTA);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Die Farbe Zeichenmaschinen-Rosa.
|
* Die Farbe Zeichenmaschinen-Rosa.
|
||||||
*/
|
*/
|
||||||
public static final Color PINK = new Color(240, 99, 164);
|
public static final Color PINK = new Color(240, 99, 164);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Die Farbe Zeichenmaschinen-Lila.
|
* Die Farbe Zeichenmaschinen-Lila.
|
||||||
*/
|
*/
|
||||||
public static final Color PURPLE = new Color(101, 0, 191);
|
public static final Color PURPLE = new Color(101, 0, 191);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Die Farbe Zeichenmaschinen-Braun.
|
* Die Farbe Zeichenmaschinen-Braun.
|
||||||
*/
|
*/
|
||||||
@@ -93,6 +122,7 @@ public class Color implements Paint {
|
|||||||
* Die Farbe Helmholtz-Grün.
|
* Die Farbe Helmholtz-Grün.
|
||||||
*/
|
*/
|
||||||
public static final Color HGGREEN = new Color(0, 165, 81);
|
public static final Color HGGREEN = new Color(0, 165, 81);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Die Farbe Helmholtz-Rot.
|
* Die Farbe Helmholtz-Rot.
|
||||||
*/
|
*/
|
||||||
@@ -272,10 +302,7 @@ public class Color implements Paint {
|
|||||||
color = color.toLowerCase().strip();
|
color = color.toLowerCase().strip();
|
||||||
|
|
||||||
// Parse colornames file and return first match
|
// Parse colornames file and return first match
|
||||||
try(
|
try( InputStream in = Color.class.getResourceAsStream("colornames.csv"); BufferedReader reader = new BufferedReader(new InputStreamReader(in)) ) {
|
||||||
InputStream in = Color.class.getResourceAsStream("colornames.csv");
|
|
||||||
BufferedReader reader = new BufferedReader(new InputStreamReader(in))
|
|
||||||
) {
|
|
||||||
String line;
|
String line;
|
||||||
while( (line = reader.readLine()) != null ) {
|
while( (line = reader.readLine()) != null ) {
|
||||||
String[] parts = line.split(",");
|
String[] parts = line.split(",");
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package schule.ngb.zm;
|
package schule.ngb.zm;
|
||||||
|
|
||||||
import schule.ngb.zm.anim.Easing;
|
import schule.ngb.zm.anim.Easing;
|
||||||
|
import schule.ngb.zm.util.Validator;
|
||||||
import schule.ngb.zm.util.io.ImageLoader;
|
import schule.ngb.zm.util.io.ImageLoader;
|
||||||
import schule.ngb.zm.util.Noise;
|
import schule.ngb.zm.util.Noise;
|
||||||
|
|
||||||
@@ -785,6 +786,30 @@ public class Constants {
|
|||||||
|
|
||||||
// Mathematische Funktionen
|
// Mathematische Funktionen
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Berechnet das Minimum aller angegebenen Werte.
|
||||||
|
*
|
||||||
|
* <pre><code>
|
||||||
|
* int minimum = min(1, 5, 3); // 1
|
||||||
|
* </code></pre>
|
||||||
|
*
|
||||||
|
* @param numbers Die Werte, aus denen das Minimum ermittelt werden soll.
|
||||||
|
* @return Das Minimum der Werte.
|
||||||
|
* @throws IllegalArgumentException Wenn die Eingabe {@code null} oder leer
|
||||||
|
* ist.
|
||||||
|
*/
|
||||||
|
public static final int min( int... numbers ) {
|
||||||
|
if( numbers == null || numbers.length == 0 ) {
|
||||||
|
throw new IllegalArgumentException("Array may not be <null> or empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
int min = numbers[0];
|
||||||
|
for( int i = 1; i < numbers.length; i++ ) {
|
||||||
|
min = Math.min(min, numbers[i]);
|
||||||
|
}
|
||||||
|
return min;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Berechnet das Minimum aller angegebenen Werte.
|
* Berechnet das Minimum aller angegebenen Werte.
|
||||||
*
|
*
|
||||||
@@ -809,6 +834,30 @@ public class Constants {
|
|||||||
return min;
|
return min;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Berechnet das Maximum aller angegebenen Werte.
|
||||||
|
*
|
||||||
|
* <pre><code>
|
||||||
|
* double maximum = max(1, 5, 3); // 5
|
||||||
|
* </code></pre>
|
||||||
|
*
|
||||||
|
* @param numbers Die Werte, aus denen das Maximum ermittelt werden soll.
|
||||||
|
* @return Das Maximum der Werte.
|
||||||
|
* @throws IllegalArgumentException Wenn die Eingabe {@code null} oder leer
|
||||||
|
* ist.
|
||||||
|
*/
|
||||||
|
public static final int max( int... numbers ) {
|
||||||
|
if( numbers == null || numbers.length == 0 ) {
|
||||||
|
throw new IllegalArgumentException("Array may not be <null> or empty.");
|
||||||
|
}
|
||||||
|
|
||||||
|
int max = numbers[0];
|
||||||
|
for( int i = 1; i < numbers.length; i++ ) {
|
||||||
|
max = Math.max(max, numbers[i]);
|
||||||
|
}
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Berechnet das Maximum aller angegebenen Werte.
|
* Berechnet das Maximum aller angegebenen Werte.
|
||||||
*
|
*
|
||||||
@@ -1204,6 +1253,12 @@ public class Constants {
|
|||||||
return interpolate(toMin, toMax, (value - fromMin) / (fromMax - fromMin));
|
return interpolate(toMin, toMax, (value - fromMin) / (fromMax - fromMin));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static final double distance( double fromX, double fromY, double toX, double toY ) {
|
||||||
|
double diffX = toX - fromX;
|
||||||
|
double diffY = toY - fromY;
|
||||||
|
return sqrt(diffX*diffX + diffY*diffY);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Geteilte {@code Random}-Instanz für einheitliche Zufallszahlen.
|
* Geteilte {@code Random}-Instanz für einheitliche Zufallszahlen.
|
||||||
*/
|
*/
|
||||||
@@ -1340,9 +1395,9 @@ public class Constants {
|
|||||||
* @param values Ein Array mit Werten, die zur Auswahl stehen.
|
* @param values Ein Array mit Werten, die zur Auswahl stehen.
|
||||||
* @return Ein zufälliges Element aus dem Array.
|
* @return Ein zufälliges Element aus dem Array.
|
||||||
*/
|
*/
|
||||||
public static final int choice( int[] values ) {
|
/*public static final int choice( int... values ) {
|
||||||
return values[random(0, values.length - 1)];
|
return values[random(0, values.length - 1)];
|
||||||
}
|
}*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wählt ein zufälliges Element aus dem Array aus.
|
* Wählt ein zufälliges Element aus dem Array aus.
|
||||||
@@ -1350,9 +1405,9 @@ public class Constants {
|
|||||||
* @param values Ein Array mit Werten, die zur Auswahl stehen.
|
* @param values Ein Array mit Werten, die zur Auswahl stehen.
|
||||||
* @return Ein zufälliges Element aus dem Array.
|
* @return Ein zufälliges Element aus dem Array.
|
||||||
*/
|
*/
|
||||||
public static final double choice( double[] values ) {
|
/*public static final double choice( double... values ) {
|
||||||
return values[random(0, values.length - 1)];
|
return values[random(0, values.length - 1)];
|
||||||
}
|
}*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wählt ein zufälliges Element aus dem Array aus.
|
* Wählt ein zufälliges Element aus dem Array aus.
|
||||||
@@ -1361,7 +1416,7 @@ public class Constants {
|
|||||||
* @param <T> Datentyp des Elements.
|
* @param <T> Datentyp des Elements.
|
||||||
* @return Ein zufälliges Element aus dem Array.
|
* @return Ein zufälliges Element aus dem Array.
|
||||||
*/
|
*/
|
||||||
public static final <T> T choice( T[] values ) {
|
public static final <T> T choice( T... values ) {
|
||||||
return values[random(0, values.length - 1)];
|
return values[random(0, values.length - 1)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -88,7 +88,11 @@ public abstract class Layer extends Constants implements Drawable, Updatable {
|
|||||||
} else {
|
} else {
|
||||||
createCanvas(width, height);
|
createCanvas(width, height);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: prevent access to graphics context?
|
||||||
|
public Graphics2D getGraphics() {
|
||||||
|
return this.drawing;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -203,4 +207,22 @@ public abstract class Layer extends Constants implements Drawable, Updatable {
|
|||||||
return active;
|
return active;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prüft, ob die angegebenen Koordinaten innerhalb der Ebene liegen, oder
|
||||||
|
* nicht.
|
||||||
|
* <p>
|
||||||
|
* Eine Koordinate liegt in der Ebene, wenn die {@code x}- und
|
||||||
|
* {@code y}-Koordinaten größer oder gleich Null und kleiner als die Breite
|
||||||
|
* bzw. Höhe der Ebene sind.
|
||||||
|
*
|
||||||
|
* @param x Die x-Koordinate.
|
||||||
|
* @param y Die y-Koordinate.
|
||||||
|
* @return {@code true}, wenn die Koordinaten innerhalb der Ebene liegen,
|
||||||
|
* {@code false}, wenn sie außerhalb liegen.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings( "unused" )
|
||||||
|
public boolean isInBounds( int x, int y ) {
|
||||||
|
return (x >= 0 && y >= 0 && x < getWidth() && y < getHeight());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1447,7 +1447,11 @@ public class Zeichenmaschine extends Constants {
|
|||||||
Thread.yield();
|
Thread.yield();
|
||||||
}
|
}
|
||||||
updateThreadExecutor.shutdownNow();
|
updateThreadExecutor.shutdownNow();
|
||||||
|
try {
|
||||||
|
updateThreadExecutor.awaitTermination(500, TimeUnit.MILLISECONDS);
|
||||||
|
} catch( InterruptedException ex ) {
|
||||||
|
|
||||||
|
} finally {
|
||||||
// Cleanup
|
// Cleanup
|
||||||
shutdown();
|
shutdown();
|
||||||
cleanup();
|
cleanup();
|
||||||
@@ -1457,6 +1461,7 @@ public class Zeichenmaschine extends Constants {
|
|||||||
quit();
|
quit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,9 +57,6 @@ public class ColorLayer extends Layer {
|
|||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void setSize( int width, int height ) {
|
public void setSize( int width, int height ) {
|
||||||
super.setSize(width, height);
|
super.setSize(width, height);
|
||||||
@@ -67,16 +64,14 @@ public class ColorLayer extends Layer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gibt die Hintergrundfarbe der Ebene zurück.
|
* @return Die aktuelle Hintergrundfarbe der Ebene.
|
||||||
*
|
|
||||||
* @return Die aktuelle Hintergrundfarbe.
|
|
||||||
*/
|
*/
|
||||||
public Color getColor() {
|
public Color getColor() {
|
||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Setzt die Farbe der Ebene neu.
|
* Setzt die Farbe der Ebene auf die angegebene Farbe.
|
||||||
*
|
*
|
||||||
* @param color Die neue Hintergrundfarbe.
|
* @param color Die neue Hintergrundfarbe.
|
||||||
*/
|
*/
|
||||||
@@ -86,22 +81,69 @@ public class ColorLayer extends Layer {
|
|||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt die Farbe der Ebene auf einen Grauwert mit der angegebenen
|
||||||
|
* Intensität. 0 entspricht schwarz, 255 entspricht weiß.
|
||||||
|
*
|
||||||
|
* @param gray Ein Grauwert zwischen 0 und 255.
|
||||||
|
* @see Color#Color(int)
|
||||||
|
*/
|
||||||
public void setColor( int gray ) {
|
public void setColor( int gray ) {
|
||||||
setColor(gray, gray, gray, 255);
|
setColor(gray, gray, gray, 255);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt die Farbe der Ebene auf einen Grauwert mit der angegebenen
|
||||||
|
* Intensität und dem angegebenen Transparenzwert. Der Grauwert 0 entspricht
|
||||||
|
* schwarz, 255 entspricht weiß.
|
||||||
|
*
|
||||||
|
* @param gray Ein Grauwert zwischen 0 und 255.
|
||||||
|
* @param alpha Ein Transparenzwert zwischen 0 und 255.
|
||||||
|
* @see Color#Color(int, int)
|
||||||
|
*/
|
||||||
public void setColor( int gray, int alpha ) {
|
public void setColor( int gray, int alpha ) {
|
||||||
setColor(gray, gray, gray, alpha);
|
setColor(gray, gray, gray, alpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt die Farbe der Ebene auf die Farbe mit den angegebenen Rot-, Grün-
|
||||||
|
* und Blauanteilen.
|
||||||
|
*
|
||||||
|
* @param red Der Rotanteil der Farbe zwischen 0 und 255.
|
||||||
|
* @param green Der Grünanteil der Farbe zwischen 0 und 255.
|
||||||
|
* @param blue Der Blauanteil der Farbe zwischen 0 und 255.
|
||||||
|
* @see Color#Color(int, int, int)
|
||||||
|
* @see <a
|
||||||
|
* href="https://de.wikipedia.org/wiki/RGB-Farbraum">https://de.wikipedia.org/wiki/RGB-Farbraum</a>
|
||||||
|
*/
|
||||||
public void setColor( int red, int green, int blue ) {
|
public void setColor( int red, int green, int blue ) {
|
||||||
setColor(red, green, blue, 255);
|
setColor(red, green, blue, 255);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt die Farbe der Ebene auf die Farbe mit den angegebenen Rot-, Grün-
|
||||||
|
* und Blauanteilen und dem angegebenen Transparenzwert.
|
||||||
|
*
|
||||||
|
* @param red Der Rotanteil der Farbe zwischen 0 und 255.
|
||||||
|
* @param green Der Grünanteil der Farbe zwischen 0 und 255.
|
||||||
|
* @param blue Der Blauanteil der Farbe zwischen 0 und 255.
|
||||||
|
* @param alpha Ein Transparenzwert zwischen 0 und 25
|
||||||
|
* @see Color#Color(int, int, int, int)
|
||||||
|
* @see <a
|
||||||
|
* href="https://de.wikipedia.org/wiki/RGB-Farbraum">https://de.wikipedia.org/wiki/RGB-Farbraum</a>
|
||||||
|
*/
|
||||||
public void setColor( int red, int green, int blue, int alpha ) {
|
public void setColor( int red, int green, int blue, int alpha ) {
|
||||||
setColor(new Color(red, green, blue, alpha));
|
setColor(new Color(red, green, blue, alpha));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt die Füllung der Ebene auf einen linearen Farbverlauf, der in die
|
||||||
|
* angegebene Richtung verläuft.
|
||||||
|
*
|
||||||
|
* @param from Farbe am Startpunkt.
|
||||||
|
* @param to Farbe am Endpunkt.
|
||||||
|
* @param dir Richtung des Farbverlaufs.
|
||||||
|
*/
|
||||||
public void setGradient( Color from, Color to, Options.Direction dir ) {
|
public void setGradient( Color from, Color to, Options.Direction dir ) {
|
||||||
double halfW = getWidth() * .5;
|
double halfW = getWidth() * .5;
|
||||||
double halfH = getHeight() * .5;
|
double halfH = getHeight() * .5;
|
||||||
@@ -116,6 +158,18 @@ public class ColorLayer extends Layer {
|
|||||||
setGradient(fromX, fromY, from, toX, toY, to);
|
setGradient(fromX, fromY, from, toX, toY, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt die Füllung der Ebene auf einen linearen Farbverlauf, der am Punkt
|
||||||
|
* ({@code fromX}, {@code fromY}) mit der Farbe {@code from} startet und am
|
||||||
|
* Punkt (({@code toX}, {@code toY}) mit der Farbe {@code to} endet.
|
||||||
|
*
|
||||||
|
* @param fromX x-Koordinate des Startpunktes.
|
||||||
|
* @param fromY y-Koordinate des Startpunktes.
|
||||||
|
* @param from Farbe am Startpunkt.
|
||||||
|
* @param toX x-Koordinate des Endpunktes.
|
||||||
|
* @param toY y-Koordinate des Endpunktes.
|
||||||
|
* @param to Farbe am Endpunkt.
|
||||||
|
*/
|
||||||
public void setGradient( double fromX, double fromY, Color from, double toX, double toY, Color to ) {
|
public void setGradient( double fromX, double fromY, Color from, double toX, double toY, Color to ) {
|
||||||
this.color = from;
|
this.color = from;
|
||||||
background = new GradientPaint(
|
background = new GradientPaint(
|
||||||
@@ -125,10 +179,30 @@ public class ColorLayer extends Layer {
|
|||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt die Füllung der Ebene auf einen kreisförmigen (radialen)
|
||||||
|
* Farbverlauf, der im Zentrum beginnt.
|
||||||
|
*
|
||||||
|
* @param from Farbe im Zentrum.
|
||||||
|
* @param to Farbe am Rand.
|
||||||
|
*/
|
||||||
public void setGradient( Color from, Color to ) {
|
public void setGradient( Color from, Color to ) {
|
||||||
setGradient(getWidth() * .5, getHeight() * .5, Math.min(getWidth() * .5, getHeight() * .5), from, to);
|
setGradient(getWidth() * .5, getHeight() * .5, Math.min(getWidth() * .5, getHeight() * .5), from, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt die Füllung der Ebene auf einen kreisförmigen (radialen)
|
||||||
|
* Farbverlauf, mit dem Zentrum im Punkt ({@code centerX}, {@code centerY})
|
||||||
|
* und dem angegebenen Radius. Der Verlauf starte im Zentrum mit der Farbe
|
||||||
|
* {@code from} und endet am Rand des durch den Radius beschriebenen Kreises
|
||||||
|
* mit der Farbe {@code to}.
|
||||||
|
*
|
||||||
|
* @param centerX x-Koordinate des Kreismittelpunktes.
|
||||||
|
* @param centerY y-Koordinate des Kreismittelpunktes.
|
||||||
|
* @param radius Radius des Kreises.
|
||||||
|
* @param from Farbe im Zentrum des Kreises.
|
||||||
|
* @param to Farbe am Rand des Kreises.
|
||||||
|
*/
|
||||||
public void setGradient( double centerX, double centerY, double radius, Color from, Color to ) {
|
public void setGradient( double centerX, double centerY, double radius, Color from, Color to ) {
|
||||||
this.color = from;
|
this.color = from;
|
||||||
background = new RadialGradientPaint(
|
background = new RadialGradientPaint(
|
||||||
@@ -138,6 +212,9 @@ public class ColorLayer extends Layer {
|
|||||||
clear();
|
clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet den Hintergrund der Ebene mit der gesetzten Füllung neu.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void clear() {
|
public void clear() {
|
||||||
drawing.setPaint(background);
|
drawing.setPaint(background);
|
||||||
|
|||||||
@@ -87,9 +87,6 @@ public class DrawableLayer extends Layer {
|
|||||||
this.clearBeforeDraw = pClearBeforeDraw;
|
this.clearBeforeDraw = pClearBeforeDraw;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void draw( Graphics2D graphics ) {
|
public void draw( Graphics2D graphics ) {
|
||||||
if( clearBeforeDraw ) {
|
if( clearBeforeDraw ) {
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
package schule.ngb.zm.layers;
|
package schule.ngb.zm.layers;
|
||||||
|
|
||||||
import schule.ngb.zm.Color;
|
import schule.ngb.zm.Color;
|
||||||
import schule.ngb.zm.Layer;
|
import schule.ngb.zm.*;
|
||||||
import schule.ngb.zm.Options;
|
|
||||||
import schule.ngb.zm.Fillable;
|
|
||||||
import schule.ngb.zm.Strokeable;
|
|
||||||
import schule.ngb.zm.shapes.Text;
|
import schule.ngb.zm.shapes.Text;
|
||||||
import schule.ngb.zm.util.io.ImageLoader;
|
import schule.ngb.zm.util.io.ImageLoader;
|
||||||
|
|
||||||
@@ -66,7 +63,7 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
private final Stack<schule.ngb.zm.shapes.Text> styleStack;
|
private final Stack<schule.ngb.zm.shapes.Text> styleStack;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Erstellt eine Ebene in der Standardgröße.
|
* Erstellt eine Zeichenebene in der Standardgröße.
|
||||||
*/
|
*/
|
||||||
public DrawingLayer() {
|
public DrawingLayer() {
|
||||||
super();
|
super();
|
||||||
@@ -79,7 +76,7 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Erstellt eine Ebene mit der angegebenen Größe.
|
* Erstellt eine Zeichenebene mit der angegebenen Größe.
|
||||||
*
|
*
|
||||||
* @param width Die Breite der Ebene.
|
* @param width Die Breite der Ebene.
|
||||||
* @param height Die Höhe der Ebene.
|
* @param height Die Höhe der Ebene.
|
||||||
@@ -254,12 +251,18 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entfernt den Farbverlauf von der Form.
|
* Entfernt einen Farbverlauf von der Füllung.
|
||||||
*/
|
*/
|
||||||
public void noGradient() {
|
public void noGradient() {
|
||||||
shapeDelegate.noGradient();
|
shapeDelegate.noGradient();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt den Linienstil für Konturlinien direkt auf das angegebene
|
||||||
|
* {@code Stroke}-Objekt.
|
||||||
|
*
|
||||||
|
* @param stroke Ein {@code Stroke}-Objekt.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void setStroke( Stroke stroke ) {
|
public void setStroke( Stroke stroke ) {
|
||||||
shapeDelegate.setStroke(stroke);
|
shapeDelegate.setStroke(stroke);
|
||||||
@@ -417,57 +420,151 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
return shapeDelegate.getStroke();
|
return shapeDelegate.getStroke();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt den Standard-ANker für die Zeichenebene auf die angegebene
|
||||||
|
* Richtung.
|
||||||
|
* <p>
|
||||||
|
* Zu Beginn ist der Standardanker immer auf
|
||||||
|
* {@link Options.Direction#CENTER CENTER} gesetzt. Alle Formen werden von
|
||||||
|
* der Mitte aus gezeichnet. Wir der Anker mit {@code setAnchor(NORTHWEST)}
|
||||||
|
* beispielsweise auf {@link Options.Direction#NORTHWEST NORTHWEST} gesetzt,
|
||||||
|
* werden alle Formen von der linken oberen Ecke aus gezeichnet.
|
||||||
|
*
|
||||||
|
* @param anchor Der neue Anker.
|
||||||
|
*/
|
||||||
public void setAnchor( Options.Direction anchor ) {
|
public void setAnchor( Options.Direction anchor ) {
|
||||||
shapeDelegate.setAnchor(anchor);
|
shapeDelegate.setAnchor(anchor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Übermalt die komplette Ebene mit einem Grauwert mit der angegebenen
|
||||||
|
* Intensität. 0 entspricht schwarz, 255 entspricht weiß.
|
||||||
|
*
|
||||||
|
* @param gray Ein Grauwert zwischen 0 und 255.
|
||||||
|
* @see Color#Color(int)
|
||||||
|
*/
|
||||||
public void clear( int gray ) {
|
public void clear( int gray ) {
|
||||||
clear(gray, gray, gray, 255);
|
clear(gray, gray, gray, 255);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Übermalt die komplette Ebene mit einem Grauwert mit der angegebenen
|
||||||
|
* Intensität und dem angegebenen Transparenzwert. Der Grauwert 0 entspricht
|
||||||
|
* schwarz, 255 entspricht weiß.
|
||||||
|
*
|
||||||
|
* @param gray Ein Grauwert zwischen 0 und 255.
|
||||||
|
* @param alpha Ein Transparenzwert zwischen 0 und 255.
|
||||||
|
* @see Color#Color(int, int)
|
||||||
|
*/
|
||||||
public void clear( int gray, int alpha ) {
|
public void clear( int gray, int alpha ) {
|
||||||
clear(gray, gray, gray, alpha);
|
clear(gray, gray, gray, alpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Übermalt die komplette Ebene mit der Farbe mit den angegebenen Rot-,
|
||||||
|
* Grün- und Blauanteilen.
|
||||||
|
*
|
||||||
|
* @param red Der Rotanteil der Farbe zwischen 0 und 255.
|
||||||
|
* @param green Der Grünanteil der Farbe zwischen 0 und 255.
|
||||||
|
* @param blue Der Blauanteil der Farbe zwischen 0 und 255.
|
||||||
|
* @see Color#Color(int, int, int)
|
||||||
|
* @see <a
|
||||||
|
* href="https://de.wikipedia.org/wiki/RGB-Farbraum">https://de.wikipedia.org/wiki/RGB-Farbraum</a>
|
||||||
|
*/
|
||||||
public void clear( int red, int green, int blue ) {
|
public void clear( int red, int green, int blue ) {
|
||||||
clear(red, green, blue, 255);
|
clear(red, green, blue, 255);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Übermalt die komplette Ebene mit der Farbe mit den angegebenen Rot-,
|
||||||
|
* Grün- und Blauanteilen und dem angegebenen Transparenzwert.
|
||||||
|
*
|
||||||
|
* @param red Der Rotanteil der Farbe zwischen 0 und 255.
|
||||||
|
* @param green Der Grünanteil der Farbe zwischen 0 und 255.
|
||||||
|
* @param blue Der Blauanteil der Farbe zwischen 0 und 255.
|
||||||
|
* @param alpha Ein Transparenzwert zwischen 0 und 25
|
||||||
|
* @see Color#Color(int, int, int, int)
|
||||||
|
* @see <a
|
||||||
|
* href="https://de.wikipedia.org/wiki/RGB-Farbraum">https://de.wikipedia.org/wiki/RGB-Farbraum</a>
|
||||||
|
*/
|
||||||
public void clear( int red, int green, int blue, int alpha ) {
|
public void clear( int red, int green, int blue, int alpha ) {
|
||||||
clear(new schule.ngb.zm.Color(red, green, blue, alpha));
|
clear(new schule.ngb.zm.Color(red, green, blue, alpha));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clear( schule.ngb.zm.Color pColor ) {
|
/**
|
||||||
|
* Übermalt die komplette Ebene mit der angegebenen Farbe.
|
||||||
|
*
|
||||||
|
* @param color Die neue Füllfarbe oder {@code null}.
|
||||||
|
* @see Color
|
||||||
|
*/
|
||||||
|
public void clear( schule.ngb.zm.Color color ) {
|
||||||
/*graphics.setBackground(pColor);
|
/*graphics.setBackground(pColor);
|
||||||
graphics.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());*/
|
graphics.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());*/
|
||||||
java.awt.Color currentColor = drawing.getColor();
|
java.awt.Color currentColor = drawing.getColor();
|
||||||
pushMatrix();
|
pushMatrix();
|
||||||
resetMatrix();
|
resetMatrix();
|
||||||
drawing.setColor(pColor.getJavaColor());
|
drawing.setColor(color.getJavaColor());
|
||||||
drawing.fillRect(0, 0, buffer.getWidth(), buffer.getHeight());
|
drawing.fillRect(0, 0, buffer.getWidth(), buffer.getHeight());
|
||||||
drawing.setColor(currentColor);
|
drawing.setColor(currentColor);
|
||||||
popMatrix();
|
popMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet eine gerade Line zwischen den angegebenen Koordinaten.
|
||||||
|
*
|
||||||
|
* @param x1 Die x-Koordinate des Startpunktes.
|
||||||
|
* @param y1 Die y-Koordinate des Startpunktes.
|
||||||
|
* @param x2 Die x-Koordinate des Endpunktes.
|
||||||
|
* @param y2 Die y-Koordinate des Endpunktes.
|
||||||
|
*/
|
||||||
public void line( double x1, double y1, double x2, double y2 ) {
|
public void line( double x1, double y1, double x2, double y2 ) {
|
||||||
line.setLine(x1, y1, x2, y2);
|
line.setLine(x1, y1, x2, y2);
|
||||||
drawShape(line);
|
drawShape(line);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Färbt den Pixel an der angegebenen Koordinate ein.
|
||||||
|
*
|
||||||
|
* @param x Die x-Koordinate.
|
||||||
|
* @param y Die y-Koordinate.
|
||||||
|
*/
|
||||||
public void pixel( double x, double y ) {
|
public void pixel( double x, double y ) {
|
||||||
buffer.setRGB((int) x, (int) y, shapeDelegate.getFontColor().getRGBA());
|
buffer.setRGB((int) x, (int) y, shapeDelegate.getFontColor().getRGBA());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet ein Quadrat an den angegebenen Koordinaten mit der angegebenen
|
||||||
|
* Kantenlänge in die Zeichenebene.
|
||||||
|
* <p>
|
||||||
|
* Als Ankerpunkt wird der Standardanker verwendet.
|
||||||
|
*
|
||||||
|
* @param x Die x-Koordinate des Ankerpunktes.
|
||||||
|
* @param y Die y-Koordinate des Ankerpunktes.
|
||||||
|
* @param w Die Kantenlänge des Quadrats.
|
||||||
|
* @see #square(double, double, double, Options.Direction)
|
||||||
|
*/
|
||||||
public void square( double x, double y, double w ) {
|
public void square( double x, double y, double w ) {
|
||||||
rect(x, y, w, w, shapeDelegate.getAnchor());
|
rect(x, y, w, w, shapeDelegate.getAnchor());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet ein Quadrat an den angegebenen Koordinaten mit der angegebenen
|
||||||
|
* Kantenlänge und dem angegebenen Ankerpunkt in die Zeichenebene.
|
||||||
|
*
|
||||||
|
* @param x Die x-Koordinate des Ankerpunktes.
|
||||||
|
* @param y Die y-Koordinate des Ankerpunktes.
|
||||||
|
* @param w Die Kantenlänge des Quadrats.
|
||||||
|
* @see #square(double, double, double, Options.Direction)
|
||||||
|
*/
|
||||||
public void square( double x, double y, double w, Options.Direction anchor ) {
|
public void square( double x, double y, double w, Options.Direction anchor ) {
|
||||||
rect(x, y, w, w, anchor);
|
rect(x, y, w, w, anchor);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Zeichnet ein Rechteck in die Zeichenebene.
|
* Zeichnet ein Rechteck an den angegebenen Koordinaten mit der angegebenen
|
||||||
|
* Breite und Höhe in die Zeichenebene.
|
||||||
|
* <p>
|
||||||
|
* Als Ankerpunkt wird der Standardanker verwendet.
|
||||||
*
|
*
|
||||||
* @param x x-Koordinate des Rechtecks.
|
* @param x x-Koordinate des Rechtecks.
|
||||||
* @param y y-Koordinate des Rechtecks.
|
* @param y y-Koordinate des Rechtecks.
|
||||||
@@ -479,7 +576,8 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Zeichnet ein Rechteck mit in die Zeichenebene.
|
* Zeichnet ein Rechteck an den angegebenen Koordinaten mit der angegebenen
|
||||||
|
* Breite und Höhe und dem angegebenen Ankerpunkt in die Zeichenebene.
|
||||||
*
|
*
|
||||||
* @param x x-Koordinate des Rechtecks.
|
* @param x x-Koordinate des Rechtecks.
|
||||||
* @param y y-Koordinate des Rechtecks.
|
* @param y y-Koordinate des Rechtecks.
|
||||||
@@ -495,7 +593,8 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Zeichnet ein Rechteck mit abgerundeten Ecken in die Zeichenebene.
|
* Zeichnet ein Rechteck mit abgerundeten Ecken an den angegebenen
|
||||||
|
* Koordinaten mit der angegebenen Breite und Höhe in die Zeichenebene.
|
||||||
* <p>
|
* <p>
|
||||||
* Jede Ecke wird als Viertelkreis eines Kreises mit dem Radius
|
* Jede Ecke wird als Viertelkreis eines Kreises mit dem Radius
|
||||||
* {@code radius} gezeichnet.
|
* {@code radius} gezeichnet.
|
||||||
@@ -511,7 +610,9 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Zeichnet ein Rechteck mit abgerundeten Ecken in die Zeichenebene.
|
* Zeichnet ein Rechteck mit abgerundeten Ecken an den angegebenen
|
||||||
|
* Koordinaten mit der angegebenen Breite und Höhe und dem angegebenen
|
||||||
|
* Ankerpunkt in die Zeichenebene.
|
||||||
* <p>
|
* <p>
|
||||||
* Jede Ecke wird als Viertelkreis eines Kreises mit dem Radius
|
* Jede Ecke wird als Viertelkreis eines Kreises mit dem Radius
|
||||||
* {@code radius} gezeichnet.
|
* {@code radius} gezeichnet.
|
||||||
@@ -530,22 +631,64 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
drawShape(rect);
|
drawShape(rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet einen Punkt in die Zeichenebene.
|
||||||
|
* <p>
|
||||||
|
* Im Gegensatz zu einem {@link #pixel(double, double) Pixel}, besitzt ein
|
||||||
|
* Punkt einen Radius und nimmt mehr als ein Pixel auf der Ebene ein.
|
||||||
|
*
|
||||||
|
* @param x x-Koordinate des Punktes.
|
||||||
|
* @param y y-Koordinate des Punktes.
|
||||||
|
*/
|
||||||
public void point( double x, double y ) {
|
public void point( double x, double y ) {
|
||||||
ellipse(x - 1, y - 1, 2, 2, shapeDelegate.getAnchor());
|
ellipse(x - 1, y - 1, 2, 2, shapeDelegate.getAnchor());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet einen Kreis an den angegebenen Koordinaten mit dem angegebenen
|
||||||
|
* Radius.
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* @param y
|
||||||
|
* @param r
|
||||||
|
*/
|
||||||
public void circle( double x, double y, double r ) {
|
public void circle( double x, double y, double r ) {
|
||||||
ellipse(x, y, r + r, r + r, shapeDelegate.getAnchor());
|
ellipse(x, y, r + r, r + r, shapeDelegate.getAnchor());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet einen Kreis an den angegebenen Koordinaten mit dem angegebenen
|
||||||
|
* Radius und dem angegebenen Ankerpunkt.
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* @param y
|
||||||
|
* @param r
|
||||||
|
*/
|
||||||
public void circle( double x, double y, double r, Options.Direction anchor ) {
|
public void circle( double x, double y, double r, Options.Direction anchor ) {
|
||||||
ellipse(x, y, r + r, r + r, anchor);
|
ellipse(x, y, r + r, r + r, anchor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet eine Ellipse in die Zeichenebene.
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* @param y
|
||||||
|
* @param w
|
||||||
|
* @param h
|
||||||
|
*/
|
||||||
public void ellipse( double x, double y, double w, double h ) {
|
public void ellipse( double x, double y, double w, double h ) {
|
||||||
ellipse(x, y, w, h, shapeDelegate.getAnchor());
|
ellipse(x, y, w, h, shapeDelegate.getAnchor());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet eine Ellipse in die Zeichenebene.
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* @param y
|
||||||
|
* @param w
|
||||||
|
* @param h
|
||||||
|
* @param anchor
|
||||||
|
*/
|
||||||
public void ellipse( double x, double y, double w, double h, Options.Direction anchor ) {
|
public void ellipse( double x, double y, double w, double h, Options.Direction anchor ) {
|
||||||
Point2D.Double anchorPoint = getOriginPoint(x, y, w, h, anchor);
|
Point2D.Double anchorPoint = getOriginPoint(x, y, w, h, anchor);
|
||||||
ellipse.setFrame(anchorPoint.x, anchorPoint.y, w, h);
|
ellipse.setFrame(anchorPoint.x, anchorPoint.y, w, h);
|
||||||
@@ -553,10 +696,35 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
drawShape(ellipse);
|
drawShape(ellipse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet einen Kreisbogen in die Zeichenebene.
|
||||||
|
* <p>
|
||||||
|
* Der Kreisbogen liegt auf einem Kreis mit dem angegebenen Radius, beginnt
|
||||||
|
* beim angegebenen Winkel und läuft bis zum zweiten Winkel.
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* @param y
|
||||||
|
* @param r
|
||||||
|
* @param angle1
|
||||||
|
* @param angle2
|
||||||
|
*/
|
||||||
public void arc( double x, double y, double r, double angle1, double angle2 ) {
|
public void arc( double x, double y, double r, double angle1, double angle2 ) {
|
||||||
arc(x, y, r + r, r + r, angle1, angle2);
|
arc(x, y, r + r, r + r, angle1, angle2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet einen Ellipsenbogen in die Zeichenebene.
|
||||||
|
* <p>
|
||||||
|
* Der Ellipsenbogen liegt auf einer Ellipse mit der angegebenen Breite und
|
||||||
|
* Höhe, beginnt beim angegebenen Winkel und läuft bis zum zweiten Winkel.
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* @param y
|
||||||
|
* @param w
|
||||||
|
* @param h
|
||||||
|
* @param angle1
|
||||||
|
* @param angle2
|
||||||
|
*/
|
||||||
public void arc( double x, double y, double w, double h, double angle1, double angle2 ) {
|
public void arc( double x, double y, double w, double h, double angle1, double angle2 ) {
|
||||||
while( angle2 < angle1 ) angle2 += 360.0;
|
while( angle2 < angle1 ) angle2 += 360.0;
|
||||||
|
|
||||||
@@ -572,6 +740,15 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
drawShape(arc);
|
drawShape(arc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet einen Kreisausschnitt in die Zeichenebene.
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* @param y
|
||||||
|
* @param r
|
||||||
|
* @param angle1
|
||||||
|
* @param angle2
|
||||||
|
*/
|
||||||
public void pie( double x, double y, double r, double angle1, double angle2 ) {
|
public void pie( double x, double y, double r, double angle1, double angle2 ) {
|
||||||
while( angle2 < angle1 ) angle2 += 360.0;
|
while( angle2 < angle1 ) angle2 += 360.0;
|
||||||
|
|
||||||
@@ -589,16 +766,50 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
drawShape(arc);
|
drawShape(arc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet eine quadratische Bézierkurve mit den angegebenen Start- und
|
||||||
|
* Endkoordinaten, sowie dem angegebenen Kontrollpunkt.
|
||||||
|
*
|
||||||
|
* @param x1
|
||||||
|
* @param y1
|
||||||
|
* @param x2
|
||||||
|
* @param y2
|
||||||
|
* @param x3
|
||||||
|
* @param y3
|
||||||
|
*/
|
||||||
public void curve( double x1, double y1, double x2, double y2, double x3, double y3 ) {
|
public void curve( double x1, double y1, double x2, double y2, double x3, double y3 ) {
|
||||||
QuadCurve2D curve = new QuadCurve2D.Double(x1, y1, x2, y2, x3, y3);
|
QuadCurve2D curve = new QuadCurve2D.Double(x1, y1, x2, y2, x3, y3);
|
||||||
drawShape(curve);
|
drawShape(curve);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet eine kubische Bézierkurve mit den angegebenen Start- und
|
||||||
|
* Endkoordinaten, sowie den angegebenen Kontrollpunkten.
|
||||||
|
*
|
||||||
|
* @param x1
|
||||||
|
* @param y1
|
||||||
|
* @param x2
|
||||||
|
* @param y2
|
||||||
|
* @param x3
|
||||||
|
* @param y3
|
||||||
|
* @param x4
|
||||||
|
* @param y4
|
||||||
|
*/
|
||||||
public void curve( double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4 ) {
|
public void curve( double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4 ) {
|
||||||
CubicCurve2D curve = new CubicCurve2D.Double(x1, y1, x2, y2, x3, y3, x4, y4);
|
CubicCurve2D curve = new CubicCurve2D.Double(x1, y1, x2, y2, x3, y3, x4, y4);
|
||||||
drawShape(curve);
|
drawShape(curve);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet ein Dreieck mit den angegebenen Eckpunkten in die Zeichenebene.
|
||||||
|
*
|
||||||
|
* @param x1
|
||||||
|
* @param y1
|
||||||
|
* @param x2
|
||||||
|
* @param y2
|
||||||
|
* @param x3
|
||||||
|
* @param y3
|
||||||
|
*/
|
||||||
public void triangle( double x1, double y1, double x2, double y2, double x3, double y3 ) {
|
public void triangle( double x1, double y1, double x2, double y2, double x3, double y3 ) {
|
||||||
Path2D path = new Path2D.Double();
|
Path2D path = new Path2D.Double();
|
||||||
path.moveTo(x1, y1);
|
path.moveTo(x1, y1);
|
||||||
@@ -610,6 +821,14 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
drawShape(path);
|
drawShape(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet einen Rhombus in die Zeichenebene.
|
||||||
|
*
|
||||||
|
* @param x
|
||||||
|
* @param y
|
||||||
|
* @param width
|
||||||
|
* @param height
|
||||||
|
*/
|
||||||
public void rhombus( double x, double y, double width, double height ) {
|
public void rhombus( double x, double y, double width, double height ) {
|
||||||
rhombus(x, y, width, height, shapeDelegate.getAnchor());
|
rhombus(x, y, width, height, shapeDelegate.getAnchor());
|
||||||
}
|
}
|
||||||
@@ -677,11 +896,24 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Startet eine neue Freihand-Form.
|
||||||
|
*/
|
||||||
public void beginShape() {
|
public void beginShape() {
|
||||||
path.reset();
|
path.reset();
|
||||||
pathStarted = false;
|
pathStarted = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fügt einer zuvor {@link #beginShape() begonnenen} Freihand-Form eine
|
||||||
|
* Linie zu den angegebenen Koordinaten hinzu.
|
||||||
|
* <p>
|
||||||
|
* Wurde seit dem Start der Form noch keine Linie hinzugefügt, wird zunächst
|
||||||
|
* nur die angegebene Koordinate als Startpunkt der Freihand-Form gesetzt.
|
||||||
|
*
|
||||||
|
* @param x Die x-Koordinate.
|
||||||
|
* @param y Die y-Koordinate.
|
||||||
|
*/
|
||||||
public void lineTo( double x, double y ) {
|
public void lineTo( double x, double y ) {
|
||||||
if( !pathStarted ) {
|
if( !pathStarted ) {
|
||||||
path.moveTo(x, y);
|
path.moveTo(x, y);
|
||||||
@@ -691,6 +923,10 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Beendet eine zuvor {@link #beginShape() begonnene} Freihand-Form und
|
||||||
|
* zeichent sie auf die Zeichenebene.
|
||||||
|
*/
|
||||||
public void endShape() {
|
public void endShape() {
|
||||||
path.closePath();
|
path.closePath();
|
||||||
path.trimToSize();
|
path.trimToSize();
|
||||||
@@ -700,72 +936,339 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
drawShape(path);
|
drawShape(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet das Bild von der angegebenen Quelle an der angegebenen Position
|
||||||
|
* auf die Zeichenebene.
|
||||||
|
* <p>
|
||||||
|
* Die Bildquelle wird mithilfe von {@link ImageLoader#loadImage(String)}
|
||||||
|
* geladen. Schlägt dies fehl, wird nichts gezeichnet.
|
||||||
|
*
|
||||||
|
* @param imageSource Die Bildquelle.
|
||||||
|
* @param x x-Koordinate des Ankerpunktes.
|
||||||
|
* @param y y-Koordinate des Ankerpunktes.
|
||||||
|
* @see ImageLoader#loadImage(String)
|
||||||
|
*/
|
||||||
public void image( String imageSource, double x, double y ) {
|
public void image( String imageSource, double x, double y ) {
|
||||||
image(ImageLoader.loadImage(imageSource), x, y, 1.0, shapeDelegate.getAnchor());
|
image(ImageLoader.loadImage(imageSource), x, y, 1.0, shapeDelegate.getAnchor());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet das Bild von der angegebenen Quelle an der angegebenen Position
|
||||||
|
* auf die Zeichenebene.
|
||||||
|
* <p>
|
||||||
|
* Die Bildquelle wird mithilfe von {@link ImageLoader#loadImage(String)}
|
||||||
|
* geladen. Schlägt dies fehl, wird nichts gezeichnet.
|
||||||
|
*
|
||||||
|
* @param imageSource Die Bildquelle.
|
||||||
|
* @param x x-Koordinate des Ankerpunktes.
|
||||||
|
* @param y y-Koordinate des Ankerpunktes.
|
||||||
|
* @param anchor Der Ankerpunkt.
|
||||||
|
* @see ImageLoader#loadImage(String)
|
||||||
|
*/
|
||||||
public void image( String imageSource, double x, double y, Options.Direction anchor ) {
|
public void image( String imageSource, double x, double y, Options.Direction anchor ) {
|
||||||
image(ImageLoader.loadImage(imageSource), x, y, 1.0, anchor);
|
image(ImageLoader.loadImage(imageSource), x, y, 1.0, anchor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet das Bild von der angegebenen Quelle an den angegebenen
|
||||||
|
* Koordinaten auf die Zeichenebene. Das Bild wird um den angegebenen Faktor
|
||||||
|
* skaliert.
|
||||||
|
* <p>
|
||||||
|
* Siehe {@link #image(Image, double, double, double, Options.Direction)}
|
||||||
|
* für mehr Details.
|
||||||
|
*
|
||||||
|
* @param imageSource Die Bildquelle.
|
||||||
|
* @param x x-Koordinate des Ankerpunktes.
|
||||||
|
* @param y y-Koordinate des Ankerpunktes.
|
||||||
|
* @param scale Der Skalierungsfaktor des Bildes.
|
||||||
|
* @see ImageLoader#loadImage(String)
|
||||||
|
*/
|
||||||
public void image( String imageSource, double x, double y, double scale ) {
|
public void image( String imageSource, double x, double y, double scale ) {
|
||||||
image(ImageLoader.loadImage(imageSource), x, y, scale, shapeDelegate.getAnchor());
|
image(ImageLoader.loadImage(imageSource), x, y, scale, shapeDelegate.getAnchor());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet das Bild von der angegebenen Quelle an den angegebenen
|
||||||
|
* Koordinaten auf die Zeichenebene. Das Bild wird um den angegebenen Faktor
|
||||||
|
* skaliert und der angegebene Ankerpunkt verwendet.
|
||||||
|
* <p>
|
||||||
|
* Siehe {@link #image(Image, double, double, double, Options.Direction)}
|
||||||
|
* für mehr Details.
|
||||||
|
*
|
||||||
|
* @param imageSource Die Bildquelle.
|
||||||
|
* @param x x-Koordinate des Ankerpunktes.
|
||||||
|
* @param y y-Koordinate des Ankerpunktes.
|
||||||
|
* @param scale Der Skalierungsfaktor des Bildes.
|
||||||
|
* @param anchor Der Ankerpunkt.
|
||||||
|
* @see ImageLoader#loadImage(String)
|
||||||
|
*/
|
||||||
public void image( String imageSource, double x, double y, double scale, Options.Direction anchor ) {
|
public void image( String imageSource, double x, double y, double scale, Options.Direction anchor ) {
|
||||||
image(ImageLoader.loadImage(imageSource), x, y, scale, anchor);
|
image(ImageLoader.loadImage(imageSource), x, y, scale, anchor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet das angegebene Bild an den angegebenen Koordinaten auf die
|
||||||
|
* Zeichenebene.
|
||||||
|
*
|
||||||
|
* @param image Das vorher geladene Bild.
|
||||||
|
* @param x x-Koordinate des Ankerpunktes.
|
||||||
|
* @param y y-Koordinate des Ankerpunktes.
|
||||||
|
*/
|
||||||
public void image( Image image, double x, double y ) {
|
public void image( Image image, double x, double y ) {
|
||||||
image(image, x, y, 1.0, shapeDelegate.getAnchor());
|
image(image, x, y, 1.0, shapeDelegate.getAnchor());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet das angegebene Bild an den angegebenen Koordinaten auf die
|
||||||
|
* Zeichenebene. Das Bild wird um den angegebenen Faktor skaliert.
|
||||||
|
* <p>
|
||||||
|
* Siehe {@link #image(Image, double, double, double, Options.Direction)}
|
||||||
|
* für mehr Details.
|
||||||
|
*
|
||||||
|
* @param image Das vorher geladene Bild.
|
||||||
|
* @param x x-Koordinate des Ankerpunktes.
|
||||||
|
* @param y y-Koordinate des Ankerpunktes.
|
||||||
|
* @param scale Der Skalierungsfaktor des Bildes.
|
||||||
|
*/
|
||||||
public void image( Image image, double x, double y, double scale ) {
|
public void image( Image image, double x, double y, double scale ) {
|
||||||
image(image, x, y, scale, shapeDelegate.getAnchor());
|
image(image, x, y, scale, shapeDelegate.getAnchor());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet das angegebene Bild an den angegebenen Koordinaten auf die
|
||||||
|
* Zeichenebene. Das Bild wird um den angegebenen Faktor skaliert und der
|
||||||
|
* angegebene Ankerpunkt verwendet.
|
||||||
|
* <p>
|
||||||
|
* Bei einem Faktor größer 0 wird das Bild vergrößert, bei einem Faktor
|
||||||
|
* kleiner 0 verkleinert. Bei negativen Werten wird das Bild entlang der x-
|
||||||
|
* bzw. y-Achse gespiegelt.
|
||||||
|
* <p>
|
||||||
|
* Das Seitenverhältnis wird immer beibehalten.
|
||||||
|
* <p>
|
||||||
|
* Soll das Bild innerhalb eines vorgegebenen Rechtecks liegen, sollte
|
||||||
|
* {@link #image(Image, double, double, double, double, Options.Direction)}
|
||||||
|
* verwendet werden.
|
||||||
|
*
|
||||||
|
* @param image Das vorher geladene Bild.
|
||||||
|
* @param x x-Koordinate des Ankerpunktes.
|
||||||
|
* @param y y-Koordinate des Ankerpunktes.
|
||||||
|
* @param scale Der Skalierungsfaktor des Bildes.
|
||||||
|
* @param anchor Der Ankerpunkt.
|
||||||
|
*/
|
||||||
public void image( Image image, double x, double y, double scale, Options.Direction anchor ) {
|
public void image( Image image, double x, double y, double scale, Options.Direction anchor ) {
|
||||||
if( image != null ) {
|
/*if( image != null ) {
|
||||||
double neww = image.getWidth(null) * scale;
|
double neww = image.getWidth(null) * scale;
|
||||||
double newh = image.getHeight(null) * scale;
|
double newh = image.getHeight(null) * scale;
|
||||||
Point2D.Double anchorPoint = getOriginPoint(x, y, neww, newh, anchor);
|
Point2D.Double anchorPoint = getOriginPoint(x, y, neww, newh, anchor);
|
||||||
drawing.drawImage(image, (int) anchorPoint.x, (int) anchorPoint.y, (int) neww, (int) newh, null);
|
drawing.drawImage(image, (int) anchorPoint.x, (int) anchorPoint.y, (int) neww, (int) newh, null);
|
||||||
|
}*/
|
||||||
|
double neww = image.getWidth(null) * scale;
|
||||||
|
double newh = image.getHeight(null) * scale;
|
||||||
|
image(image, x, y, neww, newh, anchor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet das Bild von der angegebenen Quelle an den angegebenen
|
||||||
|
* Koordinaten in der angegebenen Größe auf die Zeichenebene.
|
||||||
|
* <p>
|
||||||
|
* Siehe
|
||||||
|
* {@link #image(Image, double, double, double, double, Options.Direction)}
|
||||||
|
* für mehr Details.
|
||||||
|
*
|
||||||
|
* @param imageSource Die Bildquelle.
|
||||||
|
* @param x x-Koordinate des Ankerpunktes.
|
||||||
|
* @param y y-Koordinate des Ankerpunktes.
|
||||||
|
* @param width Breite des Bildes auf der Zeichenebene oder 0.
|
||||||
|
* @param height Höhe des Bildes auf der Zeichenebene oder 0.
|
||||||
|
* @see ImageLoader#loadImage(String)
|
||||||
|
*/
|
||||||
|
public void image( String imageSource, double x, double y, double width, double height ) {
|
||||||
|
image(ImageLoader.loadImage(imageSource), x, y, width, height, shapeDelegate.getAnchor());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet das Bild von der angegebenen Quelle an den angegebenen
|
||||||
|
* Koordinaten in der angegebenen Größe auf die Zeichenebene. Es wird der
|
||||||
|
* angegebene Ankerpunkt verwendet.
|
||||||
|
* <p>
|
||||||
|
* Siehe
|
||||||
|
* {@link #image(Image, double, double, double, double, Options.Direction)}
|
||||||
|
* für mehr Details.
|
||||||
|
*
|
||||||
|
* @param imageSource Die Bildquelle.
|
||||||
|
* @param x x-Koordinate des Ankerpunktes.
|
||||||
|
* @param y y-Koordinate des Ankerpunktes.
|
||||||
|
* @param width Breite des Bildes auf der Zeichenebene oder 0.
|
||||||
|
* @param height Höhe des Bildes auf der Zeichenebene oder 0.
|
||||||
|
* @param anchor Der Ankerpunkt.
|
||||||
|
* @see ImageLoader#loadImage(String)
|
||||||
|
*/
|
||||||
|
public void image( String imageSource, double x, double y, double width, double height, Options.Direction anchor ) {
|
||||||
|
image(ImageLoader.loadImage(imageSource), x, y, width, height, anchor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet das angegebene Bild an den angegebenen Koordinaten in der
|
||||||
|
* angegebenen Größe auf die Zeichenebene.
|
||||||
|
* <p>
|
||||||
|
* Siehe
|
||||||
|
* {@link #image(Image, double, double, double, double, Options.Direction)}
|
||||||
|
* für mehr Details.
|
||||||
|
*
|
||||||
|
* @param image Ein Bild-Objekt.
|
||||||
|
* @param x x-Koordinate des Ankerpunktes.
|
||||||
|
* @param y y-Koordinate des Ankerpunktes.
|
||||||
|
* @param width Breite des Bildes auf der Zeichenebene oder 0.
|
||||||
|
* @param height Höhe des Bildes auf der Zeichenebene oder 0.
|
||||||
|
*/
|
||||||
|
public void image( Image image, double x, double y, double width, double height ) {
|
||||||
|
image(image, x, y, width, height, shapeDelegate.getAnchor());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zeichnet das angegebene Bild an den angegebenen Koordinaten in der
|
||||||
|
* angegebenen Größe auf die Zeichenebene. Der angegebene Ankerpunkt wird
|
||||||
|
* verwendet.
|
||||||
|
* <p>
|
||||||
|
* Das Bild wird innerhalb eines Rechtecks mit der angegebenen Breite und
|
||||||
|
* Höhe gezeichnet. Dabei wird das Abbild verzerrt, wenn beim Aufruf nicht
|
||||||
|
* auf ein passendes Seitenverhältnis der Werte zueinander geachtet wird.
|
||||||
|
* <p>
|
||||||
|
* Um das Bild auf eine bestimmte Breite oder Höhe festzulegen und die
|
||||||
|
* andere Größe passend zu skalieren, kann einer der Parameter auf 0 gesetzt
|
||||||
|
* werden.
|
||||||
|
* <p>
|
||||||
|
* Soll die Bildgröße unter Beachtung der Abmessungen um einen Faktor
|
||||||
|
* verändert werden, sollte
|
||||||
|
* {@link #image(Image, double, double, double, Options.Direction)}
|
||||||
|
* verwendet werden.
|
||||||
|
*
|
||||||
|
* @param image Ein Bild-Objekt.
|
||||||
|
* @param x x-Koordinate des Ankerpunktes.
|
||||||
|
* @param y y-Koordinate des Ankerpunktes.
|
||||||
|
* @param width Breite des Bildes auf der Zeichenebene oder 0.
|
||||||
|
* @param height Höhe des Bildes auf der Zeichenebene oder 0.
|
||||||
|
* @param anchor Der Ankerpunkt.
|
||||||
|
*/
|
||||||
|
public void image( Image image, double x, double y, double width, double height, Options.Direction anchor ) {
|
||||||
|
// TODO: Use Validator or at least LOG a message if image == null?
|
||||||
|
if( image != null ) {
|
||||||
|
if( width == 0 ) {
|
||||||
|
width = (height / image.getHeight(null)) * image.getWidth(null);
|
||||||
|
} else if( height == 0 ) {
|
||||||
|
height = (width / image.getWidth(null)) * image.getHeight(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
Point2D.Double anchorPoint = getOriginPoint(x, y, width, height, anchor);
|
||||||
|
drawing.drawImage(image, (int) anchorPoint.x, (int) anchorPoint.y, (int) width, (int) height, null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Die aktuell verwendete Schriftart.
|
||||||
|
*/
|
||||||
public Font getFont() {
|
public Font getFont() {
|
||||||
return shapeDelegate.getFont();
|
return shapeDelegate.getFont();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt die verwendete Schriftart für Texte direkt auf das angegebene
|
||||||
|
* {@code Font}-Objekt.
|
||||||
|
*
|
||||||
|
* @param newFont Ein {@code Font}-Objekt.
|
||||||
|
*/
|
||||||
public void setFont( Font newFont ) {
|
public void setFont( Font newFont ) {
|
||||||
shapeDelegate.setFont(newFont);
|
shapeDelegate.setFont(newFont);
|
||||||
// fontMetrics = drawing.getFontMetrics();
|
// fontMetrics = drawing.getFontMetrics();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt die verwendete Schriftart für Texte auf eine Schriftart mit dem
|
||||||
|
* angegebenen Namen.
|
||||||
|
* <p>
|
||||||
|
* Kann keine Schriftart mit einem passenden Namen geladen werden, wird die
|
||||||
|
* Schriftart nicht geändert.
|
||||||
|
*
|
||||||
|
* @param fontName Der Name der Schriftart.
|
||||||
|
* @see schule.ngb.zm.util.io.FontLoader#loadFont(String)
|
||||||
|
*/
|
||||||
public void setFont( String fontName ) {
|
public void setFont( String fontName ) {
|
||||||
shapeDelegate.setFont(fontName);
|
shapeDelegate.setFont(fontName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt die verwendete Schriftart für Texte auf eine Schriftart mit dem
|
||||||
|
* angegebenen Namen und der angegebenen Schriftgröße.
|
||||||
|
* <p>
|
||||||
|
* Kann keine Schriftart mit einem passenden Namen geladen werden, wird die
|
||||||
|
* Schriftart nicht geändert.
|
||||||
|
*
|
||||||
|
* @param fontName Der Name der Schriftart.
|
||||||
|
* @param size Die Schriftgröße.
|
||||||
|
* @see schule.ngb.zm.util.io.FontLoader#loadFont(String)
|
||||||
|
*/
|
||||||
public void setFont( String fontName, int size ) {
|
public void setFont( String fontName, int size ) {
|
||||||
shapeDelegate.setFont(fontName, size);
|
shapeDelegate.setFont(fontName, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt die verwendete Schriftart für Texte auf eine Schriftart mit dem
|
||||||
|
* angegebenen Namen, der angegebenen Schriftgröße und dem angegebenen
|
||||||
|
* Schriftstil.
|
||||||
|
* <p>
|
||||||
|
* Der Schriftstil kann über die Stil-Konstanten
|
||||||
|
* {@link schule.ngb.zm.Constants#PLAIN PLAIN},
|
||||||
|
* {@link schule.ngb.zm.Constants#BOLD BOLD} und
|
||||||
|
* {@link schule.ngb.zm.Constants#ITALIC ITALIC} gesetzt werden.
|
||||||
|
* <p>
|
||||||
|
* Kann keine Schriftart mit einem passenden Namen geladen werden, wird die
|
||||||
|
* Schriftart nicht geändert.
|
||||||
|
*
|
||||||
|
* @param fontName Der Name der Schriftart.
|
||||||
|
* @param size Die Schriftgröße.
|
||||||
|
* @param style Der Schriftstil.
|
||||||
|
* @see schule.ngb.zm.util.io.FontLoader#loadFont(String)
|
||||||
|
*/
|
||||||
public void setFont( String fontName, int size, int style ) {
|
public void setFont( String fontName, int size, int style ) {
|
||||||
shapeDelegate.setFont(fontName, size, style);
|
shapeDelegate.setFont(fontName, size, style);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Die aktuelle Schriftgröße für Texte.
|
||||||
|
*/
|
||||||
public double getFontSize() {
|
public double getFontSize() {
|
||||||
return shapeDelegate.getFontSize();
|
return shapeDelegate.getFontSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt die Schriftgörße für Texte auf den angegebenen Wert.
|
||||||
|
*
|
||||||
|
* @param size Die neue Schriftgröße.
|
||||||
|
*/
|
||||||
public void setFontSize( double size ) {
|
public void setFontSize( double size ) {
|
||||||
shapeDelegate.setFontSize(size);
|
shapeDelegate.setFontSize(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schreibt einen Text an der angegebenen Position auf die Zeichenebene.
|
||||||
|
*
|
||||||
|
* @param text Der zu schreibende Text.
|
||||||
|
* @param x Die x-Koordinate des Ankerpunktes.
|
||||||
|
* @param y Die y-Koordinate des Ankerpunktes.
|
||||||
|
*/
|
||||||
public void text( String text, double x, double y ) {
|
public void text( String text, double x, double y ) {
|
||||||
text(text, x, y, shapeDelegate.getAnchor());
|
text(text, x, y, shapeDelegate.getAnchor());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Schreibt einen Text an der angegebenen Position auf die Zeichenebene und
|
||||||
|
* nutzt den angegebenen Ankerpunkt.
|
||||||
|
*
|
||||||
|
* @param text Der zu schreibende Text.
|
||||||
|
* @param x Die x-Koordinate des Ankerpunktes.
|
||||||
|
* @param y Die y-Koordinate des Ankerpunktes.
|
||||||
|
* @param anchor Position des Ankerpunktes.
|
||||||
|
*/
|
||||||
public void text( String text, double x, double y, Options.Direction anchor ) {
|
public void text( String text, double x, double y, Options.Direction anchor ) {
|
||||||
drawing.setFont(shapeDelegate.getFont());
|
drawing.setFont(shapeDelegate.getFont());
|
||||||
FontMetrics fm = drawing.getFontMetrics();
|
FontMetrics fm = drawing.getFontMetrics();
|
||||||
@@ -782,7 +1285,7 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
* Koordinaten {@code (300, 300)} und den Abmessungen {@code (100, 50)}
|
* Koordinaten {@code (300, 300)} und den Abmessungen {@code (100, 50)}
|
||||||
* würde für den Anker {@link Options.Direction#NORTHWEST} also den Ursprung
|
* würde für den Anker {@link Options.Direction#NORTHWEST} also den Ursprung
|
||||||
* {@code (300, 300)} haben. Für den Anker {@link Options.Direction#CENTER}
|
* {@code (300, 300)} haben. Für den Anker {@link Options.Direction#CENTER}
|
||||||
* wäre der Ursprung zu den Koordinaten um die hälfte der Abmessungen nach
|
* wäre der Ursprung zu den Koordinaten um die Hälfte der Abmessungen nach
|
||||||
* links und oben verschoben bei {@code (250, 275)}. Beim Zeichnen liegen
|
* links und oben verschoben bei {@code (250, 275)}. Beim Zeichnen liegen
|
||||||
* die Koordinaten {@code (300, 300)} dann in der Mitte der Form.
|
* die Koordinaten {@code (300, 300)} dann in der Mitte der Form.
|
||||||
*
|
*
|
||||||
@@ -800,11 +1303,20 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
return ap;
|
return ap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Speichert die aktuellen Einstellungen für Farben, Konturlinie und
|
||||||
|
* Schriftarten ab, sodass sie zu einem späteren Zeitpunkt mit
|
||||||
|
* {@link #popStyle()} wiederhergestellt werden können.
|
||||||
|
*/
|
||||||
public void pushStyle() {
|
public void pushStyle() {
|
||||||
styleStack.push(shapeDelegate);
|
styleStack.push(shapeDelegate);
|
||||||
shapeDelegate = new Text(shapeDelegate);
|
shapeDelegate = new Text(shapeDelegate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stellt die zuletzt mit {@link #pushStyle()} gespeicherten Einstellungen
|
||||||
|
* für Farben, Konturlinien und Texte wieder her.
|
||||||
|
*/
|
||||||
public void popStyle() {
|
public void popStyle() {
|
||||||
if( styleStack.isEmpty() ) {
|
if( styleStack.isEmpty() ) {
|
||||||
resetStyle();
|
resetStyle();
|
||||||
@@ -813,6 +1325,10 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt alle Einstellungen für Farben, Konturlinien und Texte auf die
|
||||||
|
* Standardwerte.
|
||||||
|
*/
|
||||||
public void resetStyle() {
|
public void resetStyle() {
|
||||||
Text newDelegate = new Text(0, 0, "");
|
Text newDelegate = new Text(0, 0, "");
|
||||||
newDelegate.setFillColor(DEFAULT_FILLCOLOR);
|
newDelegate.setFillColor(DEFAULT_FILLCOLOR);
|
||||||
@@ -823,34 +1339,92 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
shapeDelegate = newDelegate;
|
shapeDelegate = newDelegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verschiebt den Ursprung der Zeichenebene um die angegebenen Werte entlang
|
||||||
|
* der x- und y-Achse.
|
||||||
|
*
|
||||||
|
* @param dx Verschiebung entlang der x-Achse.
|
||||||
|
* @param dy Verschiebung entlang der y-Achse.
|
||||||
|
*/
|
||||||
public void translate( double dx, double dy ) {
|
public void translate( double dx, double dy ) {
|
||||||
drawing.translate(dx, dy);
|
drawing.translate(dx, dy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Skaliert die Zeichenebene um den angegebenen Faktor.
|
||||||
|
* <p>
|
||||||
|
* Ein Abstand von zehn Einheiten wird bei einem Faktor von 2.0 zu einem
|
||||||
|
* Abstand von 20 Einheiten. Bei einem Faktor von 0.5 zu fünf Einheiten.
|
||||||
|
* <p>
|
||||||
|
* Der Skalierungsfaktor wird entlang der x- und y-Achse gleich angewandt.
|
||||||
|
*
|
||||||
|
* @param factor Der Skalierungsfaktor.
|
||||||
|
*/
|
||||||
public void scale( double factor ) {
|
public void scale( double factor ) {
|
||||||
drawing.scale(factor, factor);
|
drawing.scale(factor, factor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rotiert die Zeichenebene um den angegebenen Winkel um den Ursprung.
|
||||||
|
*
|
||||||
|
* @param pAngle Rotationswinkel in Grad.
|
||||||
|
*/
|
||||||
public void rotate( double pAngle ) {
|
public void rotate( double pAngle ) {
|
||||||
drawing.rotate(Math.toRadians(pAngle));
|
drawing.rotate(Math.toRadians(pAngle));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rotiert die Zeichenebene um den angegebenen Winkel um das angegebene
|
||||||
|
* Drehzentrum.
|
||||||
|
*
|
||||||
|
* @param pAngle Rotationswinkel in Grad.
|
||||||
|
* @param centerX x-Koordinate des Rotationszentrums.
|
||||||
|
* @param centerY y-Koordinate des Rotationszentrums.
|
||||||
|
*/
|
||||||
public void rotate( double pAngle, double centerX, double centerY ) {
|
public void rotate( double pAngle, double centerX, double centerY ) {
|
||||||
drawing.rotate(Math.toRadians(pAngle), centerX, centerY);
|
drawing.rotate(Math.toRadians(pAngle), centerX, centerY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wendet eine Scherung auf die Zeichenebene an.
|
||||||
|
* <p>
|
||||||
|
* Bei einer Scherung werden die x-Koordinaten parallel zur x-Achse abhängig
|
||||||
|
* von ihrer y-Koordinate verschoben. Ebenso die y-Koordinaten parallel zur
|
||||||
|
* y-Achse abhängig von ihrer x-Koordinate.
|
||||||
|
*
|
||||||
|
* @param dx Scherungsfaktor entlang der x-Achse.
|
||||||
|
* @param dy Scherungsfaktor entlang der y-Achse.
|
||||||
|
*/
|
||||||
public void shear( double dx, double dy ) {
|
public void shear( double dx, double dy ) {
|
||||||
drawing.shear(dx, dy);
|
drawing.shear(dx, dy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Die aktuelle <a
|
||||||
|
* href="https://de.wikibrief.org/wiki/Transformation_matrix">Transformationsmatrix</a>
|
||||||
|
* der Zeichenebene.
|
||||||
|
*/
|
||||||
public AffineTransform getMatrix() {
|
public AffineTransform getMatrix() {
|
||||||
return drawing.getTransform();
|
return drawing.getTransform();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Speichert eine Kopie der aktuellen Transformationen der Zeichenebene ab.
|
||||||
|
* <p>
|
||||||
|
* Die zuletzt gespeicherten Transformationen können mit
|
||||||
|
* {@link #popMatrix()} wiederhergestellt werden.
|
||||||
|
*/
|
||||||
public void pushMatrix() {
|
public void pushMatrix() {
|
||||||
transformStack.push(drawing.getTransform());
|
transformStack.push(drawing.getTransform());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ersetzt die aktuellen Transformationen der Zeichenebene durch die zuletzt
|
||||||
|
* mit {@link #pushMatrix()} gespeicherten.
|
||||||
|
* <p>
|
||||||
|
* Wurden keine Transformationen gespeichert, werden alle wird der
|
||||||
|
* Ursprungszustand wiederhergestellt.
|
||||||
|
*/
|
||||||
public void popMatrix() {
|
public void popMatrix() {
|
||||||
if( transformStack.isEmpty() ) {
|
if( transformStack.isEmpty() ) {
|
||||||
resetMatrix();
|
resetMatrix();
|
||||||
@@ -859,6 +1433,9 @@ public class DrawingLayer extends Layer implements Strokeable, Fillable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt alle Transformationen der Zeichenebene zurück.
|
||||||
|
*/
|
||||||
public void resetMatrix() {
|
public void resetMatrix() {
|
||||||
drawing.setTransform(new AffineTransform());
|
drawing.setTransform(new AffineTransform());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,53 +6,121 @@ import schule.ngb.zm.util.io.ImageLoader;
|
|||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.Image;
|
import java.awt.Image;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Eine Ebene, die ein statisches Bild anzeigt.
|
||||||
|
* <p>
|
||||||
|
* Die Ebene wird mit einem Bild initialisiert und zeigt dieses Bild als
|
||||||
|
* einzigen Inhalt an. Optional kann die Position des Bildes verändert werden,
|
||||||
|
* sodass es nicht im Ursprung der Ebene gezeichnet wird.
|
||||||
|
*/
|
||||||
@SuppressWarnings( "unused" )
|
@SuppressWarnings( "unused" )
|
||||||
public class ImageLayer extends Layer {
|
public class ImageLayer extends Layer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Das Bild, das angezeigt wird.
|
||||||
|
*/
|
||||||
protected Image image;
|
protected Image image;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* x-Koordinate der oberen linken Ecke auf der Ebene.
|
||||||
|
*/
|
||||||
protected double x = 0;
|
protected double x = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* y-Koordinate der oberen linken Ecke auf der Ebene.
|
||||||
|
*/
|
||||||
protected double y = 0;
|
protected double y = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interner Schalter, ob die Ebene neu gezeichnet werden muss.
|
||||||
|
*/
|
||||||
protected boolean redraw = true;
|
protected boolean redraw = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erstellt eine Bildebene in der Standardgröße aus der angegebenen
|
||||||
|
* Bildquelle.
|
||||||
|
*
|
||||||
|
* @param source Eine Bildquelle.
|
||||||
|
* @see ImageLoader#loadImage(String)
|
||||||
|
*/
|
||||||
public ImageLayer( String source ) {
|
public ImageLayer( String source ) {
|
||||||
image = ImageLoader.loadImage(source);
|
image = ImageLoader.loadImage(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erstellt eine Bildebene in der Standardgröße aus dem angegebenen Bild.
|
||||||
|
*
|
||||||
|
* @param image Ein Bild-Objekt.
|
||||||
|
*/
|
||||||
public ImageLayer( Image image ) {
|
public ImageLayer( Image image ) {
|
||||||
this.image = image;
|
this.image = image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erstellt eine Bildebene in der angegebenen Größe aus dem angegebenen
|
||||||
|
* Bild.
|
||||||
|
*
|
||||||
|
* @param width Breite der Bildebene.
|
||||||
|
* @param height Höhe der Bildebene.
|
||||||
|
* @param image Ein Bild-Objekt.
|
||||||
|
*/
|
||||||
public ImageLayer( int width, int height, Image image ) {
|
public ImageLayer( int width, int height, Image image ) {
|
||||||
super(width, height);
|
super(width, height);
|
||||||
this.image = image;
|
this.image = image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt das Bild der Ebene auf das angegebene Bild-Objekt.
|
||||||
|
*
|
||||||
|
* @param image Ein Bild-Objekt.
|
||||||
|
*/
|
||||||
public void setImage( Image image ) {
|
public void setImage( Image image ) {
|
||||||
this.image = image;
|
this.image = image;
|
||||||
redraw = true;
|
redraw = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Die x-Koordinate des Bildes in der Ebene.
|
||||||
|
*/
|
||||||
public double getX() {
|
public double getX() {
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt die {@code x}-Koordinate des BIldes in der Ebene auf den
|
||||||
|
* angegebenen Wert.
|
||||||
|
*
|
||||||
|
* @param pX Die x-Koordinate des Bildes.
|
||||||
|
*/
|
||||||
public void setX( double pX ) {
|
public void setX( double pX ) {
|
||||||
this.x = pX;
|
this.x = pX;
|
||||||
redraw = true;
|
redraw = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Die y-Koordinate des Bildes in der Ebene.
|
||||||
|
*/
|
||||||
public double getY() {
|
public double getY() {
|
||||||
return y;
|
return y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt die {@code y}-Koordinate des BIldes in der Ebene auf den
|
||||||
|
* angegebenen Wert.
|
||||||
|
*
|
||||||
|
* @param pY Die y-Koordinate des Bildes.
|
||||||
|
*/
|
||||||
public void setY( double pY ) {
|
public void setY( double pY ) {
|
||||||
this.y = pY;
|
this.y = pY;
|
||||||
redraw = true;
|
redraw = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Löscht die Ebene und zeichnet das Bild neu.
|
||||||
|
* <p>
|
||||||
|
* In der Regel muss die Ebene nicht gelöscht werden, da sie automatisch neu
|
||||||
|
* gezeichnet wird, sobald sich das zugrundeliegende Bild ändert.
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void clear() {
|
public void clear() {
|
||||||
super.clear();
|
super.clear();
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import java.util.*;
|
|||||||
import java.util.function.DoubleUnaryOperator;
|
import java.util.function.DoubleUnaryOperator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ein Layer um {@link Shape} Objekte zu zeichnen.
|
* Eine Ebene um {@link Shape} Objekte zu zeichnen.
|
||||||
* <p>
|
* <p>
|
||||||
* Ein {@code ShapesLayer} ist eine der drei Standardebenen der
|
* Ein {@code ShapesLayer} ist eine der drei Standardebenen der
|
||||||
* {@link schule.ngb.zm.Zeichenmaschine}.
|
* {@link schule.ngb.zm.Zeichenmaschine}.
|
||||||
@@ -19,6 +19,9 @@ import java.util.function.DoubleUnaryOperator;
|
|||||||
@SuppressWarnings( "unused" )
|
@SuppressWarnings( "unused" )
|
||||||
public class ShapesLayer extends Layer {
|
public class ShapesLayer extends Layer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
protected boolean clearBeforeDraw = true;
|
protected boolean clearBeforeDraw = true;
|
||||||
|
|
||||||
private final List<Shape> shapes;
|
private final List<Shape> shapes;
|
||||||
|
|||||||
@@ -12,6 +12,12 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Eine Ebene, auf der eine Turtle gesteuert werden kann, die Grafiken plottet.
|
||||||
|
* <p>
|
||||||
|
* Die Turtle verhält sich ähnlich zu ihren Entsprechungen in Logo oder
|
||||||
|
* TigerJython.
|
||||||
|
*/
|
||||||
@SuppressWarnings( "unused" )
|
@SuppressWarnings( "unused" )
|
||||||
public class TurtleLayer extends Layer implements Strokeable, Fillable {
|
public class TurtleLayer extends Layer implements Strokeable, Fillable {
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,21 @@
|
|||||||
package schule.ngb.zm.media;
|
package schule.ngb.zm.media;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface für Audio-Medien.
|
* Schnittstelle für Audio-Medien.
|
||||||
|
*
|
||||||
|
* <h2>MP3-Dateien verwenden</h2>
|
||||||
|
* Java kann nativ nur Waveform ({@code .wav}) Dateien wiedergeben. Um auch
|
||||||
|
* MP3-Dateien zu nutzen, müssen die Bibliotheken <a href="#">jlayer</a>, <a
|
||||||
|
* href="#">tritonus-share</a> und <a href="#">mp3spi</a> eingebunden werden.
|
||||||
|
* Details zur Verwendung können in der <a
|
||||||
|
* href="https://zeichenmaschine.xyz/installation/#unterstutzung-fur-mp3">Dokumentation
|
||||||
|
* der Zeichenmaschine</a> nachgelesen werden.
|
||||||
*/
|
*/
|
||||||
public interface Audio {
|
public interface Audio {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Die Quelle, aus der das Medium geladen wurde.
|
||||||
|
*/
|
||||||
String getSource();
|
String getSource();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -17,7 +28,7 @@ public interface Audio {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Prüft, ob das Medium gerade in einer Schleife abgespielt wird. Wenn
|
* Prüft, ob das Medium gerade in einer Schleife abgespielt wird. Wenn
|
||||||
* {@code isLooping() == true}, dann muss auch immer
|
* {@code isLooping() == true} gilt, dann muss auch immer
|
||||||
* {@code isPlaying() == true} gelten.
|
* {@code isPlaying() == true} gelten.
|
||||||
*
|
*
|
||||||
* @return {@code true}, wenn das Medium in einer Schleife abgespielt wird,
|
* @return {@code true}, wenn das Medium in einer Schleife abgespielt wird,
|
||||||
@@ -30,7 +41,7 @@ public interface Audio {
|
|||||||
* <p>
|
* <p>
|
||||||
* Die Lautstärke wird auf einer linearen Skale festgelegt, wobei 0 kein Ton
|
* Die Lautstärke wird auf einer linearen Skale festgelegt, wobei 0 kein Ton
|
||||||
* und 1 volle Lautstärke bedeutet. Werte über 1 verstärken den Ton des
|
* und 1 volle Lautstärke bedeutet. Werte über 1 verstärken den Ton des
|
||||||
* Mediums.
|
* Mediums. Negative Werte setzen die Lautstärke aud 0.
|
||||||
*
|
*
|
||||||
* @param volume Die neue Lautstärke zwischen 0 und 1.
|
* @param volume Die neue Lautstärke zwischen 0 und 1.
|
||||||
* @see <a
|
* @see <a
|
||||||
@@ -39,7 +50,7 @@ public interface Audio {
|
|||||||
void setVolume( double volume );
|
void setVolume( double volume );
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gibt die aktuelle Lautstärkeeinstellung dieses Mediums zurück.
|
* Liefert die aktuelle Lautstärke dieses Mediums.
|
||||||
* <p>
|
* <p>
|
||||||
* Die Lautstärke wird auf einer linearen Skale angegeben, wobei 0 kein Ton
|
* Die Lautstärke wird auf einer linearen Skale angegeben, wobei 0 kein Ton
|
||||||
* und 1 volle Lautstärke bedeutet. Werte über 1 verstärken den Ton des
|
* und 1 volle Lautstärke bedeutet. Werte über 1 verstärken den Ton des
|
||||||
@@ -50,8 +61,16 @@ public interface Audio {
|
|||||||
double getVolume();
|
double getVolume();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Startet die Wiedergabe des Mediums und beendet die Methode. Das
|
* Startet die Wiedergabe des Mediums. Das Audio-Medium wird einmal
|
||||||
* Audio-Medium wird einmal abgespielt und stoppt dann.
|
* abgespielt und stoppt dann.
|
||||||
|
* <p>
|
||||||
|
* Die Methode beendet sofort und die Wiedergabe erfolgt im Hintergrund.
|
||||||
|
* Soll die Programmausführung erst nach Wiedergabe des Mediums fortgesetzt
|
||||||
|
* werden, sollte {@link #playAndWait()} verwendet werden.
|
||||||
|
* <p>
|
||||||
|
* Soll die Wiedergabe im Hintergrund ablaufen, aber dennoch auf das Ende
|
||||||
|
* reagiert werden, kann ein
|
||||||
|
* {@link #addAudioListener(AudioListener) AudioListener} verwendet werden.
|
||||||
*/
|
*/
|
||||||
void play();
|
void play();
|
||||||
|
|
||||||
@@ -63,21 +82,36 @@ public interface Audio {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Spielt das Medium in einer kontinuierlichen Schleife ab. Die Methode
|
* Spielt das Medium in einer kontinuierlichen Schleife ab. Die Methode
|
||||||
* startet die Wiedergabe und beendet dann direkt die Methode. Um die
|
* startet die Wiedergabe im Hintergrund und beendet dann sofort. Um die
|
||||||
* Wiedergabe zu stoppen muss {@link #stop()} aufgerufen werden.
|
* Wiedergabe zu stoppen, muss {@link #stop()} aufgerufen werden.
|
||||||
*/
|
*/
|
||||||
void loop();
|
void loop();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stoppt die Wiedergabe. Wird das Medium gerade nicht abgespielt
|
* Stoppt die Wiedergabe. Wird das Medium gerade nicht abgespielt
|
||||||
* {@code isPlaying() == false}, dann passiert nichts.
|
* ({@code isPlaying() == false}), dann passiert nichts.
|
||||||
*/
|
*/
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stoppt die Wiedergabe und gibt alle Resourcen, die für das Medium
|
* Stoppt die Wiedergabe und gibt alle Ressourcen, die für das Medium
|
||||||
* verwendet werden, frei.
|
* verwendet werden, frei.
|
||||||
*/
|
*/
|
||||||
void dispose();
|
void dispose();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fügt dem Medium das angegebene Objekt als {@code AudioListener} hinzu,
|
||||||
|
* der bei Start und Stopp der Wiedergabe informiert wird.
|
||||||
|
*
|
||||||
|
* @param listener Das Listener-Objekt.
|
||||||
|
*/
|
||||||
|
void addAudioListener( AudioListener listener );
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entfernt den angegebenen {@code AudioListener} vom Medium.
|
||||||
|
*
|
||||||
|
* @param listener Das Listener-Objekt.
|
||||||
|
*/
|
||||||
|
void removeAudioListener( AudioListener listener );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,33 @@ package schule.ngb.zm.media;
|
|||||||
|
|
||||||
import schule.ngb.zm.util.events.Listener;
|
import schule.ngb.zm.util.events.Listener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface für Klassen, die auf das starten und stoppen der Wiedergabe von
|
||||||
|
* {@link Audio}-Objekten reagieren möchten.
|
||||||
|
* <p>
|
||||||
|
* Implementierende Klassen können sich bei einem Auido-Objekt mittels
|
||||||
|
* {@link Audio#addAudioListener(AudioListener)} anmelden und werden über die
|
||||||
|
* jeweilige Methode informiert, sobald die Wiedergabe gestartet oder gestoppt
|
||||||
|
* wird.
|
||||||
|
*/
|
||||||
public interface AudioListener extends Listener<Audio> {
|
public interface AudioListener extends Listener<Audio> {
|
||||||
|
|
||||||
void start( Audio source );
|
/**
|
||||||
|
* Wird aufgerufen, sobald die Wiedergabe eines Audio-Mediums startet, dem
|
||||||
|
* dieses Objekt mittels {@link Audio#addAudioListener(AudioListener)}
|
||||||
|
* hinzugefügt wurde.
|
||||||
|
*
|
||||||
|
* @param source Das Audio-Medium, dessen Wiedergabe gestartet wurde.
|
||||||
|
*/
|
||||||
|
void playbackStarted( Audio source );
|
||||||
|
|
||||||
void stop( Audio source );
|
/**
|
||||||
|
* Wird aufgerufen, sobald die Wiedergabe eines Audio-Mediums stoppt, dem
|
||||||
|
* dieses Objekt mittels {@link Audio#addAudioListener(AudioListener)}
|
||||||
|
* hinzugefügt wurde.
|
||||||
|
*
|
||||||
|
* @param source Das Audio-Medium, dessen Wiedergabe gestoppt wurde.
|
||||||
|
*/
|
||||||
|
void playbackStopped( Audio source );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package schule.ngb.zm.media;
|
package schule.ngb.zm.media;
|
||||||
|
|
||||||
import schule.ngb.zm.Constants;
|
import schule.ngb.zm.Constants;
|
||||||
|
import schule.ngb.zm.util.events.EventDispatcher;
|
||||||
import schule.ngb.zm.util.tasks.TaskRunner;
|
import schule.ngb.zm.util.tasks.TaskRunner;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -18,24 +20,14 @@ import java.util.List;
|
|||||||
* Darüber hinaus kann ein Mixer Effekte wie einen
|
* Darüber hinaus kann ein Mixer Effekte wie einen
|
||||||
* {@link #fade(double, int) fadeIn} auf die Medien anwenden.
|
* {@link #fade(double, int) fadeIn} auf die Medien anwenden.
|
||||||
*/
|
*/
|
||||||
public class Mixer implements Audio {
|
@SuppressWarnings( "unused" )
|
||||||
|
public class Mixer implements Audio, AudioListener {
|
||||||
|
|
||||||
private List<AudioWrapper> audios;
|
private List<AudioWrapper> audios;
|
||||||
|
|
||||||
private float volume = 0.8f;
|
private float volume = 0.8f;
|
||||||
|
|
||||||
class AudioWrapper {
|
EventDispatcher<Audio, AudioListener> eventDispatcher;
|
||||||
|
|
||||||
Audio audio;
|
|
||||||
|
|
||||||
float volumeFactor;
|
|
||||||
|
|
||||||
public AudioWrapper( Audio audio, float volumeFactor ) {
|
|
||||||
this.audio = audio;
|
|
||||||
this.volumeFactor = volumeFactor;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public Mixer() {
|
public Mixer() {
|
||||||
this.audios = new ArrayList<>(4);
|
this.audios = new ArrayList<>(4);
|
||||||
@@ -45,66 +37,93 @@ public class Mixer implements Audio {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
public void add( Audio pAudio ) {
|
private AudioWrapper findWrapper( Audio pAudio ) {
|
||||||
add(pAudio, 1f);
|
for( AudioWrapper aw: audios ) {
|
||||||
|
if( aw.audio == pAudio ) {
|
||||||
|
return aw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean contains( Audio pAudio ) {
|
||||||
|
return findWrapper(pAudio) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add( Audio pAudio ) {
|
||||||
|
add(pAudio, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fügt ein Audio-Objekt dem Mixer mit dem angegebenen Lautstärke-Faktor
|
||||||
|
* hinzu.
|
||||||
|
* <p>
|
||||||
|
* Der Lautstärke-Faktor setzt die Lautstärke des Audio-Objektes relativ zur
|
||||||
|
* Lautstärke des Mixers. Bei einem Faktor von 1.0 wird die Lautstärke des
|
||||||
|
* Mixers übernommen. Bei einem Wert von 0.5 wird das Objekt halb so laut
|
||||||
|
* abgespielt. Auf diese Weise lässt sich die Lautstärke aller Audio-Objekte
|
||||||
|
* des Mixers gleichzeitig anpassen, während ihre relative Lautstärke
|
||||||
|
* zueinander gleich bleibt.
|
||||||
|
*
|
||||||
|
* @param pAudio Ein Audio-Objekt.
|
||||||
|
* @param pVolumeFactor Der Lautstärke-Faktor.
|
||||||
|
*/
|
||||||
public void add( Audio pAudio, double pVolumeFactor ) {
|
public void add( Audio pAudio, double pVolumeFactor ) {
|
||||||
|
if( !contains(pAudio) ) {
|
||||||
audios.add(new AudioWrapper(pAudio, (float) pVolumeFactor));
|
audios.add(new AudioWrapper(pAudio, (float) pVolumeFactor));
|
||||||
|
} else {
|
||||||
|
findWrapper(pAudio).volumeFactor = (float) pVolumeFactor;
|
||||||
|
}
|
||||||
pAudio.setVolume(pVolumeFactor * volume);
|
pAudio.setVolume(pVolumeFactor * volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entfernt die das angegebene Audio-Objekt aus dem Mixer. Ist das Objekt
|
||||||
|
* nicht Teil des Mixers, passiert nichts.
|
||||||
|
*
|
||||||
|
* @param pAudio Ein Audio-Objekt.
|
||||||
|
*/
|
||||||
public void remove( Audio pAudio ) {
|
public void remove( Audio pAudio ) {
|
||||||
|
Iterator<AudioWrapper> it = audios.listIterator();
|
||||||
|
while( it.hasNext() ) {
|
||||||
|
AudioWrapper aw = it.next();
|
||||||
|
if( aw.audio == pAudio ) {
|
||||||
|
it.remove();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeAll() {
|
public void removeAll() {
|
||||||
audios.clear();
|
audios.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isPlaying() {
|
public boolean isPlaying() {
|
||||||
return audios.stream().anyMatch(aw -> aw.audio.isPlaying());
|
return audios.stream().anyMatch(aw -> aw.audio.isPlaying());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isLooping() {
|
public boolean isLooping() {
|
||||||
return audios.stream().anyMatch(aw -> aw.audio.isLooping());
|
return audios.stream().anyMatch(aw -> aw.audio.isLooping());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void setVolume( double pVolume ) {
|
public void setVolume( double volume ) {
|
||||||
volume = (float) pVolume;
|
this.volume = volume < 0 ? 0f : (float) volume;
|
||||||
audios.stream().forEach(aw -> aw.audio.setVolume(aw.volumeFactor * pVolume));
|
audios.stream().forEach(aw -> aw.audio.setVolume(aw.volumeFactor * volume));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public double getVolume() {
|
public double getVolume() {
|
||||||
return volume;
|
return volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void play() {
|
public void play() {
|
||||||
audios.stream().forEach(aw -> aw.audio.play());
|
audios.stream().forEach(aw -> aw.audio.play());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void playAndWait() {
|
public void playAndWait() {
|
||||||
audios.stream().forEach(aw -> aw.audio.play());
|
audios.stream().forEach(aw -> aw.audio.play());
|
||||||
@@ -117,25 +136,16 @@ public class Mixer implements Audio {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void loop() {
|
public void loop() {
|
||||||
audios.stream().forEach(aw -> aw.audio.loop());
|
audios.stream().forEach(aw -> aw.audio.loop());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void stop() {
|
public void stop() {
|
||||||
audios.stream().forEach(aw -> aw.audio.stop());
|
audios.stream().forEach(aw -> aw.audio.stop());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void dispose() {
|
public void dispose() {
|
||||||
if( isPlaying() ) {
|
if( isPlaying() ) {
|
||||||
@@ -178,4 +188,58 @@ public class Mixer implements Audio {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void playbackStarted( Audio source ) {
|
||||||
|
if( eventDispatcher != null ) {
|
||||||
|
eventDispatcher.dispatchEvent("start", Mixer.this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void playbackStopped( Audio source ) {
|
||||||
|
if( !isPlaying() ) {
|
||||||
|
if( eventDispatcher != null ) {
|
||||||
|
eventDispatcher.dispatchEvent("stop", Mixer.this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addAudioListener( AudioListener listener ) {
|
||||||
|
initializeEventDispatcher().addListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAudioListener( AudioListener listener ) {
|
||||||
|
initializeEventDispatcher().removeListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interne Methode, um den Listener-Mechanismus zu initialisieren. Wird erst
|
||||||
|
* aufgerufen, sobald sich auch ein Listener registrieren möchte.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private EventDispatcher<Audio, AudioListener> initializeEventDispatcher() {
|
||||||
|
if( eventDispatcher == null ) {
|
||||||
|
eventDispatcher = new EventDispatcher<>();
|
||||||
|
eventDispatcher.registerEventType("start", (a,l) -> l.playbackStarted(a));
|
||||||
|
eventDispatcher.registerEventType("stop", (a,l) -> l.playbackStopped(a));
|
||||||
|
}
|
||||||
|
return eventDispatcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
class AudioWrapper {
|
||||||
|
|
||||||
|
Audio audio;
|
||||||
|
|
||||||
|
float volumeFactor;
|
||||||
|
|
||||||
|
public AudioWrapper( Audio audio, float volumeFactor ) {
|
||||||
|
this.audio = audio;
|
||||||
|
this.volumeFactor = volumeFactor;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,83 +1,103 @@
|
|||||||
package schule.ngb.zm.media;
|
package schule.ngb.zm.media;
|
||||||
|
|
||||||
import schule.ngb.zm.util.events.EventDispatcher;
|
|
||||||
import schule.ngb.zm.util.tasks.TaskRunner;
|
|
||||||
import schule.ngb.zm.util.Log;
|
import schule.ngb.zm.util.Log;
|
||||||
import schule.ngb.zm.util.io.ResourceStreamProvider;
|
|
||||||
import schule.ngb.zm.util.Validator;
|
import schule.ngb.zm.util.Validator;
|
||||||
|
import schule.ngb.zm.util.events.EventDispatcher;
|
||||||
|
import schule.ngb.zm.util.io.ResourceStreamProvider;
|
||||||
|
import schule.ngb.zm.util.tasks.TaskRunner;
|
||||||
|
|
||||||
import javax.sound.sampled.*;
|
import javax.sound.sampled.*;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ein Musikstück, dass im Projekt abgespielt werden soll.
|
* Eine Musik, die abgespielt werden kann.
|
||||||
* <p>
|
* <p>
|
||||||
* Im gegensatz zu einem {@link Sound} sind Musikstücke längere Audiodateien,
|
* Im Gegensatz zu einem {@link Sound} wird {@code Music} für längere
|
||||||
* die zum Beispiel als Hintergrundmusik ablaufen sollen. Die Musik wird daher
|
* Audiodateien benutzt, die zum Beispiel als Hintergrundmusik gespielt werden.
|
||||||
* nicht komplett in den Speicher geladen, sondern direkt aus der Audioquelle
|
* Die Audiodaten werden daher nicht vollständig in den Speicher geladen,
|
||||||
* gestreamt und wiedergegeben.
|
* sondern direkt aus der Quelle gestreamt und direkt wiedergegeben.
|
||||||
|
* <p>
|
||||||
|
* Daher ist es nicht möglich, die länge der Musik im Vorfeld abzufragen oder zu
|
||||||
|
* einer bestimmten Stelle im Stream zu springen.
|
||||||
|
*
|
||||||
|
* <h2>MP3-Dateien verwenden</h2>
|
||||||
|
* Java kann nativ nur Waveform ({@code .wav}) Dateien wiedergeben. Um auch
|
||||||
|
* MP3-Dateien zu nutzen, müssen die Bibliotheken <a href="#">jlayer</a>, <a
|
||||||
|
* href="#">tritonus-share</a> und <a href="#">mp3spi</a> eingebunden werden.
|
||||||
|
* Details zur Verwendung können in der <a
|
||||||
|
* href="https://zeichenmaschine.xyz/installation/#unterstutzung-fur-mp3">Dokumentation
|
||||||
|
* der Zeichenmaschine</a> nachgelesen werden.
|
||||||
*/
|
*/
|
||||||
|
// TODO: Wann sollten Listener beim Loopen informiert werden? Nach jedem Loop oder erst ganz am Ende?
|
||||||
|
@SuppressWarnings( "unused" )
|
||||||
public class Music implements Audio {
|
public class Music implements Audio {
|
||||||
|
|
||||||
// size of the byte buffer used to read/write the audio stream
|
/**
|
||||||
|
* Größe des verwendeten Input-Puffers für die Audiodaten.
|
||||||
|
*/
|
||||||
private static final int BUFFER_SIZE = 4096;
|
private static final int BUFFER_SIZE = 4096;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ob der Sound gerade abgespielt wird.
|
* Ob der Sound aktuell abgespielt wird.
|
||||||
*/
|
*/
|
||||||
private boolean playing = false;
|
private boolean playing = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ob der Sound gerade in einer Schleife abgespielt wird.
|
* Ob der Sound aktuell in einer Schleife abgespielt wird.
|
||||||
*/
|
*/
|
||||||
private boolean looping = false;
|
private boolean looping = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Die Quelle des Musikstücks.
|
* Die Quelle der Audiodaten.
|
||||||
*/
|
*/
|
||||||
private String audioSource;
|
private String audioSource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Der AudioStream, um die AUdiosdaten zulsen, falls dieser schon geöffnet
|
* Der {@link AudioInputStream}, um die Audiosdaten zu lesen. {@code null},
|
||||||
* wurde. Sonst {@code null}.
|
* falls noch kein Stream geöffnet wurde.
|
||||||
*/
|
*/
|
||||||
private AudioInputStream audioStream;
|
private AudioInputStream audioStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Die Line für die Ausgabe, falls diese schon geöffnet wurde. Sonst
|
* Die {@link SourceDataLine} für die Ausgabe. {@code null}, falls die
|
||||||
* {@code null}.
|
* Audiodatei noch nicht geöffnet wurde.
|
||||||
*/
|
*/
|
||||||
private SourceDataLine audioLine;
|
private SourceDataLine audioLine;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Die Lautstärke der Musik.
|
* Die aktuelle Lautstärke des Mediums.
|
||||||
*/
|
*/
|
||||||
private float volume = 0.8f;
|
private float volume = 0.8f;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatcher für Audio-Events (start und stop).
|
||||||
|
*/
|
||||||
EventDispatcher<Audio, AudioListener> eventDispatcher;
|
EventDispatcher<Audio, AudioListener> eventDispatcher;
|
||||||
|
|
||||||
public Music( String source ) {
|
/**
|
||||||
Validator.requireNotNull(source);
|
* Erstellt eine Musik aus der angegebenen Audioquelle.
|
||||||
this.audioSource = source;
|
*
|
||||||
|
* @param audioSource Quelle der Audiodaten.
|
||||||
|
* @throws NullPointerException Falls die Quelle {@code null} ist.
|
||||||
|
* @see ResourceStreamProvider#getResourceURL(String)
|
||||||
|
*/
|
||||||
|
public Music( String audioSource ) {
|
||||||
|
Validator.requireNotNull(audioSource);
|
||||||
|
this.audioSource = audioSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getSource() {
|
public String getSource() {
|
||||||
return audioSource;
|
return audioSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isPlaying() {
|
public boolean isPlaying() {
|
||||||
return playing;
|
return playing;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isLooping() {
|
public boolean isLooping() {
|
||||||
if( !playing ) {
|
if( !playing ) {
|
||||||
@@ -86,28 +106,21 @@ public class Music implements Audio {
|
|||||||
return looping;
|
return looping;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void setVolume( double volume ) {
|
public void setVolume( double volume ) {
|
||||||
this.volume = (float) volume;
|
this.volume = volume < 0 ? 0f : (float) volume;
|
||||||
if( audioLine != null ) {
|
if( audioLine != null ) {
|
||||||
applyVolume();
|
applyVolume();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public double getVolume() {
|
public double getVolume() {
|
||||||
return volume;
|
return volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interne Methode, um die gesetzte Lautstärke vor dem Abspielen
|
* Wendet die Lautstärke vor dem Abspielen auf den Audiostream an.
|
||||||
* anzuwenden.
|
|
||||||
*/
|
*/
|
||||||
private void applyVolume() {
|
private void applyVolume() {
|
||||||
FloatControl gainControl =
|
FloatControl gainControl =
|
||||||
@@ -118,24 +131,13 @@ public class Music implements Audio {
|
|||||||
gainControl.setValue(vol);
|
gainControl.setValue(vol);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void play() {
|
public void play() {
|
||||||
if( openLine() ) {
|
if( openLine() ) {
|
||||||
TaskRunner.run(new Runnable() {
|
TaskRunner.run(this::stream);
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
stream();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void playAndWait() {
|
public void playAndWait() {
|
||||||
if( openLine() ) {
|
if( openLine() ) {
|
||||||
@@ -143,18 +145,12 @@ public class Music implements Audio {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void loop() {
|
public void loop() {
|
||||||
looping = true;
|
looping = true;
|
||||||
play();
|
play();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void stop() {
|
public void stop() {
|
||||||
playing = false;
|
playing = false;
|
||||||
@@ -162,9 +158,6 @@ public class Music implements Audio {
|
|||||||
dispose();
|
dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void dispose() {
|
public synchronized void dispose() {
|
||||||
if( audioLine != null ) {
|
if( audioLine != null ) {
|
||||||
@@ -188,7 +181,17 @@ public class Music implements Audio {
|
|||||||
audioStream = null;
|
audioStream = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Startet den Stream der Audiodaten und damit die Wiedergabe.
|
||||||
|
* <p>
|
||||||
|
* Die {@link #audioLine} muss vorher mit {@link #openLine()} geöffnet
|
||||||
|
* werden, ansonsten passiert nichts.
|
||||||
|
*/
|
||||||
private synchronized void stream() {
|
private synchronized void stream() {
|
||||||
|
if( audioLine == null ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
audioLine.start();
|
audioLine.start();
|
||||||
playing = true;
|
playing = true;
|
||||||
if( eventDispatcher != null ) {
|
if( eventDispatcher != null ) {
|
||||||
@@ -225,6 +228,14 @@ public class Music implements Audio {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Öffnet eine {@link SourceDataLine} für die
|
||||||
|
* {@link #audioSource Audioquelle} und bereitet die Wiedergabe vor. Es wird
|
||||||
|
* noch nichts abgespielt.
|
||||||
|
*
|
||||||
|
* @return {@code true}, wenn die Line geöffnet werden konnte, {@code false}
|
||||||
|
* sonst.
|
||||||
|
*/
|
||||||
private boolean openLine() {
|
private boolean openLine() {
|
||||||
if( audioLine != null ) {
|
if( audioLine != null ) {
|
||||||
return true;
|
return true;
|
||||||
@@ -261,6 +272,15 @@ public class Music implements Audio {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wird aufgerufen, wenn die Wiedergabe beendet wurde. Entweder durch einen
|
||||||
|
* Aufruf von {@link #stop()} oder weil keine Audiodaten mehr vorhanden
|
||||||
|
* sind.
|
||||||
|
* <p>
|
||||||
|
* Nach dem Ende des Streams wird {@link #dispose()} aufgerufen und, falls
|
||||||
|
* das Musikstück in einer Schleife abgespielt wird, der Stream direkt
|
||||||
|
* wieder gestartet.
|
||||||
|
*/
|
||||||
private void streamingStopped() {
|
private void streamingStopped() {
|
||||||
dispose();
|
dispose();
|
||||||
|
|
||||||
@@ -276,24 +296,27 @@ public class Music implements Audio {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addListener( AudioListener listener ) {
|
@Override
|
||||||
|
public void addAudioListener( AudioListener listener ) {
|
||||||
initializeEventDispatcher().addListener(listener);
|
initializeEventDispatcher().addListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeListener( AudioListener listener ) {
|
@Override
|
||||||
|
public void removeAudioListener( AudioListener listener ) {
|
||||||
initializeEventDispatcher().removeListener(listener);
|
initializeEventDispatcher().removeListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interne Methode, um den Listener-Mechanismus zu initialisieren. Wird erst
|
* Interne Methode, um den Listener-Mechanismus zu initialisieren. Wird erst
|
||||||
* aufgerufen, soblad sich auch ein Listener registrieren möchte.
|
* aufgerufen, sobald sich der erste Listener anmelden möchte.
|
||||||
* @return
|
*
|
||||||
|
* @return Der {@code EventDispatcher} für dieses Objekt.
|
||||||
*/
|
*/
|
||||||
private EventDispatcher<Audio, AudioListener> initializeEventDispatcher() {
|
private EventDispatcher<Audio, AudioListener> initializeEventDispatcher() {
|
||||||
if( eventDispatcher == null ) {
|
if( eventDispatcher == null ) {
|
||||||
eventDispatcher = new EventDispatcher<>();
|
eventDispatcher = new EventDispatcher<>();
|
||||||
eventDispatcher.registerEventType("start", (a,l) -> l.start(a));
|
eventDispatcher.registerEventType("start", ( a, l ) -> l.playbackStarted(a));
|
||||||
eventDispatcher.registerEventType("stop", (a,l) -> l.stop(a));
|
eventDispatcher.registerEventType("stop", ( a, l ) -> l.playbackStopped(a));
|
||||||
}
|
}
|
||||||
return eventDispatcher;
|
return eventDispatcher;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package schule.ngb.zm.media;
|
|||||||
|
|
||||||
import schule.ngb.zm.util.Log;
|
import schule.ngb.zm.util.Log;
|
||||||
import schule.ngb.zm.util.Validator;
|
import schule.ngb.zm.util.Validator;
|
||||||
|
import schule.ngb.zm.util.events.EventDispatcher;
|
||||||
import schule.ngb.zm.util.io.ResourceStreamProvider;
|
import schule.ngb.zm.util.io.ResourceStreamProvider;
|
||||||
|
|
||||||
import javax.sound.sampled.*;
|
import javax.sound.sampled.*;
|
||||||
@@ -9,78 +10,87 @@ import java.io.IOException;
|
|||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wiedergabe kurzer Soundclips, die mehrmals wiederverwendet werden.
|
* Ein kurzer Soundclip, der mehrmals wiederverwendet werden kann.
|
||||||
* <p>
|
* <p>
|
||||||
* In Spielen und anderen Projekten gibt es oftmals eine Reihe kurzer Sounds,
|
* In Spielen und anderen Projekten gibt es oftmals eine Reihe kurzer
|
||||||
* die zusammen mit bestimmten Aktionen wiedergegeben werden (zum Beispiel, wenn
|
* Soundclips, die zusammen mit bestimmten Aktionen wiedergegeben werden (zum
|
||||||
* die Spielfigur springt, wenn zwei Objekte kollidieren, usw.). Sounds werden
|
* Beispiel, wenn die Spielfigur springt, wenn zwei Objekte kollidieren, usw.).
|
||||||
* komplett in den Speicher geladen und können dadurch immer wieder, als
|
* Sounds werden vollständig in den Speicher geladen und können immer wieder,
|
||||||
* Schleife oder auch nur Abschnittsweise abgespielt werden.
|
* als Schleife oder auch nur Abschnittsweise, abgespielt werden.
|
||||||
* <p>
|
* <p>
|
||||||
* Für längre Musikstücke (beispielsweise Hintergrundmusik) bietet sich eher die
|
* Für längere Musikstücke (beispielsweise Hintergrundmusik) bietet sich eher
|
||||||
* KLasse {@link Music} an.
|
* die Klasse {@link Music} an.
|
||||||
|
*
|
||||||
|
* <h2>MP3-Dateien verwenden</h2>
|
||||||
|
* Java kann nativ nur Waveform ({@code .wav}) Dateien wiedergeben. Um auch
|
||||||
|
* MP3-Dateien zu nutzen, müssen die Bibliotheken <a href="#">jlayer</a>, <a
|
||||||
|
* href="#">tritonus-share</a> und <a href="#">mp3spi</a> eingebunden werden.
|
||||||
|
* Details zur Verwendung können in der <a
|
||||||
|
* href="https://zeichenmaschine.xyz/installation/#unterstutzung-fur-mp3">Dokumentation
|
||||||
|
* der Zeichenmaschine</a> nachgelesen werden.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings( "unused" )
|
@SuppressWarnings( "unused" )
|
||||||
public class Sound implements Audio {
|
public class Sound implements Audio {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ob der Sound gerade abgespielt wird.
|
* Ob der Sound aktuell abgespielt wird.
|
||||||
*/
|
*/
|
||||||
private boolean playing = false;
|
private boolean playing = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ob der Sound gerade in einer Schleife abgespielt wird.
|
* Ob der Sound aktuell in einer Schleife abgespielt wird.
|
||||||
*/
|
*/
|
||||||
private boolean looping = false;
|
private boolean looping = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Die Quelle des Musikstücks.
|
* Die Quelle der Audiodaten.
|
||||||
*/
|
*/
|
||||||
private String audioSource;
|
private String audioSource;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Der Clip, falls er schon geladen wurde, sonst {@code null}.
|
* Der Clip, falls er schon geladen wurde. Ansonsten {@code null}.
|
||||||
*/
|
*/
|
||||||
private Clip audioClip;
|
private Clip audioClip;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ob die Resourcen des Clips im Speicher nach dem nächsten Abspielen
|
* Ob die Ressourcen des Clips im Speicher nach dem nächsten Abspielen
|
||||||
* freigegeben werden sollen.
|
* freigegeben werden sollen.
|
||||||
*/
|
*/
|
||||||
private boolean disposeAfterPlay = false;
|
private boolean disposeAfterPlay = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Die Lautstärke des Clips.
|
* Die aktuelle Lautstärke des Clips.
|
||||||
*/
|
*/
|
||||||
private float volume = 0.8f;
|
private float volume = 0.8f;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatcher für Audio-Events (start und stop).
|
||||||
|
*/
|
||||||
|
EventDispatcher<Audio, AudioListener> eventDispatcher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Erstellt einen Sound aus der angegebene Quelle.
|
* Erstellt einen Sound aus der angegebene Quelle.
|
||||||
*
|
*
|
||||||
* @param source Ein Dateipfad oder eine Webadresse.
|
* @param source Quelle der Audiodaten.
|
||||||
* @throws NullPointerException Falls die Quelle {@code null} ist.
|
* @throws NullPointerException Falls die Quelle {@code null} ist.
|
||||||
|
* @see ResourceStreamProvider#getResourceURL(String)
|
||||||
*/
|
*/
|
||||||
public Sound( String source ) {
|
public Sound( String source ) {
|
||||||
Validator.requireNotNull(source);
|
Validator.requireNotNull(source);
|
||||||
this.audioSource = source;
|
this.audioSource = source;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getSource() {
|
public String getSource() {
|
||||||
return audioSource;
|
return audioSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isPlaying() {
|
public boolean isPlaying() {
|
||||||
// return audioClip != null && audioClip.isRunning();
|
// return audioClip != null && audioClip.isRunning();
|
||||||
return playing;
|
return playing;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isLooping() {
|
public boolean isLooping() {
|
||||||
if( !playing ) {
|
if( !playing ) {
|
||||||
@@ -89,28 +99,21 @@ public class Sound implements Audio {
|
|||||||
return looping;
|
return looping;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void setVolume( double volume ) {
|
public void setVolume( double volume ) {
|
||||||
this.volume = (float) volume;
|
this.volume = volume < 0 ? 0f : (float) volume;
|
||||||
if( audioClip != null ) {
|
if( audioClip != null ) {
|
||||||
applyVolume();
|
applyVolume();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public double getVolume() {
|
public double getVolume() {
|
||||||
return volume;
|
return volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interne Methode, um die gesetzte Lautstärke vor dem Abspielen
|
* Wendet die Lautstärke vor dem Abspielen auf den Clip an.
|
||||||
* anzuwenden.
|
|
||||||
*/
|
*/
|
||||||
private void applyVolume() {
|
private void applyVolume() {
|
||||||
FloatControl gainControl =
|
FloatControl gainControl =
|
||||||
@@ -121,9 +124,6 @@ public class Sound implements Audio {
|
|||||||
gainControl.setValue(vol);
|
gainControl.setValue(vol);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void stop() {
|
public void stop() {
|
||||||
looping = false;
|
looping = false;
|
||||||
@@ -133,9 +133,6 @@ public class Sound implements Audio {
|
|||||||
playing = false;
|
playing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void play() {
|
public void play() {
|
||||||
if( this.openClip() ) {
|
if( this.openClip() ) {
|
||||||
@@ -144,9 +141,6 @@ public class Sound implements Audio {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void playAndWait() {
|
public void playAndWait() {
|
||||||
this.play();
|
this.play();
|
||||||
@@ -165,7 +159,7 @@ public class Sound implements Audio {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Spielt den Sound genau einmal ab und gibt danach alle Resourcen des Clips
|
* Spielt den Sound einmal ab und gibt danach alle Ressourcen des Clips
|
||||||
* frei.
|
* frei.
|
||||||
* <p>
|
* <p>
|
||||||
* Der Aufruf ist effektiv gleich zu
|
* Der Aufruf ist effektiv gleich zu
|
||||||
@@ -182,7 +176,7 @@ public class Sound implements Audio {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Spielt den Sound genau einmal ab und gibt danach alle Resourcen des Clips
|
* Spielt den Sound einmal ab und gibt danach alle Ressourcen des Clips
|
||||||
* frei.
|
* frei.
|
||||||
* <p>
|
* <p>
|
||||||
* Der Aufruf entspricht
|
* Der Aufruf entspricht
|
||||||
@@ -196,17 +190,18 @@ public class Sound implements Audio {
|
|||||||
playAndWait();
|
playAndWait();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void loop() {
|
public void loop() {
|
||||||
loop(Clip.LOOP_CONTINUOUSLY);
|
loop(Clip.LOOP_CONTINUOUSLY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wiederholt den Sound die angegebene Anzahl an Wiederholungen ab und
|
* Wiederholt den Sound die angegebene Anzahl an Wiederholungen und stoppt
|
||||||
* stoppt die Wiedergabe dann.
|
* die Wiedergabe dann.
|
||||||
|
* <p>
|
||||||
|
* Wird {@code count} auf {@link Clip#LOOP_CONTINUOUSLY} gesetzt (-1), wird
|
||||||
|
* der Clip unendlich oft wiederholt. Der Aufruf entspricht dann
|
||||||
|
* {@link #loop()}.
|
||||||
*
|
*
|
||||||
* @param count Anzahl der Wiederholungen.
|
* @param count Anzahl der Wiederholungen.
|
||||||
*/
|
*/
|
||||||
@@ -228,20 +223,24 @@ public class Sound implements Audio {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void dispose() {
|
public synchronized void dispose() {
|
||||||
if( audioClip != null ) {
|
if( audioClip != null ) {
|
||||||
if( audioClip.isRunning() ) {
|
if( audioClip.isRunning() ) {
|
||||||
audioClip.stop();
|
stop();
|
||||||
}
|
}
|
||||||
audioClip.close();
|
audioClip.close();
|
||||||
audioClip = null;
|
audioClip = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lädt falls nötig den {@link Clip} für die
|
||||||
|
* {@link #audioSource Audioquelle} und startet die Wiedergabe.
|
||||||
|
*
|
||||||
|
* @return {@code true}, wenn der Clip geöffnet werden konnte, {@code false}
|
||||||
|
* sonst.
|
||||||
|
*/
|
||||||
private synchronized boolean openClip() {
|
private synchronized boolean openClip() {
|
||||||
if( audioClip != null ) {
|
if( audioClip != null ) {
|
||||||
audioClip.setFramePosition(0);
|
audioClip.setFramePosition(0);
|
||||||
@@ -259,8 +258,16 @@ public class Sound implements Audio {
|
|||||||
audioClip.addLineListener(new LineListener() {
|
audioClip.addLineListener(new LineListener() {
|
||||||
@Override
|
@Override
|
||||||
public void update( LineEvent event ) {
|
public void update( LineEvent event ) {
|
||||||
if( event.getType() == LineEvent.Type.STOP ) {
|
if( event.getType() == LineEvent.Type.START ) {
|
||||||
|
if( eventDispatcher != null ) {
|
||||||
|
eventDispatcher.dispatchEvent("start", Sound.this);
|
||||||
|
}
|
||||||
|
} else if( event.getType() == LineEvent.Type.STOP ) {
|
||||||
playbackStopped();
|
playbackStopped();
|
||||||
|
|
||||||
|
if( eventDispatcher != null ) {
|
||||||
|
eventDispatcher.dispatchEvent("stop", Sound.this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -296,18 +303,47 @@ public class Sound implements Audio {
|
|||||||
}*/
|
}*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interne Methode, die aufgerufen wird, wenn die Wiedergabe gestoppt wird.
|
* Wird aufgerufen, wenn die Wiedergabe beendet wurde. Entweder durch einen
|
||||||
* Entweder durch einen Aufruf von {@link #stop()} oder, weil die Wiedergabe
|
* Aufruf von {@link #stop()} oder, weil die Wiedergabe nach
|
||||||
* nach {@link #playOnce()} beendet wurde.
|
* {@link #playOnce()} beendet wurde.
|
||||||
|
* <p>
|
||||||
|
* Falls {@link #disposeAfterPlay} gesetzt ist, wird nach dem Ende der
|
||||||
|
* Wiedergabe {@link #dispose()} aufgerufen.
|
||||||
*/
|
*/
|
||||||
private void playbackStopped() {
|
private void playbackStopped() {
|
||||||
playing = false;
|
playing = false;
|
||||||
|
|
||||||
if( disposeAfterPlay ) {
|
if( disposeAfterPlay ) {
|
||||||
this.dispose();
|
this.dispose();
|
||||||
disposeAfterPlay = false;
|
disposeAfterPlay = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addAudioListener( AudioListener listener ) {
|
||||||
|
initializeEventDispatcher().addListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeAudioListener( AudioListener listener ) {
|
||||||
|
initializeEventDispatcher().removeListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interne Methode, um den Listener-Mechanismus zu initialisieren. Wird erst
|
||||||
|
* aufgerufen, sobald sich der erste Listener anmelden möchte.
|
||||||
|
*
|
||||||
|
* @return Der {@code EventDispatcher} für dieses Objekt.
|
||||||
|
*/
|
||||||
|
private EventDispatcher<Audio, AudioListener> initializeEventDispatcher() {
|
||||||
|
if( eventDispatcher == null ) {
|
||||||
|
eventDispatcher = new EventDispatcher<>();
|
||||||
|
eventDispatcher.registerEventType("start", ( a, l ) -> l.playbackStarted(a));
|
||||||
|
eventDispatcher.registerEventType("stop", ( a, l ) -> l.playbackStopped(a));
|
||||||
|
}
|
||||||
|
return eventDispatcher;
|
||||||
|
}
|
||||||
|
|
||||||
private static final Log LOG = Log.getLogger(Sound.class);
|
private static final Log LOG = Log.getLogger(Sound.class);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,32 +93,20 @@ public final class DoubleMatrix implements MLMatrix {
|
|||||||
rows * columns);
|
rows * columns);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public int columns() {
|
public int columns() {
|
||||||
return columns;
|
return columns;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public int rows() {
|
public int rows() {
|
||||||
return rows;
|
return rows;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
int idx( int r, int c ) {
|
int idx( int r, int c ) {
|
||||||
return c * rows + r;
|
return c * rows + r;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public double get( int row, int col ) {
|
public double get( int row, int col ) {
|
||||||
try {
|
try {
|
||||||
@@ -128,9 +116,6 @@ public final class DoubleMatrix implements MLMatrix {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public MLMatrix set( int row, int col, double value ) {
|
public MLMatrix set( int row, int col, double value ) {
|
||||||
try {
|
try {
|
||||||
@@ -141,52 +126,34 @@ public final class DoubleMatrix implements MLMatrix {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public MLMatrix initializeRandom() {
|
public MLMatrix initializeRandom() {
|
||||||
return initializeRandom(-1.0, 1.0);
|
return initializeRandom(-1.0, 1.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public MLMatrix initializeRandom( double lower, double upper ) {
|
public MLMatrix initializeRandom( double lower, double upper ) {
|
||||||
applyInPlace(( d ) -> ((upper - lower) * Constants.random()) + lower);
|
applyInPlace(( d ) -> ((upper - lower) * Constants.random()) + lower);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public MLMatrix initializeOne() {
|
public MLMatrix initializeOne() {
|
||||||
applyInPlace(( d ) -> 1.0);
|
applyInPlace(( d ) -> 1.0);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public MLMatrix initializeZero() {
|
public MLMatrix initializeZero() {
|
||||||
applyInPlace(( d ) -> 0.0);
|
applyInPlace(( d ) -> 0.0);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public MLMatrix duplicate() {
|
public MLMatrix duplicate() {
|
||||||
return new DoubleMatrix(this);
|
return new DoubleMatrix(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public MLMatrix multiplyTransposed( MLMatrix B ) {
|
public MLMatrix multiplyTransposed( MLMatrix B ) {
|
||||||
/*return new DoubleMatrix(IntStream.range(0, rows).parallel().mapToObj(
|
/*return new DoubleMatrix(IntStream.range(0, rows).parallel().mapToObj(
|
||||||
@@ -208,9 +175,6 @@ public final class DoubleMatrix implements MLMatrix {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public MLMatrix multiplyAddBias( final MLMatrix B, final MLMatrix C ) {
|
public MLMatrix multiplyAddBias( final MLMatrix B, final MLMatrix C ) {
|
||||||
/*return new DoubleMatrix(IntStream.range(0, rows).parallel().mapToObj(
|
/*return new DoubleMatrix(IntStream.range(0, rows).parallel().mapToObj(
|
||||||
@@ -233,9 +197,6 @@ public final class DoubleMatrix implements MLMatrix {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public MLMatrix transposedMultiplyAndScale( final MLMatrix B, final double scalar ) {
|
public MLMatrix transposedMultiplyAndScale( final MLMatrix B, final double scalar ) {
|
||||||
/*return new DoubleMatrix(IntStream.range(0, columns).parallel().mapToObj(
|
/*return new DoubleMatrix(IntStream.range(0, columns).parallel().mapToObj(
|
||||||
@@ -258,9 +219,6 @@ public final class DoubleMatrix implements MLMatrix {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public MLMatrix add( MLMatrix B ) {
|
public MLMatrix add( MLMatrix B ) {
|
||||||
/*return new DoubleMatrix(IntStream.range(0, rows).parallel().mapToObj(
|
/*return new DoubleMatrix(IntStream.range(0, rows).parallel().mapToObj(
|
||||||
@@ -277,9 +235,6 @@ public final class DoubleMatrix implements MLMatrix {
|
|||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public MLMatrix addInPlace( MLMatrix B ) {
|
public MLMatrix addInPlace( MLMatrix B ) {
|
||||||
for( int j = 0; j < columns; j++ ) {
|
for( int j = 0; j < columns; j++ ) {
|
||||||
@@ -290,9 +245,6 @@ public final class DoubleMatrix implements MLMatrix {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public MLMatrix sub( MLMatrix B ) {
|
public MLMatrix sub( MLMatrix B ) {
|
||||||
/*return new DoubleMatrix(IntStream.range(0, rows).parallel().mapToObj(
|
/*return new DoubleMatrix(IntStream.range(0, rows).parallel().mapToObj(
|
||||||
@@ -309,9 +261,6 @@ public final class DoubleMatrix implements MLMatrix {
|
|||||||
return diff;
|
return diff;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public MLMatrix colSums() {
|
public MLMatrix colSums() {
|
||||||
/*DoubleMatrix colSums = new DoubleMatrix(1, columns);
|
/*DoubleMatrix colSums = new DoubleMatrix(1, columns);
|
||||||
@@ -331,9 +280,6 @@ public final class DoubleMatrix implements MLMatrix {
|
|||||||
return colSums;
|
return colSums;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public MLMatrix scaleInPlace( final double scalar ) {
|
public MLMatrix scaleInPlace( final double scalar ) {
|
||||||
for( int i = 0; i < coefficients.length; i++ ) {
|
for( int i = 0; i < coefficients.length; i++ ) {
|
||||||
@@ -342,9 +288,6 @@ public final class DoubleMatrix implements MLMatrix {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public MLMatrix scaleInPlace( final MLMatrix S ) {
|
public MLMatrix scaleInPlace( final MLMatrix S ) {
|
||||||
for( int j = 0; j < columns; j++ ) {
|
for( int j = 0; j < columns; j++ ) {
|
||||||
@@ -355,9 +298,6 @@ public final class DoubleMatrix implements MLMatrix {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public MLMatrix apply( DoubleUnaryOperator op ) {
|
public MLMatrix apply( DoubleUnaryOperator op ) {
|
||||||
DoubleMatrix result = new DoubleMatrix(rows, columns);
|
DoubleMatrix result = new DoubleMatrix(rows, columns);
|
||||||
@@ -367,9 +307,6 @@ public final class DoubleMatrix implements MLMatrix {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public MLMatrix applyInPlace( DoubleUnaryOperator op ) {
|
public MLMatrix applyInPlace( DoubleUnaryOperator op ) {
|
||||||
for( int i = 0; i < coefficients.length; i++ ) {
|
for( int i = 0; i < coefficients.length; i++ ) {
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package schule.ngb.zm.shapes;
|
package schule.ngb.zm.shapes;
|
||||||
|
|
||||||
import schule.ngb.zm.*;
|
import schule.ngb.zm.BasicDrawable;
|
||||||
|
import schule.ngb.zm.Fillable;
|
||||||
|
import schule.ngb.zm.Options;
|
||||||
|
import schule.ngb.zm.Strokeable;
|
||||||
|
|
||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.geom.AffineTransform;
|
import java.awt.geom.AffineTransform;
|
||||||
@@ -142,9 +145,6 @@ public abstract class Shape extends BasicDrawable {
|
|||||||
*/
|
*/
|
||||||
public abstract double getHeight();
|
public abstract double getHeight();
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void setGradient( schule.ngb.zm.Color from, schule.ngb.zm.Color to, Options.Direction dir ) {
|
public void setGradient( schule.ngb.zm.Color from, schule.ngb.zm.Color to, Options.Direction dir ) {
|
||||||
Point2D apDir = getAbsAnchorPoint(dir);
|
Point2D apDir = getAbsAnchorPoint(dir);
|
||||||
@@ -152,9 +152,6 @@ public abstract class Shape extends BasicDrawable {
|
|||||||
setGradient(apInv.getX(), apInv.getY(), from, apDir.getX(), apDir.getY(), to);
|
setGradient(apInv.getX(), apInv.getY(), from, apDir.getX(), apDir.getY(), to);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void setGradient( schule.ngb.zm.Color from, schule.ngb.zm.Color to ) {
|
public void setGradient( schule.ngb.zm.Color from, schule.ngb.zm.Color to ) {
|
||||||
Point2D ap = getAbsAnchorPoint(CENTER);
|
Point2D ap = getAbsAnchorPoint(CENTER);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public class BarChart extends Rectangle {
|
public class BarChart extends Rectangle {
|
||||||
|
|
||||||
public static String DEFAULT_LABEL = "%.2f";
|
public static String DEFAULT_LABEL = "%.2f";
|
||||||
@@ -105,10 +106,10 @@ public class BarChart extends Rectangle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void addValue( double pValue, Color pColor ) {
|
public void addValue( double pValue, Color pColor ) {
|
||||||
addValue(pValue, pColor, String.format(DEFAULT_LABEL, pValue));
|
addValue(pValue, String.format(DEFAULT_LABEL, pValue), pColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addValue( double pValue, Color pColor, String pLabel ) {
|
public void addValue( double pValue, String pLabel, Color pColor ) {
|
||||||
addValue(new BasicChartValue(pValue, pLabel, pColor));
|
addValue(new BasicChartValue(pValue, pLabel, pColor));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -136,57 +136,36 @@ public class BasicChartValue implements ChartValue {
|
|||||||
this.color = color;
|
this.color = color;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public double getX() {
|
public double getX() {
|
||||||
return xValue;
|
return xValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public double getValue() {
|
public double getValue() {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void setValue( double pValue ) {
|
public void setValue( double pValue ) {
|
||||||
this.value = pValue;
|
this.value = pValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public String getLabel() {
|
public String getLabel() {
|
||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void setLabel( String pLabel ) {
|
public void setLabel( String pLabel ) {
|
||||||
this.label = pLabel;
|
this.label = pLabel;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public Color getColor() {
|
public Color getColor() {
|
||||||
return color;
|
return color;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void setColor( Color pColor ) {
|
public void setColor( Color pColor ) {
|
||||||
this.color = pColor;
|
this.color = pColor;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import java.util.List;
|
|||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Eine Hilfsklasse um Dinge zu zählen.
|
* Eine Helferklasse, um Dinge zu zählen.
|
||||||
* <p>
|
* <p>
|
||||||
* Im einfachsten Fall kann der Zähler als geteilte Zählvariable genutzt werden,
|
* Im einfachsten Fall kann der Zähler als geteilte Zählvariable genutzt werden,
|
||||||
* die mit {@link #inc()} und {@link #dec()} aus verschiedenen Objekten oder
|
* die mit {@link #inc()} und {@link #dec()} aus verschiedenen Objekten oder
|
||||||
@@ -13,11 +13,11 @@ import java.util.concurrent.atomic.AtomicInteger;
|
|||||||
* <p>
|
* <p>
|
||||||
* Der Zähler kann aber auch Objekte zählen, indem die Instanzen an
|
* Der Zähler kann aber auch Objekte zählen, indem die Instanzen an
|
||||||
* {@link #count(Object)} übergeben werden. Am Ende kann mit {@link #getCount()}
|
* {@link #count(Object)} übergeben werden. Am Ende kann mit {@link #getCount()}
|
||||||
* die Anzahl der Objkete abgerufen werden.
|
* die Anzahl der Objekte abgerufen werden.
|
||||||
* <p>
|
* <p>
|
||||||
* Handelt es sich bei den Objekten um Zahlen, dann merkt sich ein Zähler auch
|
* Handelt es sich bei den Objekten um Zahlen, dann merkt sich ein
|
||||||
* das Maximum, das Minimum, die Summe und den Durchschnitt der gezählten
|
* {@code Counter} auch das Maximum, das Minimum, die Summe und den Durchschnitt
|
||||||
* Werte.
|
* der gezählten Werte.
|
||||||
* <p>
|
* <p>
|
||||||
* Ein Zähler kann auch komplette Arrays oder Listen von Zahlen zählen und die
|
* Ein Zähler kann auch komplette Arrays oder Listen von Zahlen zählen und die
|
||||||
* obigen Statistiken auswerten.
|
* obigen Statistiken auswerten.
|
||||||
@@ -96,31 +96,42 @@ public final class Counter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gibt die aktuelle Anzahl zurück.
|
|
||||||
* @return Die aktuelle Anzahl.
|
* @return Die aktuelle Anzahl.
|
||||||
*/
|
*/
|
||||||
public int getCount() {
|
public int getCount() {
|
||||||
return count.get();
|
return count.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Das Maxium der bisher gezählten Werte.
|
||||||
|
*/
|
||||||
public double getMax() {
|
public double getMax() {
|
||||||
synchronized( count ) {
|
synchronized( count ) {
|
||||||
return max;
|
return max;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Das Minimum der bisher gezählten Werte.
|
||||||
|
*/
|
||||||
public double getMin() {
|
public double getMin() {
|
||||||
synchronized( count ) {
|
synchronized( count ) {
|
||||||
return min;
|
return min;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Die Summe der bisher gezählten Werte.
|
||||||
|
*/
|
||||||
public double getSum() {
|
public double getSum() {
|
||||||
synchronized( count ) {
|
synchronized( count ) {
|
||||||
return sum;
|
return sum;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Der Mittelwert der bisher gezählten Werte.
|
||||||
|
*/
|
||||||
public double getAvg() {
|
public double getAvg() {
|
||||||
if( Double.isNaN(sum) ) {
|
if( Double.isNaN(sum) ) {
|
||||||
return Double.NaN;
|
return Double.NaN;
|
||||||
@@ -129,13 +140,27 @@ public final class Counter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt den Zähler auf den angegebenen Wert.
|
||||||
|
* <p>
|
||||||
|
* Die anderen Statistiken werden nicht verändert.
|
||||||
|
*
|
||||||
|
* @param count Der neue Wert des Zählers.
|
||||||
|
* @return Dieser Zähler selbst (method chaining).
|
||||||
|
*/
|
||||||
@SuppressWarnings( "UnusedReturnValue" )
|
@SuppressWarnings( "UnusedReturnValue" )
|
||||||
public Counter setCount( int count ) {
|
public Counter setCount( int count ) {
|
||||||
this.count.set(count);
|
this.count.set(count);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Setzt den Zähler auf Null.
|
||||||
|
* <p>
|
||||||
|
* Die Statistiken werden auf {@link Double#NaN} gesetzt.
|
||||||
|
*
|
||||||
|
* @return Dieser Zähler selbst (method chaining).
|
||||||
|
*/
|
||||||
@SuppressWarnings( "UnusedReturnValue" )
|
@SuppressWarnings( "UnusedReturnValue" )
|
||||||
public Counter reset() {
|
public Counter reset() {
|
||||||
count.set(0);
|
count.set(0);
|
||||||
@@ -147,18 +172,40 @@ public final class Counter {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erhöht den Zähler um Eins.
|
||||||
|
* <p>
|
||||||
|
* Die anderen Statistiken werden nicht verändert.
|
||||||
|
*
|
||||||
|
* @return Dieser Zähler selbst (method chaining).
|
||||||
|
*/
|
||||||
@SuppressWarnings( "UnusedReturnValue" )
|
@SuppressWarnings( "UnusedReturnValue" )
|
||||||
public Counter inc() {
|
public Counter inc() {
|
||||||
this.count.incrementAndGet();
|
this.count.incrementAndGet();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verringert den Zähler um Eins.
|
||||||
|
* <p>
|
||||||
|
* Die anderen Statistiken werden nicht verändert.
|
||||||
|
*
|
||||||
|
* @return Dieser Zähler selbst (method chaining).
|
||||||
|
*/
|
||||||
@SuppressWarnings( "UnusedReturnValue" )
|
@SuppressWarnings( "UnusedReturnValue" )
|
||||||
public Counter dec() {
|
public Counter dec() {
|
||||||
this.count.decrementAndGet();
|
this.count.decrementAndGet();
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zählt den angegebenen Wert.
|
||||||
|
* <p>
|
||||||
|
* Erhöht den Zähler um Eins und aktualisiert die Statistiken.
|
||||||
|
*
|
||||||
|
* @param value Der neue Wert.
|
||||||
|
* @return Dieser Zähler selbst (method chaining).
|
||||||
|
*/
|
||||||
@SuppressWarnings( "UnusedReturnValue" )
|
@SuppressWarnings( "UnusedReturnValue" )
|
||||||
public Counter count( double value ) {
|
public Counter count( double value ) {
|
||||||
inc();
|
inc();
|
||||||
@@ -173,11 +220,27 @@ public final class Counter {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zählt die angegebene Zahl.
|
||||||
|
* <p>
|
||||||
|
* Erhöht den Zähler um Eins und aktualisiert die Statistiken.
|
||||||
|
*
|
||||||
|
* @param num Die neue Zahl.
|
||||||
|
* @return Dieser Zähler selbst (method chaining).
|
||||||
|
*/
|
||||||
@SuppressWarnings( "UnusedReturnValue" )
|
@SuppressWarnings( "UnusedReturnValue" )
|
||||||
public Counter count( Number num ) {
|
public Counter count( Number num ) {
|
||||||
return count(num.doubleValue());
|
return count(num.doubleValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zählt das angegebenen Objekt.
|
||||||
|
* <p>
|
||||||
|
* Erhöht den Zähler um Eins.
|
||||||
|
*
|
||||||
|
* @param obj Ein beliebiges Objekt.
|
||||||
|
* @return Dieser Zähler selbst (method chaining).
|
||||||
|
*/
|
||||||
@SuppressWarnings( "UnusedReturnValue" )
|
@SuppressWarnings( "UnusedReturnValue" )
|
||||||
public Counter count( Object obj ) {
|
public Counter count( Object obj ) {
|
||||||
if( obj instanceof Number ) {
|
if( obj instanceof Number ) {
|
||||||
@@ -188,6 +251,13 @@ public final class Counter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zöhlt alle Werte im angegebenen Array.
|
||||||
|
*
|
||||||
|
* @param values Das Array der neuen Werte.
|
||||||
|
* @return Dieser Zähler selbst (method chaining).
|
||||||
|
* @see #count(double)
|
||||||
|
*/
|
||||||
@SuppressWarnings( "UnusedReturnValue" )
|
@SuppressWarnings( "UnusedReturnValue" )
|
||||||
public synchronized Counter countAll( double[] values ) {
|
public synchronized Counter countAll( double[] values ) {
|
||||||
for( double value : values ) {
|
for( double value : values ) {
|
||||||
@@ -196,6 +266,13 @@ public final class Counter {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zöhlt alle Werte im angegebenen Array.
|
||||||
|
*
|
||||||
|
* @param values Das Array der neuen Werte.
|
||||||
|
* @return Dieser Zähler selbst (method chaining).
|
||||||
|
* @see #count(double)
|
||||||
|
*/
|
||||||
@SuppressWarnings( "UnusedReturnValue" )
|
@SuppressWarnings( "UnusedReturnValue" )
|
||||||
public synchronized Counter countAll( int[] values ) {
|
public synchronized Counter countAll( int[] values ) {
|
||||||
for( double value : values ) {
|
for( double value : values ) {
|
||||||
@@ -204,6 +281,13 @@ public final class Counter {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zöhlt alle Zahlen im angegebenen Array.
|
||||||
|
*
|
||||||
|
* @param values Das Array der neuen Zahlen.
|
||||||
|
* @return Dieser Zähler selbst (method chaining).
|
||||||
|
* @see #count(Number)
|
||||||
|
*/
|
||||||
@SuppressWarnings( "UnusedReturnValue" )
|
@SuppressWarnings( "UnusedReturnValue" )
|
||||||
public synchronized Counter countAll( Number[] values ) {
|
public synchronized Counter countAll( Number[] values ) {
|
||||||
for( Number value : values ) {
|
for( Number value : values ) {
|
||||||
@@ -212,6 +296,13 @@ public final class Counter {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Zöhlt alle Zahlen in der angegebenen Sammlung.
|
||||||
|
*
|
||||||
|
* @param values Die Sammlung der neuen Zahlen.
|
||||||
|
* @return Dieser Zähler selbst (method chaining).
|
||||||
|
* @see #count(Number)
|
||||||
|
*/
|
||||||
@SuppressWarnings( "UnusedReturnValue" )
|
@SuppressWarnings( "UnusedReturnValue" )
|
||||||
public synchronized Counter countAll( Collection<Number> values ) {
|
public synchronized Counter countAll( Collection<Number> values ) {
|
||||||
for( Number value : values ) {
|
for( Number value : values ) {
|
||||||
|
|||||||
@@ -2,8 +2,6 @@ package schule.ngb.zm.util;
|
|||||||
|
|
||||||
import schule.ngb.zm.Color;
|
import schule.ngb.zm.Color;
|
||||||
import schule.ngb.zm.Constants;
|
import schule.ngb.zm.Constants;
|
||||||
import schule.ngb.zm.Zeichenmaschine;
|
|
||||||
import schule.ngb.zm.util.io.FileLoader;
|
|
||||||
import schule.ngb.zm.util.io.ImageLoader;
|
import schule.ngb.zm.util.io.ImageLoader;
|
||||||
|
|
||||||
import java.awt.Font;
|
import java.awt.Font;
|
||||||
@@ -11,46 +9,69 @@ import java.awt.FontMetrics;
|
|||||||
import java.awt.Graphics2D;
|
import java.awt.Graphics2D;
|
||||||
import java.awt.font.LineMetrics;
|
import java.awt.font.LineMetrics;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.ZoneOffset;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import static java.lang.Math.exp;
|
||||||
|
import static java.lang.Math.log;
|
||||||
|
import static schule.ngb.zm.Constants.random;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hilfsklasse, um zufällige Beispieldaten zu erzeugen.
|
* Hilfsklasse, um zufällige Beispieldaten zu erzeugen.
|
||||||
* <p>
|
* <p>
|
||||||
* Die Klasse kann verschiedene Arten realistischer Beispieldaten erzeugen,
|
* Die Klasse kann verschiedene Arten realistischer Beispieldaten erzeugen,
|
||||||
* unter anderem Namen, E-Mail-Adressen, Passwörter oder Platzhalterbilder.
|
* unter anderem Namen, E-Mail-Adressen, Passwörter oder Platzhalter-Bilder.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings( "unused" )
|
||||||
public final class Faker {
|
public final class Faker {
|
||||||
|
|
||||||
public static final String FAKE_IMG_URL = "https://loremflickr.com/%d/%d";
|
public static final String FAKE_IMG_URL = "https://loremflickr.com/%d/%d";
|
||||||
|
|
||||||
|
public static void main( String[] args ) {
|
||||||
|
String text = Faker.fakeText(2000, 8);
|
||||||
|
System.out.println(text);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Erzeugt ein Array mit den angegebenen Anzahl zufälliger Nutzernamen.
|
* Erzeugt ein Array mit den angegebenen Anzahl zufälliger Benutzerdaten.
|
||||||
|
* <p>
|
||||||
|
* Jeder Datensatz besteht aus einem String-Array mit den folgenden Daten
|
||||||
|
* <ul>
|
||||||
|
* <li><code>fakeUser[i][0]</code>: Vorname</li>
|
||||||
|
* <li><code>fakeUser[i][1]</code>: Nachname</li>
|
||||||
|
* <li><code>fakeUser[i][2]</code>: Geschlecht</li>
|
||||||
|
* <li><code>fakeUser[i][3]</code>: Nutzername</li>
|
||||||
|
* <li><code>fakeUser[i][4]</code>: Passwort</li>
|
||||||
|
* <li><code>fakeUser[i][5]</code>: E-Mail</li>
|
||||||
|
* <li><code>fakeUser[i][6]</code>: Geburtsdatum</li>
|
||||||
|
* </ul>
|
||||||
*
|
*
|
||||||
* @param n Anzahl der Beispieldaten.
|
* @param count Anzahl der Beispieldaten.
|
||||||
* @return Ein Array mit den Beispieldaten.
|
* @return Ein Array mit den Beispieldaten.
|
||||||
*/
|
*/
|
||||||
public static String[][] fakeUsers( int n ) {
|
public static String[][] fakeUsers( int count ) {
|
||||||
String[][] data = loadMockfile("users");
|
return randomSample("users", count, ( line ) -> line.split(","), String[].class);
|
||||||
String[][] result = new String[n][];
|
|
||||||
for( int i = 0; i < n; i++ ) {
|
|
||||||
result[i] = Constants.choice(data);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Erzeugt ein Array mit den angegebenen Anzahl zufälliger Vornamen.
|
* Erzeugt ein Array mit den angegebenen Anzahl zufälliger Vornamen.
|
||||||
*
|
*
|
||||||
* @param n Anzahl der Beispieldaten.
|
* @param count Anzahl der Beispieldaten.
|
||||||
* @return Ein Array mit den Beispieldaten.
|
* @return Ein Array mit den Beispieldaten.
|
||||||
*/
|
*/
|
||||||
public static String[] fakeNames( int n ) {
|
public static String[] fakeNames( int count ) {
|
||||||
String[][] data = loadMockfile("users");
|
return randomSample("users", count, ( line ) -> line.split(",")[0], String.class);
|
||||||
String[] result = new String[n];
|
|
||||||
for( int i = 0; i < n; i++ ) {
|
|
||||||
String[] row = Constants.choice(data);
|
|
||||||
result[i] = row[0];
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -58,65 +79,187 @@ public final class Faker {
|
|||||||
* Erzeugt ein Array mit den angegebenen Anzahl zufälliger Namen (Vor- und
|
* Erzeugt ein Array mit den angegebenen Anzahl zufälliger Namen (Vor- und
|
||||||
* Nachname).
|
* Nachname).
|
||||||
*
|
*
|
||||||
* @param n Anzahl der Beispieldaten.
|
* @param count Anzahl der Beispieldaten.
|
||||||
* @return Ein Array mit den Beispieldaten.
|
* @return Ein Array mit den Beispieldaten.
|
||||||
*/
|
*/
|
||||||
public static String[] fakeFullnames( int n ) {
|
public static String[] fakeFullnames( int count ) {
|
||||||
String[][] data = loadMockfile("users");
|
return randomSample("users", count, ( line ) -> {
|
||||||
String[] result = new String[n];
|
String[] parts = line.split(",");
|
||||||
for( int i = 0; i < n; i++ ) {
|
return parts[0] + " " + parts[1];
|
||||||
String[] row = Constants.choice(data);
|
}, String.class);
|
||||||
result[i] = row[0] + " " + row[1];
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Erzeugt ein Array mit den angegebenen Anzahl zufälliger Nutzernamen.
|
* Erzeugt ein Array mit den angegebenen Anzahl zufälliger Nutzernamen.
|
||||||
*
|
*
|
||||||
* @param n Anzahl der Beispieldaten.
|
* @param count Anzahl der Beispieldaten.
|
||||||
* @return Ein Array mit den Beispieldaten.
|
* @return Ein Array mit den Beispieldaten.
|
||||||
*/
|
*/
|
||||||
public static String[] fakeUsernames( int n ) {
|
public static String[] fakeUsernames( int count ) {
|
||||||
String[][] data = loadMockfile("users");
|
return randomSample("users", count, ( line ) -> line.split(",")[3], String.class);
|
||||||
String[] result = new String[n];
|
|
||||||
for( int i = 0; i < n; i++ ) {
|
|
||||||
String[] row = Constants.choice(data);
|
|
||||||
result[i] = row[3];
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Erzeugt ein Array mit den angegebenen Anzahl zufälliger Passwörter.
|
* Erzeugt ein Array mit den angegebenen Anzahl zufälliger Passwörter.
|
||||||
*
|
*
|
||||||
* @param n Anzahl der Beispieldaten.
|
* @param count Anzahl der Beispieldaten.
|
||||||
* @return Ein Array mit den Beispieldaten.
|
* @return Ein Array mit den Beispieldaten.
|
||||||
*/
|
*/
|
||||||
public static String[] fakePasswords( int n ) {
|
public static String[] fakePasswords( int count ) {
|
||||||
String[][] data = loadMockfile("users");
|
return randomSample("users", count, ( line ) -> line.split(",")[4], String.class);
|
||||||
String[] result = new String[n];
|
|
||||||
for( int i = 0; i < n; i++ ) {
|
|
||||||
String[] row = Constants.choice(data);
|
|
||||||
result[i] = row[4];
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Erzeugt ein Array mit den angegebenen Anzahl zufälliger E-Mail-Adressen.
|
* Erzeugt ein Array mit den angegebenen Anzahl zufälliger E-Mail-Adressen.
|
||||||
*
|
*
|
||||||
* @param n Anzahl der Beispieldaten.
|
* @param count Anzahl der Beispieldaten.
|
||||||
* @return Ein Array mit den Beispieldaten.
|
* @return Ein Array mit den Beispieldaten.
|
||||||
*/
|
*/
|
||||||
public static String[] fakeEmails( int n ) {
|
public static String[] fakeEmails( int count ) {
|
||||||
String[][] data = loadMockfile("users");
|
return randomSample("users", count, ( line ) -> line.split(",")[5], String.class);
|
||||||
String[] result = new String[n];
|
|
||||||
for( int i = 0; i < n; i++ ) {
|
|
||||||
String[] row = Constants.choice(data);
|
|
||||||
result[i] = row[5];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erzeugt ein Array mit den angegebenen Anzahl zufälliger deutscher
|
||||||
|
* Wörter.
|
||||||
|
*
|
||||||
|
* @param count Anzahl der Beispieldaten.
|
||||||
|
* @return Ein Array mit den Beispieldaten.
|
||||||
|
*/
|
||||||
|
public static String[] fakeStrings( int count ) {
|
||||||
|
return randomSample("words", count, ( line ) -> line, String.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erzeugt ein Array mit den angegebenen Anzahl zufälliger
|
||||||
|
* {@code Date}-Objekte.
|
||||||
|
*
|
||||||
|
* @param count Anzahl der Beispieldaten.
|
||||||
|
* @return Ein Array mit den Beispieldaten.
|
||||||
|
*/
|
||||||
|
public static LocalDate[] fakeDates( int count ) {
|
||||||
|
long nowEpoch = LocalDate.now().toEpochDay();
|
||||||
|
long from = LocalDate.ofEpochDay(nowEpoch - 18 * 365).toEpochDay();
|
||||||
|
long to = LocalDate.ofEpochDay(nowEpoch - 14 * 365).toEpochDay();
|
||||||
|
|
||||||
|
LocalDate[] result = new LocalDate[count];
|
||||||
|
for( int i = 0; i < count; i++ ) {
|
||||||
|
result[i] = LocalDate.ofEpochDay((int) Constants.interpolate(from, to, random()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LocalDateTime[] fakeDatetimes( int count ) {
|
||||||
|
long nowEpoch = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC);
|
||||||
|
long from = LocalDateTime.ofEpochSecond(nowEpoch - 18 * 365, 0, ZoneOffset.UTC).toEpochSecond(ZoneOffset.UTC);
|
||||||
|
long to = LocalDateTime.ofEpochSecond(nowEpoch - 14 * 365, 0, ZoneOffset.UTC).toEpochSecond(ZoneOffset.UTC);
|
||||||
|
|
||||||
|
LocalDateTime[] result = new LocalDateTime[count];
|
||||||
|
for( int i = 0; i < count; i++ ) {
|
||||||
|
result[i] = LocalDateTime.ofEpochSecond((int) Constants.interpolate(from, to, random()), 0, ZoneOffset.UTC);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String fakeText( int words, int paragraphs ) {
|
||||||
|
String basetext = "";
|
||||||
|
try(
|
||||||
|
InputStream in = Faker.class.getResourceAsStream("mock-text.txt");
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(in))
|
||||||
|
) {
|
||||||
|
basetext = reader.readLine();
|
||||||
|
} catch( IOException ex ) {
|
||||||
|
LOG.error(ex, "Error generating fake blindtext: " + ex.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
String result = basetext.repeat(words / 283);
|
||||||
|
|
||||||
|
int w = (words % 283);
|
||||||
|
if( w > 0 ) {
|
||||||
|
Matcher m = Pattern.compile("([a-zA-Z_0-9\\u00C0-\\u017F]+\\W+){" + w + "}").matcher(basetext);
|
||||||
|
if( m.find() ) {
|
||||||
|
result += m.group().stripTrailing();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if( paragraphs > 1 ) {
|
||||||
|
int half = words / paragraphs;
|
||||||
|
final int maxLength = result.length();
|
||||||
|
Matcher m = Pattern.compile("([a-zA-Z_0-9\\u00C0-\\u017F]+\\W+){" + (half - 10) + "," + (half + 10) + "}([a-zA-Z_0-9\\u00C0-\\u017F]+)\\.").matcher(result);
|
||||||
|
if( m.find() ) {
|
||||||
|
result = m.replaceAll(( mr ) -> mr.end() == maxLength ? mr.group().trim() : mr.group().trim() + "\n\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int[] fakeIntArray( int count, int min, int max ) {
|
||||||
|
int[] arr = new int[count];
|
||||||
|
for( int i = 0; i < count; i++ ) {
|
||||||
|
arr[i] = random(min, max);
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Integer> fakeIntegerList( int count, int min, int max, List<Integer> list ) {
|
||||||
|
if( list == null ) {
|
||||||
|
list = new ArrayList<>(count);
|
||||||
|
}
|
||||||
|
for( int i = 0; i < count; i++ ) {
|
||||||
|
list.add(random(min, max));
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings( "unchecked" )
|
||||||
|
private static <T> T[] randomSample( String filename, int count, Function<String, T> transformer, Class<T> type ) {
|
||||||
|
T[] result = (T[]) Array.newInstance(type, count);
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
double k = count; // cast to double
|
||||||
|
|
||||||
|
double W = exp(log(random()) / k);
|
||||||
|
|
||||||
|
try(
|
||||||
|
InputStream in = Faker.class.getResourceAsStream("mock-" + filename + ".csv");
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(in))
|
||||||
|
) {
|
||||||
|
String line;
|
||||||
|
while( (line = reader.readLine()) != null ) {
|
||||||
|
if( i < count ) {
|
||||||
|
result[i] = transformer.apply(line);
|
||||||
|
i += 1;
|
||||||
|
} else {
|
||||||
|
int j = (int) (log(random()) / log(1 - W)) + 1;
|
||||||
|
while( j > 0 ) {
|
||||||
|
line = reader.readLine();
|
||||||
|
j -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( line != null ) {
|
||||||
|
result[random(0, count - 1)] = transformer.apply(line);
|
||||||
|
i += 1;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
W *= exp(log(random()) / k);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill remaining array
|
||||||
|
while( i < count ) {
|
||||||
|
result[i] = result[random(0, i - 1)];
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
} catch( IOException ex ) {
|
||||||
|
LOG.error(ex, "Error loading mock data file: " + ex.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,7 +308,7 @@ public final class Faker {
|
|||||||
FontMetrics fontMerics = graphics.getFontMetrics();
|
FontMetrics fontMerics = graphics.getFontMetrics();
|
||||||
LineMetrics lineMetrics = fontMerics.getLineMetrics(text, graphics);
|
LineMetrics lineMetrics = fontMerics.getLineMetrics(text, graphics);
|
||||||
graphics.drawString(text,
|
graphics.drawString(text,
|
||||||
(int) ((width - fontMerics.stringWidth(text)) / 2),
|
(width - fontMerics.stringWidth(text)) / 2,
|
||||||
(int) (height / 2 - lineMetrics.getDescent() + lineMetrics.getAscent() / 2)
|
(int) (height / 2 - lineMetrics.getDescent() + lineMetrics.getAscent() / 2)
|
||||||
);
|
);
|
||||||
graphics.dispose();
|
graphics.dispose();
|
||||||
@@ -176,15 +319,9 @@ public final class Faker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String fakeText( int words, int paragraphs ) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String[][] loadMockfile( String name ) {
|
|
||||||
return FileLoader.loadCsv("schule/ngb/zm/util/mock-" + name + ".csv", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Faker() {
|
private Faker() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final Log LOG = Log.getLogger(Faker.class);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package schule.ngb.zm.util;
|
|||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Zufallsgenerator für Perlin-Noise.
|
* Generator für Perlin-Noise.
|
||||||
* <p>
|
* <p>
|
||||||
* Die Implementierung basiert auf dem von Ken Perlin entwickelten Algorithmus
|
* Die Implementierung basiert auf dem von Ken Perlin entwickelten Algorithmus
|
||||||
* und wurde anhand der <a
|
* und wurde anhand der <a
|
||||||
@@ -18,7 +18,7 @@ public class Noise {
|
|||||||
private static final int M = N - 1;
|
private static final int M = N - 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interne Permutationstabelle für diesen Generator
|
* Interne Permutationstabelle für diesen Generator.
|
||||||
*/
|
*/
|
||||||
private int[] p;
|
private int[] p;
|
||||||
|
|
||||||
|
|||||||
@@ -3,13 +3,13 @@ package schule.ngb.zm.util;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Eine Hilfsklasse zur Zeitmessung im Nanosekundenbereich.
|
* Helferklasse zur Zeitmessung im Nanosekundenbereich.
|
||||||
* <p>
|
* <p>
|
||||||
* Die Klasse kann zum Beispiel genutzt werden, um die Laufzeit eines
|
* Mit einem {@code Timer} kann zum Beispiel die Laufzeit eines Algorithmus
|
||||||
* Quelltextes zu messen. Wie eine echte Stoppuhr läuft der {@code Timer}
|
* gemessen werden. Wie eine echte Stoppuhr läuft der {@code Timer} weiter, wenn
|
||||||
* weiter, wenn nach einem {@link #stop()} wieder {@link #start()} aufgerufen
|
* nach einem {@link #stop()} wieder {@link #start()} aufgerufen wird. Soll die
|
||||||
* wird. Soll die Zeitmessung wieder bei null beginnen, muss vorher
|
* Zeitmessung wieder bei null beginnen, muss vorher {@link #reset()} genutzt
|
||||||
* {@link #reset()} genutzt werden.
|
* werden.
|
||||||
* <p>
|
* <p>
|
||||||
* Die gemessene Zeit kann in {@link #getMillis() Millisekunden} oder
|
* Die gemessene Zeit kann in {@link #getMillis() Millisekunden} oder
|
||||||
* {@link #getSeconds() Sekunden} abgerufen werden. Wird eine noch größere
|
* {@link #getSeconds() Sekunden} abgerufen werden. Wird eine noch größere
|
||||||
@@ -100,7 +100,7 @@ public final class Timer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stoppt den {@code Timer}, wenn er derzeit läuft. Die gemessene Zeit wird
|
* Stoppt den {@code Timer}, wenn er derzeit läuft. Die gemessene Dauer wird
|
||||||
* zur Summe aller gemessenen Zeiten hinzuaddiert.
|
* zur Summe aller gemessenen Zeiten hinzuaddiert.
|
||||||
*
|
*
|
||||||
* @return Dieser {@code Timer} selbst (method chaining).
|
* @return Dieser {@code Timer} selbst (method chaining).
|
||||||
@@ -116,8 +116,8 @@ public final class Timer {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Setzt den {@code Timer} auf den Startzustand und löscht alle bisher
|
* Setzt den {@code Timer} auf den Startzustand und löscht alle bisher
|
||||||
* gemessenen Zeiten. Falls die Zeitmessung gerade läuft, wird sie nicht
|
* gemessenen Zeiten. Falls die Zeitmessung gerade läuft, stoppt sie nicht,
|
||||||
* gestoppt, sondern läuft vom Zeitpunkt des Aufrufs weiter.
|
* sondern startet vom Zeitpunkt des Aufrufs neu.
|
||||||
*
|
*
|
||||||
* @return Dieser {@code Timer} selbst (method chaining).
|
* @return Dieser {@code Timer} selbst (method chaining).
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ package schule.ngb.zm.util;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Statische Methoden, um Methodenparameter auf Gültigkeit zu prüfen.
|
||||||
|
*/
|
||||||
@SuppressWarnings( "UnusedReturnValue" )
|
@SuppressWarnings( "UnusedReturnValue" )
|
||||||
public class Validator {
|
public class Validator {
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,60 @@ import java.util.concurrent.ConcurrentMap;
|
|||||||
import java.util.concurrent.CopyOnWriteArraySet;
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementierung einer EventListener-API basierend auf dem Artikel
|
||||||
|
* <a href="https://org.coloradomesa.edu/~wmacevoy/listen/paper.html">Skilled
|
||||||
|
* Listening in Java</a> von Dr. Warren D. MacEvoy.
|
||||||
|
* <p>
|
||||||
|
* Der {@code EventDispatcher} verwaltet eine Liste von angemeldeten Listenern
|
||||||
|
* in einem {@link CopyOnWriteArraySet}, dass besonders für Situationen geeignet
|
||||||
|
* ist, in denen in der Regel wenige Objekte verwaltet werden müssen, auf die
|
||||||
|
* schnell zugegriffen werden soll und die sich selten ändern. Das trifft in den
|
||||||
|
* meisten Fällen auf die Listener eines Objekts zu.
|
||||||
|
* <p>
|
||||||
|
* Um einen {@code EventDispatcher} zu nutzen, muss eine Schnittstelle als
|
||||||
|
* Unterinterface von {@link Listener} erstellt werden, die Methoden für die
|
||||||
|
* einzelnen Events definiert. In der Regel wird eine Methode pro Event
|
||||||
|
* angelegt, aber dies ist nicht unbedingt notwendig.
|
||||||
|
* <p>
|
||||||
|
* Ein Objekt, dass Events für diese Art von Listener erzeugt, erstellt einen
|
||||||
|
* {@code EventDispatcher} und
|
||||||
|
* {@link #registerEventType(String, BiConsumer) registriert} die notwendigen
|
||||||
|
* Events.
|
||||||
|
*
|
||||||
|
* <pre><code>
|
||||||
|
* EventDispatcher<MyEvent, MyEventListener> dispatcher = new EventDispatcher<>();
|
||||||
|
* dispatcher.registerEventType("start", (evt, listener) -> listener.started(evt));
|
||||||
|
* dispatcher.registerEventType("stop", (evt, listener) -> listener.stopped(evt));
|
||||||
|
* </code></pre>
|
||||||
|
* <p>
|
||||||
|
* Hier werden zwei Events registriert "start" und "stop". Die Bezeichnung der
|
||||||
|
* Events wird nut intern verwendet. Jedes Event registriert eine Funktion, die
|
||||||
|
* den Aufruf der Listener-Schnittstelle umsetzt. In der Regel ist dies ein
|
||||||
|
* Lambda-Ausdruck von zwei Parametern: dem Event-Objekt vom Typ {@code E} und
|
||||||
|
* der Listener, der aufgerufen werden soll, vom Typ {@code L}.
|
||||||
|
* <p>
|
||||||
|
* Nun können {@code MyEventListener} angemeldet und Events ausgelöst werden.
|
||||||
|
*
|
||||||
|
* <pre><code>
|
||||||
|
* public void addEventListener( MyEventListener listener ) {
|
||||||
|
* dispatcher.addListener(listener);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* public void removeEventListener( MyEventListener listener ) {
|
||||||
|
* dispatcher.removeListener(listener);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* public void methodThatCreatesEvents() {
|
||||||
|
* dispatcher.dispatchEvent("start", new MyEvent());
|
||||||
|
* // Do something
|
||||||
|
* dispatcher.dispatchEvent("stop", new MyEvent());
|
||||||
|
* }
|
||||||
|
* </code></pre>
|
||||||
|
*
|
||||||
|
* @param <E> Typ der Event-Objekte.
|
||||||
|
* @param <L> Typ der verwendeten Listener-Schnittstelle.
|
||||||
|
*/
|
||||||
public class EventDispatcher<E, L extends Listener<E>> {
|
public class EventDispatcher<E, L extends Listener<E>> {
|
||||||
|
|
||||||
private final CopyOnWriteArraySet<L> listeners;
|
private final CopyOnWriteArraySet<L> listeners;
|
||||||
@@ -35,6 +89,7 @@ public class EventDispatcher<E, L extends Listener<E>> {
|
|||||||
listeners.remove(listener);
|
listeners.remove(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public boolean hasListeners() {
|
public boolean hasListeners() {
|
||||||
return !listeners.isEmpty();
|
return !listeners.isEmpty();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
package schule.ngb.zm.util.events;
|
package schule.ngb.zm.util.events;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generische Basisschnittstelle für die Implementierung von EventListenern.
|
||||||
|
*
|
||||||
|
* @param <E> Typ der Event-Objekte, die von diesem Listener empfangen werden.
|
||||||
|
* @see EventDispatcher
|
||||||
|
*/
|
||||||
public interface Listener<E> {
|
public interface Listener<E> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,37 +4,63 @@ import schule.ngb.zm.util.Log;
|
|||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URISyntaxException;
|
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Stream;
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helferklasse, um Textdateien in verschiedenen Formaten einzulesen.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings( "unused" )
|
||||||
public final class FileLoader {
|
public final class FileLoader {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Charset: ASCII
|
||||||
|
*/
|
||||||
public static final Charset ASCII = StandardCharsets.US_ASCII;
|
public static final Charset ASCII = StandardCharsets.US_ASCII;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Charset: UTF-8
|
||||||
|
*/
|
||||||
public static final Charset UTF8 = StandardCharsets.UTF_8;
|
public static final Charset UTF8 = StandardCharsets.UTF_8;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Charset: UTF-16
|
||||||
|
*/
|
||||||
public static final Charset UTF16 = StandardCharsets.UTF_16;
|
public static final Charset UTF16 = StandardCharsets.UTF_16;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Charset: ISO-8859-1
|
||||||
|
*/
|
||||||
public static final Charset ISO_8859_1 = StandardCharsets.ISO_8859_1;
|
public static final Charset ISO_8859_1 = StandardCharsets.ISO_8859_1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lädt die angegebene Textdatei zeilenweise in eine Liste.
|
||||||
|
* <p>
|
||||||
|
* Als {@link Charset} wird {@link #UTF8} verwendet.
|
||||||
|
*
|
||||||
|
* @param source Die Quelle der Textdatei.
|
||||||
|
* @return Eine Liste mit den Zeilen der Textdatei.
|
||||||
|
* @see #loadLines(String, Charset)
|
||||||
|
*/
|
||||||
public static List<String> loadLines( String source ) {
|
public static List<String> loadLines( String source ) {
|
||||||
return loadLines(source, UTF8);
|
return loadLines(source, UTF8);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lädt die angegebene Datei Zeilenweise in eine Liste.
|
* Lädt die angegebene Textdatei mit dem angegebenen Charset zeilenweise in
|
||||||
|
* eine Liste.
|
||||||
|
* <p>
|
||||||
|
* Am Ende jeder Zeile wird das Symbol für einen Zeilenumbruch ({@code \n})
|
||||||
|
* entfernt.
|
||||||
*
|
*
|
||||||
* @param source
|
* @param source Die Quelle der Textdatei.
|
||||||
* @param charset
|
* @param charset Das zu verwendene {@code Charset}.
|
||||||
* @return
|
* @return Eine Liste mit den Zeilen der Textdatei.
|
||||||
*/
|
*/
|
||||||
public static List<String> loadLines( String source, Charset charset ) {
|
public static List<String> loadLines( String source, Charset charset ) {
|
||||||
try( BufferedReader reader = ResourceStreamProvider.getReader(source, charset) ) {
|
try( BufferedReader reader = ResourceStreamProvider.getReader(source, charset) ) {
|
||||||
@@ -52,10 +78,28 @@ public final class FileLoader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lädt den Inhalt der angegebenen Textdatei vollständig in einen String.
|
||||||
|
* <p>
|
||||||
|
* Als {@link Charset} wird {@link #UTF8} verwendet.
|
||||||
|
*
|
||||||
|
* @param source Eine Quelle für die Textdatei (Absoluter Dateipfad,
|
||||||
|
* Dateipfad im Classpath oder Netzwerkressource)
|
||||||
|
* @return Der Inhalt der Textdatei.
|
||||||
|
*/
|
||||||
public static String loadText( String source ) {
|
public static String loadText( String source ) {
|
||||||
return loadText(source, UTF8);
|
return loadText(source, UTF8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lädt den Inhalt der angegebenen Textdatei mit dem angegebenen
|
||||||
|
* {@code Charset} vollständig in einen String.
|
||||||
|
*
|
||||||
|
* @param source Eine Quelle für die Textdatei (Absoluter Dateipfad,
|
||||||
|
* Dateipfad im Classpath oder Netzwerkressource)
|
||||||
|
* @param charset Der {@code Charset} der Textdatei.
|
||||||
|
* @return Der Inhalt der Textdatei.
|
||||||
|
*/
|
||||||
public static String loadText( String source, Charset charset ) {
|
public static String loadText( String source, Charset charset ) {
|
||||||
try( BufferedReader reader = ResourceStreamProvider.getReader(source, charset) ) {
|
try( BufferedReader reader = ResourceStreamProvider.getReader(source, charset) ) {
|
||||||
StringBuilder result = new StringBuilder();
|
StringBuilder result = new StringBuilder();
|
||||||
@@ -73,15 +117,16 @@ public final class FileLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lädt die Daten aus einer CSV Datei in ein zweidimensionales
|
* Lädt die Daten aus einer CSV-Datei in ein zweidimensionales
|
||||||
* String-Array.
|
* String-Array.
|
||||||
* <p>
|
* <p>
|
||||||
* Der Aufruf entspricht dem Aufruf von
|
* Der Aufruf entspricht
|
||||||
* <pre><code>
|
* <pre><code>
|
||||||
* FileLoader.loadCsv(source, ',', skipFirst, UTF8);
|
* FileLoader.loadCsv(source, ',', skipFirst, UTF8);
|
||||||
* </code></pre>
|
* </code></pre>
|
||||||
*
|
*
|
||||||
* @param source Die Quelle der CSV-Daten.
|
* @param source Eine Quelle für die CSV-Datei (Absoluter Dateipfad,
|
||||||
|
* * Dateipfad im Classpath oder Netzwerkressource)
|
||||||
* @param skipFirst Ob die erste Zeile übersprungen werden soll.
|
* @param skipFirst Ob die erste Zeile übersprungen werden soll.
|
||||||
* @return Ein Array mit den Daten als {@code String}s.
|
* @return Ein Array mit den Daten als {@code String}s.
|
||||||
* @see #loadCsv(String, char, boolean, Charset)
|
* @see #loadCsv(String, char, boolean, Charset)
|
||||||
@@ -91,12 +136,12 @@ public final class FileLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lädt die Daten aus einer CSV Datei in ein zweidimensionales
|
* Lädt die Daten aus einer CSV-Datei in ein zweidimensionales
|
||||||
* String-Array.
|
* String-Array.
|
||||||
* <p>
|
* <p>
|
||||||
* Die Methode ist nicht in der Lage, komplexe CSV-Dateien zu verarbeiten.
|
* Die Methode ist nicht in der Lage, komplexe CSV-Dateien zu verarbeiten.
|
||||||
* Insbesondere können Inhalte, die das Trennzeichen {@code separator}
|
* Insbesondere werden Inhalte, die das Trennzeichen {@code separator}
|
||||||
* enthalten, nicht korrekt erkannt werden. Das Trennzeichen wird unabhängig
|
* enthalten, nicht korrekt erkannt. Das Trennzeichen wird unabhängig
|
||||||
* vom Kontext immer als Zelltrenner erkannt. (Im Normalfall kann das
|
* vom Kontext immer als Zelltrenner erkannt. (Im Normalfall kann das
|
||||||
* Trennzeichen durch die Verwendung doppelter Anführungszeichen in der Art
|
* Trennzeichen durch die Verwendung doppelter Anführungszeichen in der Art
|
||||||
* {@code Inhalt,"Inhalt, der Komma enthält",Inhalt} maskiert werden.)
|
* {@code Inhalt,"Inhalt, der Komma enthält",Inhalt} maskiert werden.)
|
||||||
|
|||||||
@@ -13,39 +13,38 @@ import java.nio.file.Files;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helferklasse, um {@link InputStream}s für Resourcen zu erhalten.
|
* Helferklasse, um {@link InputStream}s für Ressourcen zu erhalten.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public class ResourceStreamProvider {
|
public class ResourceStreamProvider {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ermittelt zur angegebenen Quelle einen passenden {@link URL} (<em>Unified
|
* Ermittelt zur angegebenen Quelle einen passenden {@link URL} (<em>Unified
|
||||||
* Resource Locator</em>). Eine passende Datei-Resource wird wie folgt
|
* Resource Locator</em>). Eine passende Datei-Ressource wird wie folgt
|
||||||
* ermittelt:
|
* ermittelt:
|
||||||
* <ol>
|
* <ol>
|
||||||
* <li>Ist {@code source} eine existierende Datei
|
* <li>Ist {@code source} eine existierende Datei
|
||||||
* ({@code new File(source}.isFile() == true})?</li>
|
* ({@code new File(source}.isFile() == true})?</li>
|
||||||
* <li>Ist {@code source} ein relativer Pfad im Projekt ({@code getResource(source) != null})?.</li>
|
* <li>Ist {@code source} ein relativer Pfad im Projekt ({@code getResource(source) != null})?</li>
|
||||||
* <li>Ist {@code source} im Classpath enthalten ({@code getClassLoader().getResource(source) != null})?</li>
|
* <li>Ist {@code source} im Classpath enthalten ({@code getClassLoader().getResource(source) != null})?</li>
|
||||||
* <li>Ansonten erstellt ein {@link URL}-Objekt.</li>
|
* <li>Ansonsten erstelle ein {@link URL}-Objekt.</li>
|
||||||
* </ol>
|
* </ol>
|
||||||
* <p>
|
* <p>
|
||||||
* Ein {@code URL} für die erste gefundene Resource wird zurückgegeben.
|
* Ein {@code URL} für die erste gefundene Resource wird zurückgegeben.
|
||||||
* Auftretende Exceptions
|
|
||||||
* werden als {@link IOException} geworfen.
|
|
||||||
* <p>
|
* <p>
|
||||||
* Bei einer Exception werden die folgenden Quellen nicht mehr abgefragt.
|
* Auftretende Exceptions werden als {@link IOException} geworfen.
|
||||||
|
* Bei einer Exception werden die nachfolgenden Quellen nicht mehr abgefragt.
|
||||||
* Eine {@link java.net.MalformedURLException} beim Konstruieren des {@code URL}
|
* Eine {@link java.net.MalformedURLException} beim Konstruieren des {@code URL}
|
||||||
* zu einer Datei verhindert daher, dass noch im Classpath gesucht wird.
|
* zu einer Datei verhindert daher, dass noch im Classpath gesucht wird.
|
||||||
*
|
*
|
||||||
* @param source Eine Quelle für die Resource (Absoluter Dateipfad,
|
* @param source Eine Quelle für die Ressource (Absoluter Dateipfad,
|
||||||
* Dateipfad im Classpath oder Netzwerkresource)
|
* Dateipfad im Classpath oder Netzwerkressource)
|
||||||
* @return Ein {@code InputStream} für die Resource
|
* @return Ein {@code InputStream} für die Ressource
|
||||||
* @throws NullPointerException Falls {@code source} {@code null} ist.
|
* @throws NullPointerException Falls {@code source} {@code null} ist.
|
||||||
* @throws IllegalArgumentException Falls {@code source} ein leerer String
|
* @throws IllegalArgumentException Falls {@code source} ein leerer String
|
||||||
* ist.
|
* ist.
|
||||||
* @throws IOException Geworfen beim Erzeugen einer URL zu
|
* @throws IOException Geworfen beim Erzeugen einer URL zu
|
||||||
* einer bestehenden Resource.
|
* einer bestehenden Ressource.
|
||||||
*/
|
*/
|
||||||
public static URL getResourceURL( String source ) throws NullPointerException, IllegalArgumentException, IOException {
|
public static URL getResourceURL( String source ) throws NullPointerException, IllegalArgumentException, IOException {
|
||||||
Validator.requireNotNull(source, "Resource source may not be null");
|
Validator.requireNotNull(source, "Resource source may not be null");
|
||||||
@@ -66,12 +65,12 @@ public class ResourceStreamProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sucht eine zur angegebenen Quelle passende Resource und öffnet einen
|
* Sucht eine zur angegebenen Quelle passende Ressource und öffnet einen
|
||||||
* passenden {@link InputStream}. Die konkrete Art des Streams hängt davon
|
* passenden {@link InputStream}. Die konkrete Art des Streams hängt davon
|
||||||
* ab, welche Art an Resource gefunden wird:
|
* ab, welche Art an Ressource gefunden wird:
|
||||||
* <ol>
|
* <ol>
|
||||||
* <li>Ist {@code source} eine existierende Datei
|
* <li>Ist {@code source} eine existierende Datei
|
||||||
* {@code new File(source}.isFile() == true}, dann wird ein
|
* {@code new File(source).isFile() == true}, dann wird ein
|
||||||
* {@link FileInputStream} erstellt.</li>
|
* {@link FileInputStream} erstellt.</li>
|
||||||
* <li>Ist {@code source} ein relativer Pfad im Projekt, wird ein passender
|
* <li>Ist {@code source} ein relativer Pfad im Projekt, wird ein passender
|
||||||
* Stream mit {@link Class#getResourceAsStream(String)} geöffnet.</li>
|
* Stream mit {@link Class#getResourceAsStream(String)} geöffnet.</li>
|
||||||
@@ -81,35 +80,44 @@ public class ResourceStreamProvider {
|
|||||||
* {@link URL#openStream() Netwerkstream} geöffnet.</li>
|
* {@link URL#openStream() Netwerkstream} geöffnet.</li>
|
||||||
* </ol>
|
* </ol>
|
||||||
* <p>
|
* <p>
|
||||||
* Die Möglichen Resourcen werden in der gelisteten Reihenfolge durchprobiert
|
* Die Möglichen Ressourcen werden in der gelisteten Reihenfolge durchprobiert
|
||||||
* und der erste gefundene Stream zurückgegeben. Auftretende Exceptions
|
* und der erste gefundene Stream zurückgegeben. Auftretende Exceptions
|
||||||
* werden als {@link IOException} geworfen. Das bedeutet, falls für {@code source}
|
* werden als {@link IOException} geworfen. Das bedeutet, falls für {@code source}
|
||||||
* keine gültige Resource gefunden werden kann, wird am Ende die
|
* keine gültige Ressource gefunden werden kann, wird am Ende die
|
||||||
* von {@link URL#openStream()} erzeugte {@code IOException} geworfen.
|
* von {@link URL#openStream()} erzeugte {@code IOException} geworfen.
|
||||||
* <p>
|
* <p>
|
||||||
* Bei einer Exception werden die folgenden Quellen nicht mehr abgefragt.
|
* Bei einer Exception werden die nachfolgenden Quellen nicht mehr abgefragt.
|
||||||
* Eine {@link SecurityException} beim Öffnen des {@code FileInputStream}
|
* Eine {@link SecurityException} beim Öffnen des {@code FileInputStream}
|
||||||
* (zum Beispiel, weil auf eine existierende Datei keine Leserechte bestehen)
|
* (zum Beispiel, weil auf eine existierende Datei keine Leserechte bestehen)
|
||||||
* verhindert daher, dass noch im Classpath gesucht wird.
|
* verhindert daher, dass noch im Classpath gesucht wird.
|
||||||
*
|
*
|
||||||
* @param source Eine Quelle für die Resource (Absoluter Dateipfad,
|
* @param source Eine Quelle für die Ressource (Absoluter Dateipfad,
|
||||||
* Dateipfad im Classpath oder Netzwerkresource)
|
* Dateipfad im Classpath oder Netzwerkresource).
|
||||||
* @return Ein {@code InputStream} für die Resource
|
* @return Ein {@code InputStream} für die Ressource.
|
||||||
* @throws NullPointerException Falls {@code source} {@code null} ist.
|
* @throws NullPointerException Falls {@code source} {@code null} ist.
|
||||||
* @throws IllegalArgumentException Falls {@code source} ein leerer String
|
* @throws IllegalArgumentException Falls {@code source} ein leerer String
|
||||||
* ist.
|
* ist.
|
||||||
* @throws IOException Geworfen beim Öffnen des Streams zu
|
* @throws IOException Geworfen beim Öffnen des Streams zu
|
||||||
* einer bestehenden Resource oder falls
|
* einer bestehenden Ressource oder falls
|
||||||
* keine passende Resource gefunden wurde.
|
* keine passende Ressource gefunden
|
||||||
|
* wurde.
|
||||||
*/
|
*/
|
||||||
public static InputStream getInputStream( String source ) throws NullPointerException, IllegalArgumentException, IOException {
|
public static InputStream getInputStream( String source ) throws NullPointerException, IllegalArgumentException, IOException {
|
||||||
return getResourceURL(source).openStream();
|
return getResourceURL(source).openStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param source
|
* Sucht eine zur angegebenen Quelle passende Ressource und öffnet einen
|
||||||
* @return
|
* passenden {@link OutputStream}.
|
||||||
* @throws IOException
|
* <p>
|
||||||
|
* Es wird davon ausgegangen, dass die Quelle auf eine Datei verweist, auf
|
||||||
|
* die Schreibrechte bestehen. Ist dies nicht der Fall, wird eine
|
||||||
|
* {@link IOException} geworfen.
|
||||||
|
*
|
||||||
|
* @param source Eine Quelle für die Ressource (Absoluter Dateipfad,
|
||||||
|
* Dateipfad im Classpath).
|
||||||
|
* @return {@code OutputStream} für die Ressource.
|
||||||
|
* @throws IOException Falls die Quelle kein gültiges Ziel beschreibt.
|
||||||
*/
|
*/
|
||||||
public static OutputStream getOutputStream( String source ) throws IOException {
|
public static OutputStream getOutputStream( String source ) throws IOException {
|
||||||
try {
|
try {
|
||||||
@@ -119,22 +127,83 @@ public class ResourceStreamProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sucht eine zur angegebenen Quelle passende Ressource und öffnet einen
|
||||||
|
* {@link BufferedReader}.
|
||||||
|
* <p>
|
||||||
|
* Die Suche erfolgt, wie bei {@link #getInputStream(String)} beschrieben.
|
||||||
|
* <p>
|
||||||
|
* Als {@link Charset} wird {@link StandardCharsets#UTF_8 UTF-8} verwendet.
|
||||||
|
*
|
||||||
|
* @param source Eine Quelle für die Ressource (Absoluter Dateipfad,
|
||||||
|
* Dateipfad im Classpath oder Netzwerkresource).
|
||||||
|
* @return Ein {@code BufferedReader} für die Quelle.
|
||||||
|
* @throws IOException Falls die Quelle nicht exisitert oder nicht geöffnet
|
||||||
|
* werden kann.
|
||||||
|
* @see #getInputStream(String)
|
||||||
|
*/
|
||||||
public static BufferedReader getReader( String source ) throws IOException {
|
public static BufferedReader getReader( String source ) throws IOException {
|
||||||
return getReader(source, StandardCharsets.UTF_8);
|
return getReader(source, StandardCharsets.UTF_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sucht eine zur angegebenen Quelle passende Ressource und öffnet einen
|
||||||
|
* {@link BufferedReader} mit dem angegebenen Charset.
|
||||||
|
* <p>
|
||||||
|
* Die Suche erfolgt wie bei {@link #getInputStream(String)} beschrieben.
|
||||||
|
*
|
||||||
|
* @param source Eine Quelle für die Ressource (Absoluter Dateipfad,
|
||||||
|
* Dateipfad im Classpath oder Netzwerkresource).
|
||||||
|
* @param charset Der zu verwendende Charset.
|
||||||
|
* @return Ein {@code BufferedReader} für die Quelle.
|
||||||
|
* @throws IOException Falls die Quelle nicht exisitert oder nicht geöffnet
|
||||||
|
* werden kann.
|
||||||
|
* @see #getInputStream(String)
|
||||||
|
*/
|
||||||
public static BufferedReader getReader( String source, Charset charset ) throws IOException {
|
public static BufferedReader getReader( String source, Charset charset ) throws IOException {
|
||||||
return new BufferedReader(new InputStreamReader(getInputStream(source), charset.newDecoder()));
|
return new BufferedReader(new InputStreamReader(getInputStream(source), charset.newDecoder()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sucht eine zur angegebenen Quelle passende Datei und öffnet einen
|
||||||
|
* {@link BufferedWriter}.
|
||||||
|
* <p>
|
||||||
|
* Die Suche erfolgt wie bei {@link #getOutputStream(String)} beschrieben.
|
||||||
|
* <p>
|
||||||
|
* Als {@link Charset} wird {@link StandardCharsets#UTF_8 UTF-8} verwendet.
|
||||||
|
*
|
||||||
|
* @param source Eine Quelle für die Ressource (Absoluter Dateipfad,
|
||||||
|
* Dateipfad im Classpath oder Netzwerkresource).
|
||||||
|
* @return Ein {@code BufferedWriter} für die Quelle.
|
||||||
|
* @throws IOException Falls die Quelle nicht exisitert oder nicht geöffnet
|
||||||
|
* werden kann.
|
||||||
|
* @see #getOutputStream(String)
|
||||||
|
*/
|
||||||
public static BufferedWriter getWriter( String source ) throws IOException {
|
public static BufferedWriter getWriter( String source ) throws IOException {
|
||||||
return getWriter(source, StandardCharsets.UTF_8);
|
return getWriter(source, StandardCharsets.UTF_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sucht eine zur angegebenen Quelle passende Datei und öffnet einen
|
||||||
|
* {@link BufferedWriter}.
|
||||||
|
* <p>
|
||||||
|
* Die Suche erfolgt wie bei {@link #getOutputStream(String)} beschrieben.
|
||||||
|
* <p>
|
||||||
|
* Als {@link Charset} wird {@link StandardCharsets#UTF_8 UTF-8} verwendet.
|
||||||
|
*
|
||||||
|
* @param source Eine Quelle für die Ressource (Absoluter Dateipfad,
|
||||||
|
* Dateipfad im Classpath oder Netzwerkresource).
|
||||||
|
* @return Ein {@code BufferedWriter} für die Quelle.
|
||||||
|
* @param charset Der zu verwendende Charset.
|
||||||
|
* @throws IOException Falls die Quelle nicht exisitert oder nicht geöffnet
|
||||||
|
* werden kann.
|
||||||
|
* @see #getOutputStream(String)
|
||||||
|
*/
|
||||||
public static BufferedWriter getWriter( String source, Charset charset ) throws IOException {
|
public static BufferedWriter getWriter( String source, Charset charset ) throws IOException {
|
||||||
return new BufferedWriter(new OutputStreamWriter(getOutputStream(source), charset.newEncoder()));
|
return new BufferedWriter(new OutputStreamWriter(getOutputStream(source), charset.newEncoder()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Privater C'tor
|
||||||
private ResourceStreamProvider() {
|
private ResourceStreamProvider() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
1
src/main/resources/schule/ngb/zm/util/mock-text.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Weit hinten, hinter den Wortbergen, fern der Länder Vokalien und Konsonantien leben die Blindtexte. Abgeschieden wohnen Sie in Buchstabhausen an der Küste des Semantik, eines großen Sprachozeans. Ein kleines Bächlein namens Duden fließt durch ihren Ort und versorgt sie mit den nötigen Regelialien. Es ist ein paradiesmatisches Land, in dem einem gebratene Satzteile in den Mund fliegen. Nicht einmal von der allmächtigen Interpunktion werden die Blindtexte beherrscht - ein geradezu unorthographisches Leben. Eines Tages aber beschloß eine kleine Zeile Blindtext, ihr Name war Lorem Ipsum, hinaus zu gehen in die weite Grammatik. Der große Oxmox riet ihr davon ab, da es dort wimmele von bösen Kommata, wilden Fragezeichen und hinterhältigen Semikoli, doch das Blindtextchen ließ sich nicht beirren. Es packte seine sieben Versalien, schob sich sein Initial in den Gürtel und machte sich auf den Weg. Als es die ersten Hügel des Kursivgebirges erklommen hatte, warf es einen letzten Blick zurück auf die Skyline seiner Heimatstadt Buchstabhausen, die Headline von Alphabetdorf und die Subline seiner eigenen Straße, der Zeilengasse. Wehmütig lief ihm eine rethorische Frage über die Wange, dann setzte es seinen Weg fort. Unterwegs traf es eine Copy. Die Copy warnte das Blindtextchen, da, wo sie herkäme wäre sie zigmal umgeschrieben worden und alles, was von ihrem Ursprung noch übrig wäre, sei das Wort "und" und das Blindtextchen solle umkehren und wieder in sein eigenes, sicheres Land zurückkehren. Doch alles Gutzureden konnte es nicht überzeugen und so dauerte es nicht lange, bis ihm ein paar heimtückische Werbetexter auflauerten, es mit Longe und Parole betrunken machten und es dann in ihre Agentur schleppten, wo sie es für ihre Projekte wieder und wieder mißbrauchten. Und wenn es nicht umgeschrieben wurde, dann benutzen Sie es immernoch.
|
||||||
949
src/main/resources/schule/ngb/zm/util/mock-words.csv
Normal file
@@ -0,0 +1,949 @@
|
|||||||
|
Abbau
|
||||||
|
Abend
|
||||||
|
Abzug
|
||||||
|
Adler
|
||||||
|
Adolf
|
||||||
|
Agent
|
||||||
|
Akten
|
||||||
|
Aktie
|
||||||
|
Alarm
|
||||||
|
Album
|
||||||
|
Allee
|
||||||
|
Alpen
|
||||||
|
Ampel
|
||||||
|
Amtes
|
||||||
|
Anbau
|
||||||
|
Angst
|
||||||
|
Anruf
|
||||||
|
Anton
|
||||||
|
Anzug
|
||||||
|
Apple
|
||||||
|
April
|
||||||
|
Areal
|
||||||
|
Arena
|
||||||
|
Armee
|
||||||
|
Armut
|
||||||
|
Arten
|
||||||
|
Asien
|
||||||
|
Athen
|
||||||
|
Augen
|
||||||
|
Autor
|
||||||
|
Autos
|
||||||
|
Baden
|
||||||
|
Bande
|
||||||
|
Bands
|
||||||
|
Basel
|
||||||
|
Basis
|
||||||
|
Bauch
|
||||||
|
Bauer
|
||||||
|
Bayer
|
||||||
|
Beate
|
||||||
|
Beide
|
||||||
|
Beine
|
||||||
|
Beleg
|
||||||
|
Bernd
|
||||||
|
Beruf
|
||||||
|
Beste
|
||||||
|
Beton
|
||||||
|
Beute
|
||||||
|
Bevor
|
||||||
|
Bezug
|
||||||
|
Bibel
|
||||||
|
Bitte
|
||||||
|
Blatt
|
||||||
|
Blick
|
||||||
|
Block
|
||||||
|
Blues
|
||||||
|
Bogen
|
||||||
|
Bombe
|
||||||
|
Boris
|
||||||
|
Brand
|
||||||
|
Braun
|
||||||
|
Brief
|
||||||
|
Bruch
|
||||||
|
Bruno
|
||||||
|
Brust
|
||||||
|
Buben
|
||||||
|
Buche
|
||||||
|
Busse
|
||||||
|
Bäume
|
||||||
|
Böden
|
||||||
|
Börse
|
||||||
|
Bühne
|
||||||
|
Büros
|
||||||
|
Chaos
|
||||||
|
Chefs
|
||||||
|
Chile
|
||||||
|
China
|
||||||
|
Clubs
|
||||||
|
Coach
|
||||||
|
Daher
|
||||||
|
Damen
|
||||||
|
Daran
|
||||||
|
Darum
|
||||||
|
Datei
|
||||||
|
Daten
|
||||||
|
Datum
|
||||||
|
Dauer
|
||||||
|
David
|
||||||
|
Davon
|
||||||
|
Decke
|
||||||
|
Deren
|
||||||
|
Diana
|
||||||
|
Dinge
|
||||||
|
Donau
|
||||||
|
Doris
|
||||||
|
Drama
|
||||||
|
Dreck
|
||||||
|
Druck
|
||||||
|
Duell
|
||||||
|
Durch
|
||||||
|
Ecken
|
||||||
|
Edith
|
||||||
|
Eiern
|
||||||
|
Eisen
|
||||||
|
Elite
|
||||||
|
Engel
|
||||||
|
Enkel
|
||||||
|
Erbin
|
||||||
|
Erich
|
||||||
|
Erika
|
||||||
|
Erlös
|
||||||
|
Ernst
|
||||||
|
Erste
|
||||||
|
Erwin
|
||||||
|
Essen
|
||||||
|
Ethik
|
||||||
|
Fahne
|
||||||
|
Fahrt
|
||||||
|
Falls
|
||||||
|
Farbe
|
||||||
|
Faust
|
||||||
|
Fazit
|
||||||
|
Feier
|
||||||
|
Feind
|
||||||
|
Felix
|
||||||
|
Ferne
|
||||||
|
Feuer
|
||||||
|
Figur
|
||||||
|
Filme
|
||||||
|
Films
|
||||||
|
Firma
|
||||||
|
Fisch
|
||||||
|
Flüge
|
||||||
|
Focus
|
||||||
|
Folge
|
||||||
|
Fonds
|
||||||
|
Forum
|
||||||
|
Fotos
|
||||||
|
Foyer
|
||||||
|
Franc
|
||||||
|
Frank
|
||||||
|
Franz
|
||||||
|
Frist
|
||||||
|
Fritz
|
||||||
|
Front
|
||||||
|
Frust
|
||||||
|
Fuchs
|
||||||
|
Fulda
|
||||||
|
Fälle
|
||||||
|
Fülle
|
||||||
|
Füßen
|
||||||
|
Geben
|
||||||
|
Gebot
|
||||||
|
Gehen
|
||||||
|
Gehör
|
||||||
|
Geist
|
||||||
|
Georg
|
||||||
|
Gerda
|
||||||
|
Gerät
|
||||||
|
Geste
|
||||||
|
Glanz
|
||||||
|
Glück
|
||||||
|
Gnade
|
||||||
|
Gramm
|
||||||
|
Griff
|
||||||
|
Group
|
||||||
|
Grund
|
||||||
|
Grüne
|
||||||
|
Gunst
|
||||||
|
Gutes
|
||||||
|
Gäste
|
||||||
|
Güter
|
||||||
|
Haare
|
||||||
|
Haars
|
||||||
|
Hafen
|
||||||
|
Halle
|
||||||
|
Hanau
|
||||||
|
Handy
|
||||||
|
Harry
|
||||||
|
Hauch
|
||||||
|
Hause
|
||||||
|
Hebel
|
||||||
|
Heike
|
||||||
|
Heims
|
||||||
|
Heinz
|
||||||
|
Helga
|
||||||
|
Henry
|
||||||
|
Herrn
|
||||||
|
Hilfe
|
||||||
|
Hitze
|
||||||
|
Horst
|
||||||
|
Hosen
|
||||||
|
Hotel
|
||||||
|
Huber
|
||||||
|
Humor
|
||||||
|
Hunde
|
||||||
|
Hände
|
||||||
|
Härte
|
||||||
|
Hätte
|
||||||
|
Höhen
|
||||||
|
Hölle
|
||||||
|
Hörer
|
||||||
|
Hügel
|
||||||
|
Hürde
|
||||||
|
Hütte
|
||||||
|
Ideen
|
||||||
|
Image
|
||||||
|
Index
|
||||||
|
Indiz
|
||||||
|
Infos
|
||||||
|
Insel
|
||||||
|
Intel
|
||||||
|
Irene
|
||||||
|
Islam
|
||||||
|
Jahre
|
||||||
|
Jakob
|
||||||
|
James
|
||||||
|
Japan
|
||||||
|
Jeans
|
||||||
|
Jesus
|
||||||
|
Josef
|
||||||
|
Jubel
|
||||||
|
Juden
|
||||||
|
Julia
|
||||||
|
Jungs
|
||||||
|
Jutta
|
||||||
|
Jäger
|
||||||
|
Kabel
|
||||||
|
Kabul
|
||||||
|
Kader
|
||||||
|
Kairo
|
||||||
|
Kampf
|
||||||
|
Kanal
|
||||||
|
Karin
|
||||||
|
Karte
|
||||||
|
Kasse
|
||||||
|
Kenia
|
||||||
|
Kette
|
||||||
|
Kinos
|
||||||
|
Kirch
|
||||||
|
Klage
|
||||||
|
Klaus
|
||||||
|
Klima
|
||||||
|
Klubs
|
||||||
|
Kohle
|
||||||
|
Kohls
|
||||||
|
Konto
|
||||||
|
Kopie
|
||||||
|
Korea
|
||||||
|
Kraft
|
||||||
|
Krebs
|
||||||
|
Kreis
|
||||||
|
Kreml
|
||||||
|
Kreuz
|
||||||
|
Krieg
|
||||||
|
Kripo
|
||||||
|
Krise
|
||||||
|
Krone
|
||||||
|
Krupp
|
||||||
|
Kugel
|
||||||
|
Kunde
|
||||||
|
Kunst
|
||||||
|
Kurse
|
||||||
|
Kurve
|
||||||
|
König
|
||||||
|
Köpfe
|
||||||
|
Küche
|
||||||
|
Kürze
|
||||||
|
Küste
|
||||||
|
Labor
|
||||||
|
Lager
|
||||||
|
Laien
|
||||||
|
Lande
|
||||||
|
Lange
|
||||||
|
Laufe
|
||||||
|
Laune
|
||||||
|
Leder
|
||||||
|
Legen
|
||||||
|
Lehre
|
||||||
|
Leser
|
||||||
|
Leute
|
||||||
|
Licht
|
||||||
|
Linie
|
||||||
|
Linke
|
||||||
|
Liste
|
||||||
|
Liter
|
||||||
|
Logik
|
||||||
|
Lokal
|
||||||
|
Luxus
|
||||||
|
Lyrik
|
||||||
|
Löhne
|
||||||
|
Löwen
|
||||||
|
Lücke
|
||||||
|
Macht
|
||||||
|
Mafia
|
||||||
|
Magen
|
||||||
|
Mainz
|
||||||
|
Major
|
||||||
|
Maler
|
||||||
|
Maria
|
||||||
|
Marie
|
||||||
|
Mario
|
||||||
|
Marke
|
||||||
|
Markt
|
||||||
|
Maske
|
||||||
|
Masse
|
||||||
|
Match
|
||||||
|
Mauer
|
||||||
|
Media
|
||||||
|
Meist
|
||||||
|
Menge
|
||||||
|
Meter
|
||||||
|
Metro
|
||||||
|
Meyer
|
||||||
|
Miami
|
||||||
|
Milch
|
||||||
|
Minen
|
||||||
|
Mitte
|
||||||
|
Monat
|
||||||
|
Moral
|
||||||
|
Motiv
|
||||||
|
Motor
|
||||||
|
Motto
|
||||||
|
Musik
|
||||||
|
Möbel
|
||||||
|
Nacht
|
||||||
|
Nagel
|
||||||
|
Namen
|
||||||
|
Natur
|
||||||
|
Nazis
|
||||||
|
Nebel
|
||||||
|
Netze
|
||||||
|
Noten
|
||||||
|
Ohren
|
||||||
|
Onkel
|
||||||
|
Opfer
|
||||||
|
Orden
|
||||||
|
Organ
|
||||||
|
Orten
|
||||||
|
Oskar
|
||||||
|
Osten
|
||||||
|
Paare
|
||||||
|
Paket
|
||||||
|
Panik
|
||||||
|
Papst
|
||||||
|
Paris
|
||||||
|
Parks
|
||||||
|
Party
|
||||||
|
Pause
|
||||||
|
Pence
|
||||||
|
Peter
|
||||||
|
Petra
|
||||||
|
Pferd
|
||||||
|
Pfund
|
||||||
|
Phase
|
||||||
|
Pilot
|
||||||
|
Platz
|
||||||
|
Polen
|
||||||
|
Preis
|
||||||
|
Prinz
|
||||||
|
Probe
|
||||||
|
Profi
|
||||||
|
Prosa
|
||||||
|
Punkt
|
||||||
|
Quote
|
||||||
|
Rache
|
||||||
|
Radio
|
||||||
|
Rande
|
||||||
|
Rasen
|
||||||
|
Ratte
|
||||||
|
Rauch
|
||||||
|
Regel
|
||||||
|
Regen
|
||||||
|
Regie
|
||||||
|
Reihe
|
||||||
|
Rente
|
||||||
|
Reste
|
||||||
|
Rhein
|
||||||
|
Rolle
|
||||||
|
Roman
|
||||||
|
Route
|
||||||
|
Rover
|
||||||
|
Rubel
|
||||||
|
Russe
|
||||||
|
Räder
|
||||||
|
Römer
|
||||||
|
Sache
|
||||||
|
Sauna
|
||||||
|
Schau
|
||||||
|
Schoß
|
||||||
|
Seele
|
||||||
|
Segen
|
||||||
|
Seite
|
||||||
|
Sekte
|
||||||
|
Senat
|
||||||
|
Seoul
|
||||||
|
Serie
|
||||||
|
Shell
|
||||||
|
Sicht
|
||||||
|
Simon
|
||||||
|
Sinne
|
||||||
|
Smith
|
||||||
|
Songs
|
||||||
|
Sonne
|
||||||
|
Sound
|
||||||
|
Spiel
|
||||||
|
Sport
|
||||||
|
Spree
|
||||||
|
Staat
|
||||||
|
Stadt
|
||||||
|
Stamm
|
||||||
|
Stars
|
||||||
|
Start
|
||||||
|
Stasi
|
||||||
|
Staub
|
||||||
|
Staus
|
||||||
|
Stein
|
||||||
|
Stern
|
||||||
|
Stirn
|
||||||
|
Stock
|
||||||
|
Stoff
|
||||||
|
Story
|
||||||
|
Strom
|
||||||
|
Stufe
|
||||||
|
Stuhl
|
||||||
|
Sturm
|
||||||
|
Sturz
|
||||||
|
Stück
|
||||||
|
Summe
|
||||||
|
Szene
|
||||||
|
Sätze
|
||||||
|
Söhne
|
||||||
|
Süden
|
||||||
|
Tagen
|
||||||
|
Tages
|
||||||
|
Tante
|
||||||
|
Tarif
|
||||||
|
Teams
|
||||||
|
Tempo
|
||||||
|
Tenor
|
||||||
|
Tests
|
||||||
|
Texas
|
||||||
|
Texte
|
||||||
|
Thema
|
||||||
|
These
|
||||||
|
Thron
|
||||||
|
Tiefs
|
||||||
|
Tiere
|
||||||
|
Tiger
|
||||||
|
Tisch
|
||||||
|
Titel
|
||||||
|
Todes
|
||||||
|
Tokio
|
||||||
|
Tonne
|
||||||
|
Toren
|
||||||
|
Traum
|
||||||
|
Treff
|
||||||
|
Trend
|
||||||
|
Trick
|
||||||
|
Trios
|
||||||
|
Trost
|
||||||
|
Typen
|
||||||
|
Täter
|
||||||
|
Tönen
|
||||||
|
Türen
|
||||||
|
Türke
|
||||||
|
UdSSR
|
||||||
|
Uhren
|
||||||
|
Ulmer
|
||||||
|
Umbau
|
||||||
|
Umzug
|
||||||
|
Union
|
||||||
|
Unmut
|
||||||
|
Vater
|
||||||
|
Vetos
|
||||||
|
Video
|
||||||
|
Villa
|
||||||
|
Viren
|
||||||
|
Virus
|
||||||
|
Vogts
|
||||||
|
Votum
|
||||||
|
Vögel
|
||||||
|
Waffe
|
||||||
|
Weber
|
||||||
|
Weile
|
||||||
|
Welle
|
||||||
|
Werke
|
||||||
|
Werks
|
||||||
|
Wesen
|
||||||
|
Wiese
|
||||||
|
Wille
|
||||||
|
Willi
|
||||||
|
Wirte
|
||||||
|
Wirts
|
||||||
|
Witwe
|
||||||
|
Witze
|
||||||
|
Woche
|
||||||
|
Wolke
|
||||||
|
World
|
||||||
|
Worte
|
||||||
|
Wände
|
||||||
|
Wüste
|
||||||
|
Zaire
|
||||||
|
Zeile
|
||||||
|
Zelle
|
||||||
|
Zeugs
|
||||||
|
Ziels
|
||||||
|
Zitat
|
||||||
|
Zorns
|
||||||
|
Zunge
|
||||||
|
Zweck
|
||||||
|
Zähne
|
||||||
|
Zügen
|
||||||
|
ahnen
|
||||||
|
ahnst
|
||||||
|
aktiv
|
||||||
|
allem
|
||||||
|
allen
|
||||||
|
aller
|
||||||
|
alles
|
||||||
|
allzu
|
||||||
|
altem
|
||||||
|
alten
|
||||||
|
altes
|
||||||
|
angab
|
||||||
|
armen
|
||||||
|
außen
|
||||||
|
außer
|
||||||
|
bauen
|
||||||
|
baute
|
||||||
|
bekam
|
||||||
|
berge
|
||||||
|
berät
|
||||||
|
besaß
|
||||||
|
bezog
|
||||||
|
biete
|
||||||
|
birgt
|
||||||
|
blaue
|
||||||
|
blieb
|
||||||
|
blind
|
||||||
|
bloße
|
||||||
|
boten
|
||||||
|
brach
|
||||||
|
breit
|
||||||
|
bunte
|
||||||
|
bösen
|
||||||
|
dabei
|
||||||
|
dafür
|
||||||
|
dahin
|
||||||
|
damit
|
||||||
|
darin
|
||||||
|
davor
|
||||||
|
deine
|
||||||
|
denen
|
||||||
|
denke
|
||||||
|
denkt
|
||||||
|
derer
|
||||||
|
desto
|
||||||
|
dicht
|
||||||
|
dicke
|
||||||
|
dient
|
||||||
|
diese
|
||||||
|
drauf
|
||||||
|
dreht
|
||||||
|
drohe
|
||||||
|
droht
|
||||||
|
dürfe
|
||||||
|
ebene
|
||||||
|
echte
|
||||||
|
ehren
|
||||||
|
eigen
|
||||||
|
einem
|
||||||
|
einen
|
||||||
|
einer
|
||||||
|
eines
|
||||||
|
einig
|
||||||
|
einst
|
||||||
|
eisig
|
||||||
|
elend
|
||||||
|
enden
|
||||||
|
endet
|
||||||
|
engem
|
||||||
|
engen
|
||||||
|
enger
|
||||||
|
enges
|
||||||
|
enorm
|
||||||
|
erben
|
||||||
|
ergab
|
||||||
|
erhob
|
||||||
|
ernte
|
||||||
|
etwas
|
||||||
|
ewige
|
||||||
|
exakt
|
||||||
|
extra
|
||||||
|
fehle
|
||||||
|
fehlt
|
||||||
|
feine
|
||||||
|
feste
|
||||||
|
finde
|
||||||
|
folgt
|
||||||
|
frage
|
||||||
|
fragt
|
||||||
|
freie
|
||||||
|
fremd
|
||||||
|
freut
|
||||||
|
frühe
|
||||||
|
fähig
|
||||||
|
fällt
|
||||||
|
fängt
|
||||||
|
fügte
|
||||||
|
fühle
|
||||||
|
fühlt
|
||||||
|
führe
|
||||||
|
führt
|
||||||
|
füllt
|
||||||
|
gaben
|
||||||
|
ganze
|
||||||
|
gegen
|
||||||
|
gelbe
|
||||||
|
gelte
|
||||||
|
gemäß
|
||||||
|
genau
|
||||||
|
genug
|
||||||
|
gerne
|
||||||
|
getan
|
||||||
|
geübt
|
||||||
|
ginge
|
||||||
|
gingt
|
||||||
|
glatt
|
||||||
|
graue
|
||||||
|
große
|
||||||
|
gutem
|
||||||
|
guten
|
||||||
|
haben
|
||||||
|
halbe
|
||||||
|
halte
|
||||||
|
heben
|
||||||
|
hebst
|
||||||
|
hefte
|
||||||
|
heiße
|
||||||
|
heißt
|
||||||
|
helle
|
||||||
|
heran
|
||||||
|
herum
|
||||||
|
heuer
|
||||||
|
heute
|
||||||
|
hielt
|
||||||
|
hilft
|
||||||
|
hinzu
|
||||||
|
hoffe
|
||||||
|
hofft
|
||||||
|
hohem
|
||||||
|
hohes
|
||||||
|
holen
|
||||||
|
holte
|
||||||
|
hängt
|
||||||
|
höher
|
||||||
|
hören
|
||||||
|
hörte
|
||||||
|
ideal
|
||||||
|
ihnen
|
||||||
|
ihrem
|
||||||
|
ihren
|
||||||
|
ihrer
|
||||||
|
ihres
|
||||||
|
immer
|
||||||
|
indem
|
||||||
|
indes
|
||||||
|
innen
|
||||||
|
jedem
|
||||||
|
jeden
|
||||||
|
jeder
|
||||||
|
jedes
|
||||||
|
jeher
|
||||||
|
jenem
|
||||||
|
jenen
|
||||||
|
jener
|
||||||
|
jenes
|
||||||
|
jetzt
|
||||||
|
junge
|
||||||
|
kalte
|
||||||
|
kauft
|
||||||
|
kehrt
|
||||||
|
keine
|
||||||
|
kenne
|
||||||
|
kennt
|
||||||
|
klagt
|
||||||
|
klang
|
||||||
|
klare
|
||||||
|
klein
|
||||||
|
knapp
|
||||||
|
komme
|
||||||
|
kommt
|
||||||
|
krank
|
||||||
|
kämen
|
||||||
|
könne
|
||||||
|
lacht
|
||||||
|
laden
|
||||||
|
lagen
|
||||||
|
lasse
|
||||||
|
leben
|
||||||
|
lebte
|
||||||
|
leere
|
||||||
|
legte
|
||||||
|
lehnt
|
||||||
|
lehrt
|
||||||
|
leise
|
||||||
|
lernt
|
||||||
|
lesen
|
||||||
|
liebe
|
||||||
|
liebt
|
||||||
|
liege
|
||||||
|
liegt
|
||||||
|
liest
|
||||||
|
ließe
|
||||||
|
links
|
||||||
|
lobte
|
||||||
|
lockt
|
||||||
|
lohnt
|
||||||
|
lässt
|
||||||
|
läuft
|
||||||
|
lösen
|
||||||
|
löste
|
||||||
|
mache
|
||||||
|
manch
|
||||||
|
meine
|
||||||
|
meint
|
||||||
|
merkt
|
||||||
|
messe
|
||||||
|
miete
|
||||||
|
minus
|
||||||
|
morde
|
||||||
|
mögen
|
||||||
|
müsse
|
||||||
|
nahen
|
||||||
|
neben
|
||||||
|
nebst
|
||||||
|
nehme
|
||||||
|
nennt
|
||||||
|
nette
|
||||||
|
neuem
|
||||||
|
neuen
|
||||||
|
neuer
|
||||||
|
neues
|
||||||
|
nicht
|
||||||
|
nimmt
|
||||||
|
näher
|
||||||
|
nötig
|
||||||
|
nützt
|
||||||
|
obere
|
||||||
|
offen
|
||||||
|
ordne
|
||||||
|
packe
|
||||||
|
packt
|
||||||
|
parat
|
||||||
|
passe
|
||||||
|
passt
|
||||||
|
plane
|
||||||
|
plant
|
||||||
|
prüfe
|
||||||
|
prüft
|
||||||
|
quasi
|
||||||
|
rasch
|
||||||
|
raten
|
||||||
|
reale
|
||||||
|
recht
|
||||||
|
reden
|
||||||
|
redet
|
||||||
|
reich
|
||||||
|
reine
|
||||||
|
reise
|
||||||
|
reist
|
||||||
|
rollt
|
||||||
|
rotem
|
||||||
|
roten
|
||||||
|
roter
|
||||||
|
rotes
|
||||||
|
rufen
|
||||||
|
ruhen
|
||||||
|
ruhig
|
||||||
|
runde
|
||||||
|
räume
|
||||||
|
räumt
|
||||||
|
rückt
|
||||||
|
sagen
|
||||||
|
sagte
|
||||||
|
sahen
|
||||||
|
sanft
|
||||||
|
satte
|
||||||
|
sauer
|
||||||
|
saßen
|
||||||
|
schuf
|
||||||
|
schön
|
||||||
|
sechs
|
||||||
|
sehen
|
||||||
|
seien
|
||||||
|
seine
|
||||||
|
sende
|
||||||
|
setze
|
||||||
|
setzt
|
||||||
|
siege
|
||||||
|
siehe
|
||||||
|
sieht
|
||||||
|
singt
|
||||||
|
sinkt
|
||||||
|
sitze
|
||||||
|
sitzt
|
||||||
|
sogar
|
||||||
|
solch
|
||||||
|
solle
|
||||||
|
sollt
|
||||||
|
somit
|
||||||
|
sonst
|
||||||
|
sorge
|
||||||
|
sorgt
|
||||||
|
sowie
|
||||||
|
spare
|
||||||
|
spart
|
||||||
|
spitz
|
||||||
|
späte
|
||||||
|
spürt
|
||||||
|
stahl
|
||||||
|
stand
|
||||||
|
starb
|
||||||
|
stark
|
||||||
|
statt
|
||||||
|
stehe
|
||||||
|
steht
|
||||||
|
stets
|
||||||
|
stich
|
||||||
|
stieg
|
||||||
|
stieß
|
||||||
|
still
|
||||||
|
stolz
|
||||||
|
stoße
|
||||||
|
stört
|
||||||
|
stößt
|
||||||
|
suche
|
||||||
|
sucht
|
||||||
|
tanze
|
||||||
|
tanzt
|
||||||
|
taten
|
||||||
|
teile
|
||||||
|
teils
|
||||||
|
teilt
|
||||||
|
teuer
|
||||||
|
teure
|
||||||
|
tiefe
|
||||||
|
tolle
|
||||||
|
total
|
||||||
|
totem
|
||||||
|
toter
|
||||||
|
totes
|
||||||
|
traue
|
||||||
|
traut
|
||||||
|
treue
|
||||||
|
trieb
|
||||||
|
trink
|
||||||
|
tritt
|
||||||
|
trotz
|
||||||
|
träge
|
||||||
|
trägt
|
||||||
|
tätig
|
||||||
|
töten
|
||||||
|
tötet
|
||||||
|
unser
|
||||||
|
unten
|
||||||
|
unter
|
||||||
|
vagem
|
||||||
|
vagen
|
||||||
|
vager
|
||||||
|
vages
|
||||||
|
viele
|
||||||
|
volle
|
||||||
|
vorab
|
||||||
|
voran
|
||||||
|
vorne
|
||||||
|
wagen
|
||||||
|
wahre
|
||||||
|
waren
|
||||||
|
warnt
|
||||||
|
warst
|
||||||
|
warte
|
||||||
|
warum
|
||||||
|
wecke
|
||||||
|
weckt
|
||||||
|
weder
|
||||||
|
wegen
|
||||||
|
wehre
|
||||||
|
wehrt
|
||||||
|
weich
|
||||||
|
weist
|
||||||
|
weite
|
||||||
|
weiße
|
||||||
|
welch
|
||||||
|
wende
|
||||||
|
wenig
|
||||||
|
werbe
|
||||||
|
werde
|
||||||
|
werte
|
||||||
|
wider
|
||||||
|
widme
|
||||||
|
wiegt
|
||||||
|
wilde
|
||||||
|
wirbt
|
||||||
|
wirft
|
||||||
|
wirke
|
||||||
|
wirkt
|
||||||
|
wisse
|
||||||
|
wobei
|
||||||
|
wofür
|
||||||
|
woher
|
||||||
|
wohin
|
||||||
|
wohne
|
||||||
|
wohnt
|
||||||
|
wolle
|
||||||
|
womit
|
||||||
|
wovon
|
||||||
|
wuchs
|
||||||
|
wunde
|
||||||
|
wurde
|
||||||
|
wähle
|
||||||
|
wählt
|
||||||
|
wärme
|
||||||
|
zahle
|
||||||
|
zeige
|
||||||
|
zeigt
|
||||||
|
zeuge
|
||||||
|
zieht
|
||||||
|
ziele
|
||||||
|
zielt
|
||||||
|
zivil
|
||||||
|
zogen
|
||||||
|
zudem
|
||||||
|
zumal
|
||||||
|
zuvor
|
||||||
|
zwang
|
||||||
|
zwölf
|
||||||
|
zählt
|
||||||
|
zügig
|
||||||
|
Ämter
|
||||||
|
Ärger
|
||||||
|
Ärzte
|
||||||
|
Übung
|
||||||
|
älter
|
||||||
|
öffne
|
||||||
|
öfter
|
||||||
|
übrig
|
||||||
|
übten
|
||||||
|
@@ -25,10 +25,28 @@ class ConstantsTest {
|
|||||||
assertEquals(5.0, Constants.max(new double[]{ 5.0, 5.0, 5.0, 5.0, 5.0 }), 0.000001);
|
assertEquals(5.0, Constants.max(new double[]{ 5.0, 5.0, 5.0, 5.0, 5.0 }), 0.000001);
|
||||||
assertEquals(5.0, Constants.max(new double[]{ 5.0 }), 0.000001);
|
assertEquals(5.0, Constants.max(new double[]{ 5.0 }), 0.000001);
|
||||||
|
|
||||||
assertThrows(IllegalArgumentException.class, () -> Constants.max(null));
|
double[] doubleArr = null;
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> Constants.max(doubleArr));
|
||||||
assertThrows(IllegalArgumentException.class, () -> Constants.max(new double[]{}));
|
assertThrows(IllegalArgumentException.class, () -> Constants.max(new double[]{}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void maxInt() {
|
||||||
|
assertEquals(5, Constants.max(5, 0));
|
||||||
|
assertEquals(5, Constants.max(5, 4));
|
||||||
|
assertEquals(5, Constants.max(5, 5));
|
||||||
|
assertEquals(100, Constants.max(100, -100));
|
||||||
|
assertEquals(0, Constants.max(0, 0));
|
||||||
|
|
||||||
|
assertEquals(5, Constants.max(new double[]{ 0, 1, 2, 3, 4, 5 }));
|
||||||
|
assertEquals(5, Constants.max(new double[]{ 5, 5, 5, 5, 5 }));
|
||||||
|
assertEquals(5, Constants.max(new double[]{ 5 }));
|
||||||
|
|
||||||
|
int[] intArr = null;
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> Constants.max(intArr));
|
||||||
|
assertThrows(IllegalArgumentException.class, () -> Constants.max(new int[]{}));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void abs() {
|
void abs() {
|
||||||
assertEquals(5.0, Constants.abs(5.0), 0.000001);
|
assertEquals(5.0, Constants.abs(5.0), 0.000001);
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import schule.ngb.zm.util.io.FileLoader;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.*;
|
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
class FileLoaderTest {
|
class FileLoaderTest {
|
||||||
|
|
||||||
|
|||||||