Compare commits
150 Commits
cf37e579ad
...
1.0.2
Author | SHA1 | Date | |
---|---|---|---|
e95248ec5e | |||
71b735785f | |||
57029b42ff
|
|||
d9aed568b7 | |||
f444fd2d6e
|
|||
a59cc07e83 | |||
8ff6011aeb | |||
5351986d65 | |||
1526c7f4bd | |||
798fde14be | |||
57eaf0d927 | |||
a05660d036 | |||
1e029e7940
|
|||
3cf401f3a1
|
|||
f026a0dcf7 | |||
74565e5744 | |||
01442b313f | |||
d38b195d92 | |||
6cf6d21e95 | |||
d59bd67d14 | |||
decc46cc2d | |||
6bb21235c5
|
|||
f2a3ffae9e | |||
5f4a7fe7e6 | |||
b4a1116513 | |||
e742f39f51 | |||
62700850b1 | |||
fb9e712778 | |||
b8ca5dc498
|
|||
dbdba120c3 | |||
33a46c4cbf
|
|||
d81aa56e49 | |||
95496536db | |||
8f3b61462a | |||
cff5ec76bf | |||
1bbfe800e1 | |||
c722a39116
|
|||
911983bff4
|
|||
6feb1c3dad | |||
7da58c7dbe | |||
86c05c0363
|
|||
a3c03c4bc7 | |||
9444e8198f | |||
730a0ab383 | |||
a10e2aceb6 | |||
f0fe4bb981 | |||
32883a2639 | |||
0e293768d7 | |||
6a5b2f555c | |||
8d210d756d | |||
8c6a43fc22
|
|||
7f4182bfad
|
|||
5fff4023f9
|
|||
dfad679e6e | |||
391dfe4282
|
|||
225316abb9 | |||
f5dbcc0134 | |||
cac4807357 | |||
33fa741a2a | |||
37406acd98 | |||
56c62422cc | |||
bc43b33536 | |||
1ac13f80d5 | |||
8dfbbae559 | |||
cc03b32ade | |||
a2f5b65e30 | |||
f094a68b04 | |||
b5545e1bae | |||
9a9a1e0056 | |||
0f23749001 | |||
c1dbd8ffb7 | |||
ebc6449e48 | |||
bef56f978c | |||
50ea54c8d6 | |||
24b9755089 | |||
6fbfa050b3 | |||
9a3524143a | |||
10076fcef2 | |||
6219ad93d0 | |||
8109782e4e | |||
c2d01760c7 | |||
2cac52b48d | |||
35f8145112 | |||
e26d54e96f | |||
7e3ce9ce56
|
|||
9975364621 | |||
491578806e | |||
b1052fa894
|
|||
36c4805061
|
|||
ee7fbae29c | |||
07f2682a2d
|
|||
efc28455c1 | |||
5d223ba72b
|
|||
15cbbb1bac | |||
08840c4c2b | |||
6b6ef67122
|
|||
4a213afdf5 | |||
36a8bccfe3 | |||
4f35293151 | |||
29354199cc | |||
f4db466b37 | |||
19eb6660e1 | |||
4e8b4c364b | |||
f22a9eb260
|
|||
9ae50df1a4
|
|||
ce11d2e263
|
|||
3f18760a74 | |||
89ef736954 | |||
3e1c46db81
|
|||
be11f35159 | |||
39c358c00f
|
|||
61980905e3 | |||
fc5db5d051 | |||
4c2e1c2917 | |||
a82cef599c
|
|||
86008828f5 | |||
c1ec000b98 | |||
ed558106e5 | |||
da153091bd
|
|||
9200e9277a
|
|||
40bfc73ba8 | |||
b1984ac8dd | |||
1c1f12ecaf
|
|||
d5b7fd0f94
|
|||
2c9a23ec8a
|
|||
f45d807536 | |||
ed344cbd49
|
|||
8e7ba1cb46 | |||
b2a84db5a7 | |||
71f50b832e | |||
0f8d2ba256 | |||
b0a1844b16 | |||
594f083a8d
|
|||
90b21ca3d1 | |||
db8178f4c4 | |||
4cd4481182 | |||
3c2984a404 | |||
c55182d6cc | |||
e540e58161 | |||
727558d867 | |||
adc8725a36 | |||
99ef2a8edc | |||
de47e99852
|
|||
d7a67c497e
|
|||
8d387250e7 | |||
e32766e581 | |||
53dcebecde
|
|||
c80233155f
|
|||
857f35e77e | |||
e78e7e3a59 |
@ -15,21 +15,8 @@ buildscript {
|
||||
|
||||
plugins {
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.3.31'
|
||||
}
|
||||
|
||||
group 'com.hso'
|
||||
version '1.0-SNAPSHOT'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1"
|
||||
implementation "no.tornado:tornadofx:$tornadofx_version"
|
||||
implementation "com.jfoenix:jfoenix:8.0.8"
|
||||
implementation 'com.google.code.gson:gson:2.8.5'
|
||||
id "com.github.johnrengelman.shadow" version "5.0.0"
|
||||
id "application"
|
||||
}
|
||||
|
||||
compileKotlin {
|
||||
@ -38,3 +25,19 @@ compileKotlin {
|
||||
compileTestKotlin {
|
||||
kotlinOptions.jvmTarget = "1.8"
|
||||
}
|
||||
|
||||
group 'org.hso'
|
||||
version '1.0.2'
|
||||
archivesBaseName = 'TextureSync'
|
||||
mainClassName = 'org.hso.texturesyncclient.app.Main'
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
|
||||
implementation "no.tornado:tornadofx:$tornadofx_version"
|
||||
implementation "com.jfoenix:jfoenix:8.0.9"
|
||||
implementation 'com.google.code.gson:gson:2.8.5'
|
||||
}
|
||||
|
@ -1,2 +1,2 @@
|
||||
rootProject.name = 'texuresync_client'
|
||||
rootProject.name = 'texturesync_client'
|
||||
|
||||
|
@ -0,0 +1,39 @@
|
||||
package org.hso.texturesyncclient.alerts
|
||||
|
||||
import com.jfoenix.controls.JFXDialogLayout
|
||||
import com.jfoenix.controls.JFXButton
|
||||
import com.jfoenix.controls.JFXAlert
|
||||
import javafx.event.ActionEvent
|
||||
import javafx.scene.text.Text
|
||||
import tornadofx.FX
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new JFoenix Alert to show some information
|
||||
* @param heading Heading text of the alert
|
||||
* @param body Content text of the alert
|
||||
* @param btnStyle Style of the okay button
|
||||
*/
|
||||
class JFXInfoAlert(heading: String, body: String, private var btnStyle: String) {
|
||||
|
||||
private var headingText = Text(heading)
|
||||
private var bodyText = Text(body)
|
||||
|
||||
fun showAndWait() {
|
||||
val alert = JFXAlert<Void>(FX.primaryStage)
|
||||
|
||||
val button = JFXButton("Okay")
|
||||
button.addEventHandler(ActionEvent.ACTION) { alert.close() }
|
||||
button.buttonType = JFXButton.ButtonType.RAISED
|
||||
button.prefHeight = 32.0
|
||||
button.style = btnStyle
|
||||
|
||||
val content = JFXDialogLayout()
|
||||
content.setActions(button)
|
||||
content.setHeading(headingText)
|
||||
content.setBody(bodyText)
|
||||
alert.setContent(content)
|
||||
alert.showAndWait()
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
package org.hso.texturesyncclient.alerts
|
||||
|
||||
import com.jfoenix.controls.JFXAlert
|
||||
import com.jfoenix.controls.JFXButton
|
||||
import com.jfoenix.controls.JFXDialogLayout
|
||||
|
||||
import javafx.event.ActionEvent
|
||||
import javafx.event.EventHandler
|
||||
import javafx.scene.text.Text
|
||||
import tornadofx.FX
|
||||
|
||||
/**
|
||||
* @param heading Heading text of the alert
|
||||
* @param body Content text of the alert
|
||||
* @param btnStyle Style of the buttons
|
||||
*/
|
||||
class JFXOkayCancelAlert(heading: String, body: String, private var btnStyle: String) {
|
||||
|
||||
var okayAction: EventHandler<ActionEvent>? = null
|
||||
var cancelAction: EventHandler<ActionEvent>? = null
|
||||
|
||||
private var okayText = "Löschen"
|
||||
private var cancelText = "Abbrechen"
|
||||
private var headingText = Text(heading)
|
||||
private var bodyText = Text(body)
|
||||
|
||||
init {
|
||||
//headingText.style = "-fx-font: 14px Verdana; -fx-text-fill: #2b7bbb;"
|
||||
//bodyText.style = "-fx-font: 14px Verdana; -fx-text-fill: #2b7bbb;"
|
||||
}
|
||||
|
||||
fun showAndWait() {
|
||||
val alert = JFXAlert<Void>(FX.primaryStage)
|
||||
|
||||
val okayBtn = JFXButton(okayText)
|
||||
okayBtn.addEventHandler(ActionEvent.ACTION) { alert.close() }
|
||||
okayBtn.addEventHandler(ActionEvent.ACTION, okayAction!!)
|
||||
okayBtn.buttonType = JFXButton.ButtonType.RAISED
|
||||
okayBtn.prefHeight = 32.0
|
||||
okayBtn.style = btnStyle
|
||||
|
||||
val cancelBtn = JFXButton(cancelText)
|
||||
cancelBtn.addEventHandler(ActionEvent.ACTION) { alert.close() }
|
||||
cancelBtn.addEventHandler(ActionEvent.ACTION, cancelAction!!)
|
||||
cancelBtn.buttonType = JFXButton.ButtonType.RAISED
|
||||
cancelBtn.prefHeight = 32.0
|
||||
cancelBtn.style = btnStyle
|
||||
|
||||
val content = JFXDialogLayout()
|
||||
//content.background = Background(BackgroundFill(Paint.valueOf("#2b2b2b"), CornerRadii.EMPTY, Insets.EMPTY))
|
||||
content.setActions(cancelBtn, okayBtn)
|
||||
content.setHeading(headingText)
|
||||
content.setBody(bodyText)
|
||||
alert.setContent(content)
|
||||
alert.showAndWait()
|
||||
}
|
||||
|
||||
}
|
@ -1,17 +1,36 @@
|
||||
package org.hso.texturesyncclient.app
|
||||
|
||||
import org.hso.texturesyncclient.controller.RootController
|
||||
import org.hso.texturesyncclient.view.importView.ImportView
|
||||
import org.hso.texturesyncclient.view.mainView.MainView
|
||||
import javafx.scene.image.Image
|
||||
import javafx.stage.Stage
|
||||
import org.hso.texturesyncclient.controller.SettingsController
|
||||
import org.hso.texturesyncclient.view.startupView.StartupView
|
||||
import org.hso.texturesyncclient.view.startupView.StartupViewController
|
||||
import tornadofx.App
|
||||
|
||||
class Main: App(StartupView::class){
|
||||
class Main : App(StartupView::class) {
|
||||
|
||||
val controller = RootController()
|
||||
private val svc: StartupViewController by inject()
|
||||
|
||||
init {
|
||||
// TODO get saved IP address, if found try to connect, else show StartupView
|
||||
override fun start(stage: Stage) {
|
||||
|
||||
// Calling super.start early prevents a weird layouting bug
|
||||
// were the button of the window is white.
|
||||
// This could(?) be caused by stage and StartupView having
|
||||
// a different min-size.
|
||||
super.start(stage)
|
||||
|
||||
stage.minWidth = 1050.00
|
||||
stage.minHeight = 700.00
|
||||
stage.width = 1050.00
|
||||
stage.height = 700.00
|
||||
stage.isResizable = true
|
||||
stage.icons.add(Image("icons/TextureSync_Icon_256x256.png"))
|
||||
stage.setOnCloseRequest { System.exit(0) }
|
||||
|
||||
stage.scene.stylesheets.add("/css/Styles.css")
|
||||
|
||||
SettingsController.init()
|
||||
svc.initConnection()
|
||||
}
|
||||
|
||||
}
|
@ -1,102 +0,0 @@
|
||||
package org.hso.texturesyncclient.controller
|
||||
|
||||
import javafx.collections.ObservableList
|
||||
import org.hso.texturesyncclient.controller.net.Connection
|
||||
import org.hso.texturesyncclient.model.Sha256
|
||||
import org.hso.texturesyncclient.model.Texture
|
||||
import org.hso.texturesyncclient.model.TextureFormat
|
||||
import org.hso.texturesyncclient.view.importView.ImportViewController
|
||||
import org.hso.texturesyncclient.view.mainView.MainView
|
||||
import org.hso.texturesyncclient.view.mainView.MainViewController
|
||||
import org.hso.texturesyncclient.view.startupView.StartupView
|
||||
import org.hso.texturesyncclient.view.startupView.StartupViewController
|
||||
import tornadofx.Controller
|
||||
import java.net.InetAddress
|
||||
import java.util.Calendar
|
||||
import java.io.File
|
||||
import javax.imageio.ImageIO
|
||||
import java.util.UUID
|
||||
import java.nio.file.Files
|
||||
|
||||
class RootController : Controller() {
|
||||
|
||||
private val mvc: MainViewController by inject()
|
||||
private val svc: StartupViewController by inject()
|
||||
private val ivc: ImportViewController by inject()
|
||||
|
||||
private lateinit var con: Connection
|
||||
|
||||
init {
|
||||
/*var data = Texture()
|
||||
var img = con.getTexturePreview(data.textureHash)
|
||||
var test = GUIModel(data, img)
|
||||
test.exportItem.setOnAction {
|
||||
|
||||
}
|
||||
mvc.addElement(test)
|
||||
|
||||
data = Texture()
|
||||
img = con.getTexturePreview(data.textureHash)
|
||||
test = GUIModel(data, img)
|
||||
test.exportItem.setOnAction {
|
||||
|
||||
}
|
||||
mvc.addElement(test)*/
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* calculate the resolution, get today's date -> upload to server
|
||||
* @param path the absolute path of the file on the client's system
|
||||
* @param name the file name
|
||||
* @param tags all tags for the file
|
||||
*/
|
||||
fun importTexture(path: String, name: String, tags: ObservableList<String>) {
|
||||
val data = Files.readAllBytes(File(path).toPath()) // this is the image as byte array
|
||||
|
||||
val uuid = UUID.randomUUID()
|
||||
val format = if (File(path).extension.toLowerCase() == "png") TextureFormat.PNG else TextureFormat.JPEG
|
||||
val bimg = ImageIO.read(File(path)) //image for obtaining resolution
|
||||
val resolution = Pair(bimg.height, bimg.width)
|
||||
val cal = Calendar.getInstance() //calendar obj with current time
|
||||
val hash = Sha256(data)
|
||||
|
||||
//Todo free image
|
||||
|
||||
val newTexture = Texture(uuid, name, tags.toTypedArray(), format, resolution, cal, hash)
|
||||
|
||||
try {
|
||||
con.uploadTexture(newTexture, data)
|
||||
println("Texture upload successful")
|
||||
} catch (e: Exception) {
|
||||
println(e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize connection to server
|
||||
* @param name server name as IP or domain
|
||||
*/
|
||||
fun initConnection(name: String) {
|
||||
try {
|
||||
con = Connection(InetAddress.getByName(name))
|
||||
println("ausgabe")
|
||||
con.ping()
|
||||
println("Connection successful")
|
||||
|
||||
|
||||
// TODO store server ip for next start
|
||||
|
||||
|
||||
// switch to MainView
|
||||
find(StartupView::class).replaceWith(MainView::class, sizeToScene = true, centerOnScreen = true)
|
||||
|
||||
} catch (e: Exception) {
|
||||
println(e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
package org.hso.texturesyncclient.controller
|
||||
|
||||
class NetworkController {
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,322 @@
|
||||
package org.hso.texturesyncclient.controller
|
||||
|
||||
import javafx.application.Platform
|
||||
import javafx.collections.ObservableList
|
||||
import javafx.event.EventHandler
|
||||
import javafx.scene.image.Image
|
||||
import javafx.stage.DirectoryChooser
|
||||
import org.hso.texturesyncclient.alerts.JFXInfoAlert
|
||||
import org.hso.texturesyncclient.alerts.JFXOkayCancelAlert
|
||||
import org.hso.texturesyncclient.controller.net.AutoConnect
|
||||
import org.hso.texturesyncclient.controller.net.Connection
|
||||
import org.hso.texturesyncclient.model.GUIModel
|
||||
import org.hso.texturesyncclient.model.Sha256
|
||||
import org.hso.texturesyncclient.model.Texture
|
||||
import org.hso.texturesyncclient.model.TextureFormat
|
||||
import org.hso.texturesyncclient.view.importView.ImportView
|
||||
import org.hso.texturesyncclient.view.mainView.MainView
|
||||
import org.hso.texturesyncclient.view.mainView.MainViewController
|
||||
import org.hso.texturesyncclient.view.startupView.StartupView
|
||||
import org.hso.texturesyncclient.view.startupView.StartupViewController
|
||||
import tornadofx.*
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
import java.net.InetAddress
|
||||
import java.nio.file.Files
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
import javax.imageio.ImageIO
|
||||
|
||||
class RootController : Controller() {
|
||||
|
||||
private val mvc: MainViewController by inject()
|
||||
private val svc: StartupViewController by inject()
|
||||
|
||||
private lateinit var con: Connection
|
||||
private lateinit var selectedTextureModel: GUIModel
|
||||
private var lastExportDir: String = System.getProperty("user.home")
|
||||
|
||||
/**
|
||||
* calculate the resolution, get today's date -> upload to server
|
||||
* @param path the absolute path of the file on the client's system
|
||||
* @param name the file name
|
||||
* @param tags all tags for the file
|
||||
*/
|
||||
fun importTexture(path: String, name: String, tags: ObservableList<String>) {
|
||||
val data = Files.readAllBytes(File(path).toPath()) // this is the image as byte array
|
||||
|
||||
val uuid = UUID.randomUUID()
|
||||
val format = if (File(path).extension.toLowerCase() == "png") TextureFormat.PNG else TextureFormat.JPEG
|
||||
val bimg = ImageIO.read(File(path)) //image for obtaining resolution
|
||||
val resolution = Pair(bimg.width, bimg.height)
|
||||
val cal = Calendar.getInstance() //calendar obj with current time
|
||||
val hash = Sha256(data)
|
||||
|
||||
|
||||
val alertImportHash = JFXInfoAlert(
|
||||
"Upload fehlgeschlagen",
|
||||
"Die Textur wurde nicht fehlerfrei übertragen",
|
||||
"-fx-button-type: RAISED; -fx-background-color: #2b7bbb; -fx-text-fill: #000000;"
|
||||
)
|
||||
|
||||
val alertImport = JFXInfoAlert(
|
||||
"Upload fehlgeschlagen",
|
||||
"Mögliche Ursachen:" +
|
||||
"\n- Textur existiert bereits" +
|
||||
"\n- Server nicht bereit",
|
||||
"-fx-button-type: RAISED; -fx-background-color: #2b7bbb; -fx-text-fill: #000000;"
|
||||
)
|
||||
|
||||
|
||||
val newTexture = Texture(uuid, name, tags.toTypedArray(), format, resolution, cal, hash)
|
||||
|
||||
try {
|
||||
con.uploadTexture(newTexture, data)
|
||||
println("Texture upload successful")
|
||||
} catch (e: IllegalArgumentException) {
|
||||
println(e)
|
||||
alertImportHash.showAndWait()
|
||||
} catch (e: Exception) {
|
||||
alertImport.showAndWait()
|
||||
println(e)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize connection to server and switch to MainView if connected
|
||||
* @param name server name as IP or domain
|
||||
*/
|
||||
fun initConnection(name: String) {
|
||||
if (name == " ") {
|
||||
//no user input, try automatic connect or restore address from settings file
|
||||
println("try auto connect")
|
||||
try {
|
||||
val foundServer = AutoConnect.searchServer()
|
||||
if (foundServer != null) {
|
||||
println("[auto] server found")
|
||||
con = foundServer
|
||||
con.ping()
|
||||
println("[auto] Connection to Server successful")
|
||||
switchStartupToMain()
|
||||
showAll()
|
||||
|
||||
return // abort
|
||||
} else {
|
||||
println("[auto] no server found")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
println(e)
|
||||
println("[auto] Connection to Server NOT successful")
|
||||
}
|
||||
if (SettingsController.serverAddressIsSet()) {
|
||||
println("[file] try connect with settings file")
|
||||
try {
|
||||
svc.setServerAddress(SettingsController.getServerAddress()) // FIXME this crashes the client when there is either not text or no label!
|
||||
con = Connection(InetAddress.getByName(SettingsController.getServerAddress()))
|
||||
con.ping()
|
||||
println("[file] Connection to Server successful")
|
||||
switchStartupToMain()
|
||||
showAll()
|
||||
|
||||
return // abort
|
||||
} catch (e: Exception) {
|
||||
println(e)
|
||||
println("[file] Connection to Server NOT successful")
|
||||
}
|
||||
} else {
|
||||
println("[file] no address in settings file")
|
||||
}
|
||||
} else {
|
||||
//try to connect with user input
|
||||
try {
|
||||
println("try connect with user input")
|
||||
con = Connection(InetAddress.getByName(name))
|
||||
con.ping()
|
||||
println("Connection to Server successful")
|
||||
SettingsController.setServerAddress(name) //store address in settings file
|
||||
switchStartupToMain()
|
||||
showAll()
|
||||
println("swithing to MainView @ initCon")
|
||||
|
||||
return // abort
|
||||
} catch (e: Exception) {
|
||||
println(e)
|
||||
println("Connection to Server NOT successful")
|
||||
|
||||
Platform.runLater {
|
||||
JFXInfoAlert(
|
||||
"Verbinden fehlgeschlagen",
|
||||
"Mögliche Ursachen:" +
|
||||
"\n- der angegebene Server ist offline" +
|
||||
"\n- die benötigten Ports sind nicht offen",
|
||||
"-fx-button-type: RAISED; -fx-background-color: #2b7bbb; -fx-text-fill: #000000;"
|
||||
).showAndWait()
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun queryElements(tags: ObservableList<String>): ArrayList<GUIModel> {
|
||||
mvc.setVisibleMetaTags(false)
|
||||
val previewList = arrayListOf<GUIModel>()
|
||||
|
||||
try {
|
||||
con.query(tags.toTypedArray()).forEach {
|
||||
runAsync {
|
||||
Platform.runLater {
|
||||
mvc.addElement(GUIModel(it, con.getTexturePreview(it.textureHash)))
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
println(e)
|
||||
}
|
||||
|
||||
println(previewList.size)
|
||||
|
||||
return previewList
|
||||
}
|
||||
|
||||
/**
|
||||
* save the texture file to a local directory
|
||||
* @param data the texture as meta element
|
||||
*/
|
||||
fun exportTexture(data: Texture) {
|
||||
val directoryChooser = DirectoryChooser()
|
||||
directoryChooser.title = "Export Verzeichnis wählen"
|
||||
directoryChooser.initialDirectory = if(File(lastExportDir).exists()) File(lastExportDir) else File(System.getProperty("user.home"))
|
||||
|
||||
|
||||
val alertExport = JFXInfoAlert(
|
||||
"Exporterien fehlgeschlagen",
|
||||
"Mögliche Ursachen:" +
|
||||
"\n- Datei existiert bereits" +
|
||||
"\n- Ordner nicht beschreibbar",
|
||||
"-fx-button-type: RAISED; -fx-background-color: #2b7bbb; -fx-text-fill: #000000;"
|
||||
)
|
||||
|
||||
val dir = directoryChooser.showDialog(primaryStage)
|
||||
if (dir != null) {
|
||||
val extension = if (data.format == TextureFormat.PNG) ".png" else ".jpeg" //get file format
|
||||
val filePath = "$dir/${data.name}$extension" //build absolute exported texture path
|
||||
val exportedFile = File(filePath) //create file
|
||||
|
||||
if (exportedFile.exists()) {
|
||||
alertExport.showAndWait()
|
||||
} else {
|
||||
try {
|
||||
val fileout = FileOutputStream(exportedFile)
|
||||
fileout.write(con.getTextureFile(data.textureHash)) //write bytes in file fileout.close()
|
||||
fileout.close()
|
||||
} catch (e: IOException) {
|
||||
alertExport.showAndWait()
|
||||
}
|
||||
}
|
||||
lastExportDir = dir.absolutePath //store last user chosen dir
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* show the detailed meta information in the DetailView
|
||||
* @param data the texture as meta element
|
||||
*/
|
||||
fun showDetail(data: Texture) {
|
||||
mvc.setPreview3DTexture(con.getTexturePreview(data.textureHash))
|
||||
val sdf = SimpleDateFormat("dd.MM.yyyy")
|
||||
|
||||
mvc.setMeta(
|
||||
data.name,
|
||||
"${data.resolution.first}px x ${data.resolution.second}px",
|
||||
data.format.toString(),
|
||||
sdf.format(data.addedOn.time)
|
||||
)
|
||||
mvc.setTags(data.tags.toList().observable())
|
||||
mvc.setVisibleMetaTags(true)
|
||||
}
|
||||
|
||||
/**
|
||||
* remove a texture from the FolderView and the server
|
||||
*/
|
||||
fun deleteTexture() {
|
||||
val dialogDelete = JFXOkayCancelAlert(
|
||||
"Löschen",
|
||||
"Textur ${selectedTextureModel.data.name} wirklich löschen?",
|
||||
"-fx-button-type: RAISED; -fx-background-color: #2b7bbb; -fx-text-fill: #000000;"
|
||||
)
|
||||
dialogDelete.okayAction = EventHandler {
|
||||
con.deleteTexture(selectedTextureModel.data)
|
||||
mvc.removeTextureFromView(selectedTextureModel.data)
|
||||
// reset the DetailView
|
||||
mvc.setVisibleMetaTags(false)
|
||||
mvc.setPreview3DTexture(Image("icons/TextureSync_Icon_256x256.jpeg"))
|
||||
}
|
||||
dialogDelete.cancelAction = EventHandler {
|
||||
// Do nothing
|
||||
}
|
||||
dialogDelete.showAndWait()
|
||||
}
|
||||
|
||||
fun updateTexture(name: String, tags: Array<String>) {
|
||||
val newTexture = selectedTextureModel.data.copy(
|
||||
tags = tags,
|
||||
name = name
|
||||
)
|
||||
try {
|
||||
con.updateTexture(
|
||||
selectedTextureModel.data,
|
||||
newTexture,
|
||||
con.getTextureFile(selectedTextureModel.data.textureHash)
|
||||
)
|
||||
selectedTextureModel.data = newTexture
|
||||
} catch (e: Exception) {
|
||||
println(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* show all available textures at start
|
||||
*/
|
||||
private fun showAll() {
|
||||
queryElements(mvc.getTags())
|
||||
}
|
||||
|
||||
/**
|
||||
* set the last selected texture-GUIModell
|
||||
* @param model the last selected element
|
||||
*/
|
||||
fun setSelectedTexture(model: GUIModel) {
|
||||
selectedTextureModel = model
|
||||
selectedTexture = model.data
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
var selectedTexture: Texture? = null
|
||||
|
||||
fun switchStartupToMain() {
|
||||
Platform.runLater {
|
||||
find(StartupView::class).replaceWith(MainView::class, ViewTransition.FadeThrough(0.01.seconds))
|
||||
}
|
||||
}
|
||||
|
||||
// These runLater calls should be unnecessary
|
||||
fun switchMainToImport() {
|
||||
Platform.runLater {
|
||||
find(MainView::class).replaceWith(ImportView::class, ViewTransition.FadeThrough(0.01.seconds))
|
||||
}
|
||||
}
|
||||
|
||||
fun switchImportToMain(needsReload: Boolean) {
|
||||
Platform.runLater {
|
||||
find(ImportView::class).replaceWith(MainView::class, ViewTransition.FadeThrough(0.01.seconds))
|
||||
if (needsReload) {
|
||||
find(MainView::class).repeatSearch()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
package org.hso.texturesyncclient.controller
|
||||
|
||||
import java.io.*
|
||||
import java.util.Properties
|
||||
|
||||
|
||||
class SettingsController {
|
||||
|
||||
companion object {
|
||||
|
||||
private lateinit var serverAddress: String
|
||||
|
||||
private lateinit var props: Properties
|
||||
|
||||
private val userHome = System.getProperty("user.home")
|
||||
private val osName = System.getProperty("os.name")
|
||||
|
||||
private lateinit var dirPath: String //path to settings file
|
||||
private lateinit var settingsFile: File //settings file
|
||||
|
||||
private const val defaultAddressValue: String = " "
|
||||
|
||||
fun init() {
|
||||
props = Properties()
|
||||
|
||||
dirPath = if (osName.contains("Windows")) {
|
||||
"$userHome/Documents/TextureSync"
|
||||
} else {
|
||||
"$userHome/.config/TextureSync"
|
||||
}
|
||||
|
||||
settingsFile = File("$dirPath/config.xml") //open Settings file
|
||||
|
||||
if (!settingsFile.exists()) {
|
||||
println("settings not found! Will create new one")
|
||||
File(dirPath).mkdir()
|
||||
settingsFile.createNewFile()
|
||||
serverAddress = defaultAddressValue //load default value
|
||||
saveSettings()
|
||||
} else {
|
||||
println("settings found")
|
||||
loadSettings()
|
||||
}
|
||||
}
|
||||
|
||||
fun serverAddressIsSet(): Boolean {
|
||||
if (serverAddress == defaultAddressValue) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun loadSettings() {
|
||||
val inputStream: InputStream
|
||||
inputStream = FileInputStream(settingsFile)
|
||||
props.loadFromXML(inputStream)
|
||||
serverAddress = props.getProperty("serverAddress")
|
||||
inputStream.close()
|
||||
}
|
||||
|
||||
private fun saveSettings() {
|
||||
val outputStream: OutputStream
|
||||
props.setProperty("serverAddress", serverAddress)
|
||||
outputStream = FileOutputStream(settingsFile)
|
||||
props.storeToXML(outputStream, "TextureSync settings")
|
||||
outputStream.close()
|
||||
println("settings saved")
|
||||
}
|
||||
|
||||
fun getServerAddress(): String {
|
||||
return serverAddress
|
||||
}
|
||||
|
||||
fun setServerAddress(serverAddress: String) {
|
||||
this.serverAddress = serverAddress
|
||||
saveSettings()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
package org.hso.texturesyncclient.controller.net
|
||||
|
||||
import java.io.IOException
|
||||
import java.net.DatagramPacket
|
||||
import java.net.DatagramSocket
|
||||
import java.net.InetAddress
|
||||
|
||||
class AutoConnect private constructor() {
|
||||
|
||||
companion object {
|
||||
@Throws(IOException::class)
|
||||
fun searchServer(
|
||||
mulicastAddr: String = "ff02::dd42:c0fe",
|
||||
port: Int = 10796,
|
||||
timeoutMs: Int = 400,
|
||||
trys: Int = 10
|
||||
): Connection? {
|
||||
val sock = DatagramSocket()
|
||||
try {
|
||||
sock.soTimeout = timeoutMs
|
||||
|
||||
for (i in 0..trys) {
|
||||
val bytes = "TextureSync".toByteArray()
|
||||
|
||||
sock.send(
|
||||
DatagramPacket(
|
||||
bytes,
|
||||
0,
|
||||
bytes.size,
|
||||
InetAddress.getByName(mulicastAddr),
|
||||
port
|
||||
)
|
||||
)
|
||||
|
||||
// Response is PortNum in BE
|
||||
val portData = ByteArray(2)
|
||||
val response = DatagramPacket(portData, portData.size)
|
||||
|
||||
try {
|
||||
sock.receive(response)
|
||||
|
||||
// 2-Byte BE to Port Number
|
||||
val serverPort = (portData[0].toInt().shl(8)).or(portData[1].toInt())
|
||||
|
||||
return Connection(response.address, serverPort)
|
||||
} catch (e: IOException) {
|
||||
// Timed out
|
||||
// NOP
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
throw e
|
||||
} finally {
|
||||
sock.close()
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
@ -19,7 +19,7 @@ class Connection(val address: InetAddress, val port: Int = 10796) : Closeable {
|
||||
|
||||
@Throws(IOException::class)
|
||||
@Synchronized
|
||||
private fun getStreams(): Pair<DataInputStream, DataOutputStream> {
|
||||
private fun <R> withStreams(retry: Boolean = true, inner: (DataInputStream, DataOutputStream) -> R): R {
|
||||
val i: DataInputStream
|
||||
val o: DataOutputStream
|
||||
|
||||
@ -40,240 +40,253 @@ class Connection(val address: InetAddress, val port: Int = 10796) : Closeable {
|
||||
o = output!!
|
||||
}
|
||||
|
||||
return Pair(i, o)
|
||||
return try {
|
||||
inner(i, o)
|
||||
} catch (e: IOException) {
|
||||
if (retry) {
|
||||
println("Got IOException; Retry ${e.message}")
|
||||
close()
|
||||
withStreams(false, inner)
|
||||
} else {
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IOException::class, ConnectionException::class)
|
||||
@Synchronized
|
||||
fun ping() {
|
||||
val io = getStreams()
|
||||
return withStreams { i, o ->
|
||||
|
||||
val obj = JsonObject()
|
||||
obj.add("ping", JsonObject())
|
||||
val obj = JsonObject()
|
||||
obj.add("ping", JsonObject())
|
||||
|
||||
JsonPackage(obj).write(io.second)
|
||||
JsonPackage(obj).write(o)
|
||||
|
||||
when (val pkg = Package.read(io.first)) {
|
||||
is JsonPackage -> return
|
||||
is BinaryPackage -> throw ConnectionUnexpectedPacketException()
|
||||
is ErrorPackage -> throw ConnectionErrorException(pkg)
|
||||
else -> throw RuntimeException("Unreachable")
|
||||
when (val pkg = Package.read(i)) {
|
||||
is JsonPackage -> return@withStreams
|
||||
is BinaryPackage -> throw ConnectionUnexpectedPacketException()
|
||||
is ErrorPackage -> throw ConnectionErrorException(pkg)
|
||||
else -> throw RuntimeException("Unreachable")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IOException::class, ConnectionException::class)
|
||||
@Synchronized
|
||||
fun query(query: Array<String>): Array<Texture> {
|
||||
val io = getStreams()
|
||||
return withStreams { i, o ->
|
||||
|
||||
val obj = JsonObject()
|
||||
obj.add("query", {
|
||||
val inner = JsonObject()
|
||||
inner.add("query", {
|
||||
val array = JsonArray()
|
||||
for (queryString in query) {
|
||||
array.add(queryString)
|
||||
}
|
||||
array
|
||||
val obj = JsonObject()
|
||||
obj.add("query", {
|
||||
val inner = JsonObject()
|
||||
inner.add("query", {
|
||||
val array = JsonArray()
|
||||
for (queryString in query) {
|
||||
array.add(queryString)
|
||||
}
|
||||
array
|
||||
}())
|
||||
inner
|
||||
}())
|
||||
inner
|
||||
}())
|
||||
|
||||
JsonPackage(obj).write(io.second)
|
||||
JsonPackage(obj).write(o)
|
||||
|
||||
when (val pkg = Package.read(io.first)) {
|
||||
is JsonPackage -> {
|
||||
try {
|
||||
return Gson().fromJson<Array<InternalTexture>>(pkg.content, Array<InternalTexture>::class.java)
|
||||
.map { tex ->
|
||||
tex.toTexture()
|
||||
}.toTypedArray()
|
||||
} catch (e: JsonSyntaxException) {
|
||||
throw ConnectionInvalidJsonException()
|
||||
when (val pkg = Package.read(i)) {
|
||||
is JsonPackage -> {
|
||||
try {
|
||||
return@withStreams Gson().fromJson<Array<InternalTexture>>(
|
||||
pkg.content,
|
||||
Array<InternalTexture>::class.java
|
||||
)
|
||||
.map { tex ->
|
||||
tex.toTexture()
|
||||
}.toTypedArray()
|
||||
} catch (e: JsonSyntaxException) {
|
||||
throw ConnectionInvalidJsonException()
|
||||
}
|
||||
}
|
||||
is BinaryPackage -> throw ConnectionUnexpectedPacketException()
|
||||
is ErrorPackage -> throw ConnectionErrorException(pkg)
|
||||
else -> throw RuntimeException("Unreachable")
|
||||
}
|
||||
is BinaryPackage -> throw ConnectionUnexpectedPacketException()
|
||||
is ErrorPackage -> throw ConnectionErrorException(pkg)
|
||||
else -> throw RuntimeException("Unreachable")
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IOException::class, ConnectionException::class)
|
||||
@Synchronized
|
||||
fun getTextureById(id: UUID): Texture? {
|
||||
val io = getStreams()
|
||||
return withStreams { i, o ->
|
||||
|
||||
val obj = JsonObject()
|
||||
obj.add("get_texture", {
|
||||
val inner = JsonObject()
|
||||
inner.addProperty("id", id.toString())
|
||||
inner
|
||||
}())
|
||||
|
||||
JsonPackage(obj).write(io.second)
|
||||
val obj = JsonObject()
|
||||
obj.add("get_texture", {
|
||||
val inner = JsonObject()
|
||||
inner.addProperty("id", id.toString())
|
||||
inner
|
||||
}())
|
||||
|
||||
when (val pkg = Package.read(io.first)) {
|
||||
is JsonPackage -> {
|
||||
return if (pkg.content.isJsonNull) {
|
||||
null
|
||||
} else {
|
||||
try {
|
||||
Gson()
|
||||
.fromJson<InternalTexture>(pkg.content, InternalTexture::class.java)
|
||||
.toTexture()
|
||||
} catch (e: JsonSyntaxException) {
|
||||
throw ConnectionInvalidJsonException()
|
||||
JsonPackage(obj).write(o)
|
||||
|
||||
when (val pkg = Package.read(i)) {
|
||||
is JsonPackage -> {
|
||||
return@withStreams if (pkg.content.isJsonNull) {
|
||||
null
|
||||
} else {
|
||||
try {
|
||||
Gson()
|
||||
.fromJson<InternalTexture>(pkg.content, InternalTexture::class.java)
|
||||
.toTexture()
|
||||
} catch (e: JsonSyntaxException) {
|
||||
throw ConnectionInvalidJsonException()
|
||||
}
|
||||
}
|
||||
}
|
||||
is BinaryPackage -> throw ConnectionUnexpectedPacketException()
|
||||
is ErrorPackage -> throw ConnectionErrorException(pkg)
|
||||
else -> throw RuntimeException("Unreachable")
|
||||
}
|
||||
is BinaryPackage -> throw ConnectionUnexpectedPacketException()
|
||||
is ErrorPackage -> throw ConnectionErrorException(pkg)
|
||||
else -> throw RuntimeException("Unreachable")
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IOException::class, ConnectionException::class)
|
||||
@Synchronized
|
||||
fun getTextureByName(name: String): Texture? {
|
||||
val io = getStreams()
|
||||
return withStreams { i, o ->
|
||||
|
||||
val obj = JsonObject()
|
||||
obj.add("get_texture", {
|
||||
val inner = JsonObject()
|
||||
inner.addProperty("name", name)
|
||||
inner
|
||||
}())
|
||||
val obj = JsonObject()
|
||||
obj.add("get_texture", {
|
||||
val inner = JsonObject()
|
||||
inner.addProperty("name", name)
|
||||
inner
|
||||
}())
|
||||
|
||||
JsonPackage(obj).write(io.second)
|
||||
JsonPackage(obj).write(o)
|
||||
|
||||
when (val pkg = Package.read(io.first)) {
|
||||
is JsonPackage -> {
|
||||
return if (pkg.content.isJsonNull) {
|
||||
null
|
||||
} else {
|
||||
try {
|
||||
Gson()
|
||||
.fromJson<InternalTexture>(pkg.content, InternalTexture::class.java)
|
||||
.toTexture()
|
||||
} catch (e: JsonSyntaxException) {
|
||||
throw ConnectionInvalidJsonException()
|
||||
when (val pkg = Package.read(i)) {
|
||||
is JsonPackage -> {
|
||||
return@withStreams if (pkg.content.isJsonNull) {
|
||||
null
|
||||
} else {
|
||||
try {
|
||||
Gson()
|
||||
.fromJson<InternalTexture>(pkg.content, InternalTexture::class.java)
|
||||
.toTexture()
|
||||
} catch (e: JsonSyntaxException) {
|
||||
throw ConnectionInvalidJsonException()
|
||||
}
|
||||
}
|
||||
}
|
||||
is BinaryPackage -> throw ConnectionUnexpectedPacketException()
|
||||
is ErrorPackage -> throw ConnectionErrorException(pkg)
|
||||
else -> throw RuntimeException("Unreachable")
|
||||
}
|
||||
is BinaryPackage -> throw ConnectionUnexpectedPacketException()
|
||||
is ErrorPackage -> throw ConnectionErrorException(pkg)
|
||||
else -> throw RuntimeException("Unreachable")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Throws(IOException::class, ConnectionException::class)
|
||||
@Synchronized
|
||||
fun getTextureFile(hash: Sha256): ByteArray {
|
||||
val io = getStreams()
|
||||
return withStreams { i, o ->
|
||||
|
||||
val obj = JsonObject()
|
||||
obj.add("get_texture_file", {
|
||||
val inner = JsonObject()
|
||||
inner.addProperty("texture_hash", hash.toString())
|
||||
inner
|
||||
}())
|
||||
val obj = JsonObject()
|
||||
obj.add("get_texture_file", {
|
||||
val inner = JsonObject()
|
||||
inner.addProperty("texture_hash", hash.toString())
|
||||
inner
|
||||
}())
|
||||
|
||||
JsonPackage(obj).write(io.second)
|
||||
JsonPackage(obj).write(o)
|
||||
|
||||
when (val pkg = Package.read(io.first)) {
|
||||
is JsonPackage -> throw ConnectionUnexpectedPacketException()
|
||||
is BinaryPackage -> return pkg.content
|
||||
is ErrorPackage -> throw ConnectionErrorException(pkg)
|
||||
else -> throw RuntimeException("Unreachable")
|
||||
when (val pkg = Package.read(i)) {
|
||||
is JsonPackage -> throw ConnectionUnexpectedPacketException()
|
||||
is BinaryPackage -> return@withStreams pkg.content
|
||||
is ErrorPackage -> throw ConnectionErrorException(pkg)
|
||||
else -> throw RuntimeException("Unreachable")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Throws(IOException::class, ConnectionException::class)
|
||||
@Synchronized
|
||||
fun getTexturePreview(hash: Sha256): Image {
|
||||
val io = getStreams()
|
||||
return withStreams { i, o ->
|
||||
val obj = JsonObject()
|
||||
obj.add("get_texture_preview", {
|
||||
val inner = JsonObject()
|
||||
inner.addProperty("texture_hash", hash.toString())
|
||||
inner.addProperty("desired_format", "jpeg")
|
||||
inner
|
||||
}())
|
||||
|
||||
val obj = JsonObject()
|
||||
obj.add("get_texture_preview", {
|
||||
val inner = JsonObject()
|
||||
inner.addProperty("texture_hash", hash.toString())
|
||||
inner.addProperty("desired_format", "jpeg")
|
||||
inner
|
||||
}())
|
||||
JsonPackage(obj).write(o)
|
||||
|
||||
JsonPackage(obj).write(io.second)
|
||||
|
||||
when (val pkg = Package.read(io.first)) {
|
||||
is JsonPackage -> throw ConnectionUnexpectedPacketException()
|
||||
is BinaryPackage -> {
|
||||
return Image(ByteArrayInputStream(pkg.content))
|
||||
when (val pkg = Package.read(i)) {
|
||||
is JsonPackage -> throw ConnectionUnexpectedPacketException()
|
||||
is BinaryPackage -> {
|
||||
return@withStreams Image(ByteArrayInputStream(pkg.content))
|
||||
}
|
||||
is ErrorPackage -> throw ConnectionErrorException(pkg)
|
||||
else -> throw RuntimeException("Unreachable")
|
||||
}
|
||||
is ErrorPackage -> throw ConnectionErrorException(pkg)
|
||||
else -> throw RuntimeException("Unreachable")
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IOException::class, ConnectionException::class, IllegalArgumentException::class)
|
||||
@Synchronized
|
||||
private fun replaceTexture(old: Texture?, new: Texture?, image: ByteArray?) {
|
||||
val io = getStreams()
|
||||
return withStreams { i, o ->
|
||||
|
||||
val obj = JsonObject()
|
||||
obj.add("replace_texture", {
|
||||
val inner = JsonObject()
|
||||
if (old != null) {
|
||||
inner.add("old", Gson().toJsonTree(InternalTexture(old), InternalTexture::class.java))
|
||||
} else {
|
||||
inner.add("old", null)
|
||||
}
|
||||
if (new != null) {
|
||||
inner.add("new", Gson().toJsonTree(InternalTexture(new), InternalTexture::class.java))
|
||||
} else {
|
||||
inner.add("new", null)
|
||||
}
|
||||
inner
|
||||
}())
|
||||
|
||||
JsonPackage(obj).write(io.second)
|
||||
val obj = JsonObject()
|
||||
obj.add("replace_texture", {
|
||||
val inner = JsonObject()
|
||||
if (old != null) {
|
||||
inner.add("old", Gson().toJsonTree(InternalTexture(old), InternalTexture::class.java))
|
||||
} else {
|
||||
inner.add("old", null)
|
||||
}
|
||||
if (new != null) {
|
||||
inner.add("new", Gson().toJsonTree(InternalTexture(new), InternalTexture::class.java))
|
||||
} else {
|
||||
inner.add("new", null)
|
||||
}
|
||||
inner
|
||||
}())
|
||||
|
||||
when (val pkg = Package.read(io.first)) {
|
||||
is JsonPackage -> {
|
||||
if (pkg.content == JsonPrimitive(true)) {
|
||||
// everthing is fine!
|
||||
return
|
||||
} else if (image != null) {
|
||||
// should be { "get_texture_file": { texture_hash : <Hash> }}
|
||||
// we don't check, since there is no good way to handle it.
|
||||
JsonPackage(obj).write(o)
|
||||
|
||||
BinaryPackage(image).write(io.second)
|
||||
when (val ipkg = Package.read(io.first)) {
|
||||
is JsonPackage -> {
|
||||
if (ipkg.content != JsonPrimitive(true)) {
|
||||
// Protokoll Assertion failed
|
||||
throw ConnectionUnexpectedPacketException()
|
||||
when (val pkg = Package.read(i)) {
|
||||
is JsonPackage -> {
|
||||
when {
|
||||
pkg.content == JsonPrimitive(true) -> // everthing is fine!
|
||||
return@withStreams
|
||||
image != null -> {
|
||||
// should be { "get_texture_file": { texture_hash : <Hash> }}
|
||||
// we don't check, since there is no good way to handle it.
|
||||
|
||||
BinaryPackage(image).write(o)
|
||||
when (val ipkg = Package.read(i)) {
|
||||
is JsonPackage -> {
|
||||
if (ipkg.content != JsonPrimitive(true)) {
|
||||
// Protokoll Assertion failed
|
||||
throw ConnectionUnexpectedPacketException()
|
||||
}
|
||||
}
|
||||
is BinaryPackage -> throw ConnectionUnexpectedPacketException()
|
||||
is ErrorPackage -> throw ConnectionErrorException(ipkg)
|
||||
else -> throw RuntimeException("Unreachable")
|
||||
}
|
||||
}
|
||||
is BinaryPackage -> throw ConnectionUnexpectedPacketException()
|
||||
is ErrorPackage -> throw ConnectionErrorException(ipkg)
|
||||
else -> throw RuntimeException("Unreachable")
|
||||
else -> {
|
||||
ErrorPackage(404, "Texture not found!").write(o)
|
||||
close() // gets re-opened on next request.
|
||||
throw IllegalArgumentException("Image Argument was needed.")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ErrorPackage(404, "Texture not found!").write(io.second)
|
||||
close() // gets re-opened on next request.
|
||||
throw IllegalArgumentException("Image Argument was needed.")
|
||||
}
|
||||
is BinaryPackage -> throw ConnectionUnexpectedPacketException()
|
||||
is ErrorPackage -> throw ConnectionErrorException(pkg)
|
||||
else -> throw RuntimeException("Unreachable")
|
||||
}
|
||||
is BinaryPackage -> throw ConnectionUnexpectedPacketException()
|
||||
is ErrorPackage -> throw ConnectionErrorException(pkg)
|
||||
else -> throw RuntimeException("Unreachable")
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IOException::class, ConnectionException::class, IllegalArgumentException::class)
|
||||
@Synchronized
|
||||
fun uploadTexture(texture: Texture, image: ByteArray) {
|
||||
if (texture.textureHash != Sha256(image)) {
|
||||
throw IllegalArgumentException("Sha256 of Image does not Match with Texture.")
|
||||
@ -283,7 +296,6 @@ class Connection(val address: InetAddress, val port: Int = 10796) : Closeable {
|
||||
}
|
||||
|
||||
@Throws(IOException::class, ConnectionException::class, IllegalArgumentException::class)
|
||||
@Synchronized
|
||||
fun updateTexture(old: Texture, new: Texture, image: ByteArray) {
|
||||
if (new.textureHash != Sha256(image)) {
|
||||
throw IllegalArgumentException("Sha256 of Image does not Match with Texture.")
|
||||
@ -293,25 +305,27 @@ class Connection(val address: InetAddress, val port: Int = 10796) : Closeable {
|
||||
}
|
||||
|
||||
@Throws(IOException::class, ConnectionException::class, IllegalArgumentException::class)
|
||||
@Synchronized
|
||||
fun deleteTexture(texture: Texture) {
|
||||
replaceTexture(texture, null, null)
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
@Synchronized
|
||||
override fun close() {
|
||||
if (output != null) {
|
||||
output!!.close()
|
||||
output = null
|
||||
}
|
||||
if (input != null) {
|
||||
input!!.close()
|
||||
input = null
|
||||
}
|
||||
if (socket != null) {
|
||||
socket!!.close()
|
||||
socket = null
|
||||
try {
|
||||
if (output != null) {
|
||||
output!!.close()
|
||||
output = null
|
||||
}
|
||||
if (input != null) {
|
||||
input!!.close()
|
||||
input = null
|
||||
}
|
||||
if (socket != null) {
|
||||
socket!!.close()
|
||||
socket = null
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ internal data class InternalTexture(
|
||||
resolution = arrayOf(tex.resolution.first, tex.resolution.second),
|
||||
added_on = arrayOf(
|
||||
tex.addedOn.get(Calendar.YEAR), //
|
||||
tex.addedOn.get(Calendar.MONTH), //
|
||||
tex.addedOn.get(Calendar.MONTH) + 1, //
|
||||
tex.addedOn.get(Calendar.DAY_OF_MONTH)
|
||||
),
|
||||
texture_hash = tex.textureHash.toString()
|
||||
@ -50,7 +50,7 @@ internal data class InternalTexture(
|
||||
else -> throw ConnectionInvalidJsonException()
|
||||
},
|
||||
resolution = Pair(resolution[0], resolution[1]),
|
||||
addedOn = GregorianCalendar(added_on[0], added_on[1], added_on[2]),
|
||||
addedOn = GregorianCalendar(added_on[0], added_on[1] - 1, added_on[2]),
|
||||
textureHash = Sha256(texture_hash)
|
||||
)
|
||||
} catch (e: Exception) { // i Know, but no time :[]
|
||||
|
@ -6,7 +6,7 @@ import java.lang.Exception
|
||||
|
||||
sealed class ConnectionException(override val message: String) : Exception(message)
|
||||
|
||||
class ConnectionErrorException(val errorCode: Int, val errorMessage: String) :
|
||||
class ConnectionErrorException(errorCode: Int, errorMessage: String) :
|
||||
ConnectionException("$errorCode $errorMessage") {
|
||||
internal constructor(err: ErrorPackage) : this(err.code, err.message)
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ enum class TextureFormat {
|
||||
data class Texture(
|
||||
val id : UUID,
|
||||
val name : String,
|
||||
val tags : Array<String>,
|
||||
var tags : Array<String>,
|
||||
val format : TextureFormat,
|
||||
val resolution : Pair<Int, Int>,
|
||||
val addedOn : Calendar,
|
||||
|
@ -12,30 +12,65 @@ import javafx.scene.layout.BackgroundFill
|
||||
import javafx.scene.layout.CornerRadii
|
||||
import javafx.scene.layout.VBox
|
||||
import javafx.scene.paint.Paint
|
||||
import tornadofx.*
|
||||
import tornadofx.addClass
|
||||
import tornadofx.find
|
||||
import tornadofx.paddingTop
|
||||
|
||||
class GUIModel constructor(data: Texture, img: Image) : VBox(){
|
||||
class GUIModel constructor(var data: Texture, img: Image) : VBox() {
|
||||
|
||||
private var image = ImageView()
|
||||
private var label = Label()
|
||||
private var contextMenu = ContextMenu()
|
||||
var exportItem = MenuItem("exportiern")
|
||||
private var exportItem = MenuItem("exportiern")
|
||||
private var deleteItem = MenuItem("löschen")
|
||||
|
||||
private val gmc = find(GUIModelController::class)
|
||||
|
||||
init {
|
||||
super.setPadding(Insets(5.0, 5.0, 5.0, 5.0))
|
||||
super.getChildren().addAll(image, label)
|
||||
super.setOnContextMenuRequested { p0 -> contextMenu.show(this@GUIModel, p0.screenX, p0.screenY) }
|
||||
super.setOnMouseClicked {
|
||||
if (gmc.isLastSelectedInitialized()) {
|
||||
gmc.lastSelected.background = Background.EMPTY
|
||||
this.background = Background(BackgroundFill(Paint.valueOf("#2b7bbb"), CornerRadii.EMPTY, Insets.EMPTY))
|
||||
gmc.lastSelected = this
|
||||
} else {
|
||||
this.background = Background(BackgroundFill(Paint.valueOf("#2b7bbb"), CornerRadii.EMPTY, Insets.EMPTY))
|
||||
gmc.lastSelected = this
|
||||
}
|
||||
gmc.previewSelectedAction(data)
|
||||
gmc.setSelected(this)
|
||||
|
||||
}
|
||||
|
||||
label.addClass("metadata")
|
||||
label.paddingTop = 5.0
|
||||
label.prefWidth = 128.0
|
||||
label.alignment = Pos.CENTER
|
||||
label.text = data.name
|
||||
label.background = Background(BackgroundFill(Paint.valueOf("#FFFF2b"), CornerRadii.EMPTY, Insets.EMPTY))
|
||||
|
||||
label.text = if (data.name.length > 15) {
|
||||
"${data.name.subSequence(0, 14)}.."
|
||||
} else {
|
||||
data.name
|
||||
}
|
||||
|
||||
label.background = Background(BackgroundFill(Paint.valueOf("#3a3a3a"), CornerRadii.EMPTY, Insets.EMPTY))
|
||||
|
||||
image.fitHeight = 128.0
|
||||
image.fitWidth = 128.0
|
||||
image.image = img
|
||||
|
||||
exportItem.setOnAction {
|
||||
gmc.export(data)
|
||||
}
|
||||
|
||||
deleteItem.setOnAction {
|
||||
gmc.delete()
|
||||
}
|
||||
|
||||
contextMenu.items.add(exportItem)
|
||||
contextMenu.items.add(deleteItem)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package org.hso.texturesyncclient.model
|
||||
|
||||
import org.hso.texturesyncclient.controller.RootController
|
||||
import tornadofx.Controller
|
||||
|
||||
class GUIModelController : Controller() {
|
||||
|
||||
private val rootc = find(RootController::class)
|
||||
|
||||
lateinit var lastSelected: GUIModel
|
||||
fun isLastSelectedInitialized() = ::lastSelected.isInitialized
|
||||
|
||||
fun export(data: Texture) {
|
||||
rootc.exportTexture(data)
|
||||
}
|
||||
|
||||
fun delete() {
|
||||
rootc.deleteTexture()
|
||||
}
|
||||
|
||||
fun previewSelectedAction(data: Texture) {
|
||||
rootc.showDetail(data)
|
||||
}
|
||||
|
||||
fun setSelected(model: GUIModel) {
|
||||
rootc.setSelectedTexture(model)
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@ package org.hso.texturesyncclient.view.importView
|
||||
import com.jfoenix.controls.JFXButton
|
||||
import com.jfoenix.controls.JFXChipView
|
||||
import com.jfoenix.controls.JFXTextField
|
||||
import javafx.collections.ListChangeListener
|
||||
import javafx.geometry.Pos
|
||||
import javafx.scene.layout.Background
|
||||
import javafx.geometry.Insets
|
||||
@ -11,24 +10,30 @@ import javafx.scene.layout.BackgroundFill
|
||||
import javafx.scene.layout.CornerRadii
|
||||
import javafx.scene.paint.Paint
|
||||
import javafx.scene.layout.Priority
|
||||
import org.hso.texturesyncclient.view.mainView.Preview3D
|
||||
import tornadofx.*
|
||||
|
||||
class ImportView : View() {
|
||||
class ImportView : View("TextureSync") {
|
||||
|
||||
val tfFilePath = JFXTextField()
|
||||
val tfName = JFXTextField()
|
||||
val cvTags = JFXChipView<String>()
|
||||
val btnImport = JFXButton("Importieren")
|
||||
|
||||
val preview = Preview3D()
|
||||
|
||||
private val btnBack = JFXButton("Zurück")
|
||||
|
||||
private val ivc: ImportViewController by inject()
|
||||
|
||||
init {
|
||||
btnImport.isVisible = false
|
||||
preview.root.isVisible = false
|
||||
}
|
||||
|
||||
override val root = borderpane {
|
||||
minWidth = 1000.0
|
||||
minHeight = 500.0
|
||||
prefWidth = FX.primaryStage.width
|
||||
prefHeight = FX.primaryStage.height
|
||||
background = Background(BackgroundFill(Paint.valueOf("#2b2b2b"), CornerRadii.EMPTY, Insets.EMPTY))
|
||||
|
||||
center = vbox(50) {
|
||||
@ -39,6 +44,8 @@ class ImportView : View() {
|
||||
style = "-fx-font: 20px Verdana; -fx-text-fill: #2b7bbb;"
|
||||
}
|
||||
|
||||
add(preview)
|
||||
|
||||
vbox(20) {
|
||||
hbox(10) {
|
||||
add(tfFilePath)
|
||||
@ -69,9 +76,8 @@ class ImportView : View() {
|
||||
vbox(5) {
|
||||
alignment = Pos.CENTER
|
||||
add(btnImport)
|
||||
add(btnBack)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,16 +89,17 @@ class ImportView : View() {
|
||||
tfName.style = "-fx-text-fill: #b15b2e;"
|
||||
tfName.promptText = "Name eingeben"
|
||||
|
||||
cvTags.style = "-fx-background-color: #3c3f41; -fx-text-inner-color: #b15b2e;"
|
||||
cvTags.style = "-fx-background-color: #53585b; -fx-text-inner-color: #b15b2e;"
|
||||
//TODO change color of Chip´s see: https://github.com/jfoenixadmin/JFoenix/blob/master/jfoenix/src/main/resources/com/jfoenix/assets/css/controls/jfx-chip-view.css#L52
|
||||
|
||||
btnImport.style = "-fx-button-type: RAISED; -fx-background-color: #3c3f41; -fx-text-fill: #2b7bbb;"
|
||||
btnImport.style = "-fx-button-type: RAISED; -fx-background-color: #b15b2e; -fx-text-fill: #3c3f41; -fx-font-size: 15;"
|
||||
btnBack.style = "-fx-button-type: RAISED; -fx-background-color: #3c3f41; -fx-text-fill: #2b7bbb; -fx-padding: 10;"
|
||||
}
|
||||
|
||||
tfFilePath.textProperty().addListener{ _, _, _ -> ivc.validateImport() }
|
||||
tfName.textProperty().addListener{ _, _, _ -> ivc.validateImport() }
|
||||
|
||||
cvTags.chips.addListener { change: ListChangeListener.Change<out String>? ->
|
||||
cvTags.chips.onChange {
|
||||
ivc.validateImport()
|
||||
}
|
||||
|
||||
@ -100,6 +107,10 @@ class ImportView : View() {
|
||||
ivc.btnImportAction()
|
||||
}
|
||||
|
||||
btnBack.setOnAction {
|
||||
ivc.btnBackAction()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -1,32 +1,71 @@
|
||||
package org.hso.texturesyncclient.view.importView
|
||||
|
||||
import javafx.stage.FileChooser.ExtensionFilter
|
||||
import javafx.scene.image.Image
|
||||
import javafx.stage.FileChooser
|
||||
import org.hso.texturesyncclient.controller.RootController
|
||||
import tornadofx.Controller
|
||||
import tornadofx.FileChooserMode
|
||||
import tornadofx.chooseFile
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
|
||||
|
||||
class ImportViewController : Controller() {
|
||||
|
||||
private val iv = find(ImportView::class)
|
||||
private val rootc = find(RootController::class)
|
||||
|
||||
fun btnFileChooserAction() {
|
||||
val list = listOf("*.png", "*.PNG", "*.jpg", "*.JPG", "*.jpeg", "*.JPEG")
|
||||
val arrayFilter = arrayOf(ExtensionFilter("Texturen vom Bildformat: PNG oder JPG", list))
|
||||
val file = chooseFile("Textur auswählen", arrayFilter, FileChooserMode.Single, owner = null)
|
||||
private var lastImportDir: String = System.getProperty("user.home")
|
||||
|
||||
if (file.isNotEmpty()) {
|
||||
iv.tfFilePath.text = file[0].absolutePath
|
||||
iv.tfName.text = file[0].nameWithoutExtension
|
||||
fun btnFileChooserAction() {
|
||||
val fileChooser = FileChooser()
|
||||
val fileExtensions = FileChooser.ExtensionFilter(
|
||||
"Texturen vom Bildformat: PNG oder JPEG", "*.png", "*.PNG", "*.jpg", "*.JPG", "*.jpeg", "*.JPEG"
|
||||
)
|
||||
|
||||
fileChooser.extensionFilters.addAll(fileExtensions)
|
||||
fileChooser.initialDirectory = File(lastImportDir)
|
||||
fileChooser.title = "Textur auswählen"
|
||||
|
||||
val file = fileChooser.showOpenDialog(null)
|
||||
|
||||
if (file != null) {
|
||||
iv.tfFilePath.text = file.absolutePath
|
||||
iv.tfName.text = file.nameWithoutExtension
|
||||
lastImportDir = file.parent.toString() //store last user chosen dir
|
||||
|
||||
runAsync {
|
||||
try {
|
||||
val fileInput = FileInputStream(file.absolutePath)
|
||||
val img = Image(fileInput)
|
||||
iv.preview.setTexture(img)
|
||||
iv.preview.root.isVisible = true
|
||||
} catch (e: Exception) {
|
||||
// Got a catch'em all
|
||||
println(e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun btnImportAction() {
|
||||
rootc.importTexture(iv.tfFilePath.text, iv.tfName.text, iv.cvTags.chips)
|
||||
RootController.switchImportToMain(true)
|
||||
reset()
|
||||
}
|
||||
|
||||
fun validateImport() {
|
||||
iv.btnImport.isVisible = iv.tfFilePath.text.isNotEmpty() && iv.tfName.text.isNotEmpty() && iv.cvTags.chips.isNotEmpty() && iv.cvTags.chips.stream().allMatch { x -> x.length < 32 }
|
||||
iv.btnImport.isVisible =
|
||||
iv.tfFilePath.text.isNotEmpty() && iv.tfName.text.isNotEmpty() && iv.cvTags.chips.isNotEmpty() && iv.cvTags.chips.stream().allMatch { x -> x.length < 32 }
|
||||
}
|
||||
|
||||
fun btnBackAction() {
|
||||
RootController.switchImportToMain(false)
|
||||
reset()
|
||||
}
|
||||
|
||||
private fun reset() {
|
||||
iv.tfFilePath.clear()
|
||||
iv.tfName.clear()
|
||||
iv.cvTags.chips.clear()
|
||||
iv.preview.root.isVisible = false
|
||||
}
|
||||
}
|
@ -1,9 +1,10 @@
|
||||
package org.hso.texturesyncclient.view.mainView
|
||||
|
||||
import com.jfoenix.controls.JFXButton
|
||||
import com.jfoenix.controls.JFXChipView
|
||||
import com.jfoenix.controls.JFXTextField
|
||||
import javafx.geometry.Insets
|
||||
import javafx.geometry.Orientation
|
||||
import javafx.scene.control.Label
|
||||
import javafx.scene.image.Image
|
||||
import javafx.scene.layout.Background
|
||||
import javafx.scene.layout.BackgroundFill
|
||||
@ -11,43 +12,69 @@ import javafx.scene.layout.CornerRadii
|
||||
import javafx.scene.paint.Paint
|
||||
import tornadofx.*
|
||||
|
||||
class DetailView: View() {
|
||||
class DetailView : View() {
|
||||
|
||||
val preview = Preview3D()
|
||||
val metaLabel = Label("Auflösung: 8MP\nName: Texture.png\nAndere: was anderes")
|
||||
val cvTags = JFXChipView<String>()
|
||||
|
||||
init {
|
||||
// set a default texture
|
||||
preview.setTexture(Image("textures/sample_texture_1.jpg"))
|
||||
val nameInfo = JFXTextField().addClass("metadata")
|
||||
val resolutionInfo = label().addClass("metadata")
|
||||
val formatInfo = label().addClass("metadata")
|
||||
val dateInfo = label().addClass("metadata")
|
||||
|
||||
val btnSubmit = JFXButton("Ändern").addClass("btn-blue")
|
||||
|
||||
val metadataPanel = gridpane {
|
||||
row {
|
||||
label("Name ").addClass("metadata")
|
||||
add(nameInfo)
|
||||
}
|
||||
row {
|
||||
label("Auflösung ").addClass("metadata")
|
||||
add(resolutionInfo)
|
||||
}
|
||||
row {
|
||||
label("Format ").addClass("metadata")
|
||||
add(formatInfo)
|
||||
}
|
||||
row {
|
||||
label("Einfügedatum ").addClass("metadata")
|
||||
add(dateInfo)
|
||||
}
|
||||
}
|
||||
|
||||
override val root = form {
|
||||
minWidth = 250.0
|
||||
background = Background(BackgroundFill(Paint.valueOf("#9f9f9f"), CornerRadii.EMPTY, Insets.EMPTY))
|
||||
background = Background(BackgroundFill(Paint.valueOf("#3a3a3a"), CornerRadii.EMPTY, Insets.EMPTY))
|
||||
|
||||
fieldset("DetailView", labelPosition = Orientation.VERTICAL) {
|
||||
fieldset(labelPosition = Orientation.VERTICAL) {
|
||||
|
||||
field("3D Preview") {
|
||||
field {
|
||||
vbox(7) {
|
||||
add(preview)
|
||||
}
|
||||
}
|
||||
|
||||
field("Meta") {
|
||||
add(metaLabel)
|
||||
field {
|
||||
add(metadataPanel)
|
||||
}
|
||||
|
||||
field("Tags") {
|
||||
field {
|
||||
minHeight = 155.0
|
||||
add(cvTags)
|
||||
}
|
||||
|
||||
// TODO add "Import" Btn
|
||||
}
|
||||
field {
|
||||
add(btnSubmit)
|
||||
}
|
||||
|
||||
style {
|
||||
cvTags.style = "-fx-background-color: #3c3f41; -fx-text-inner-color: #b15b2e;"
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
// set a default texture
|
||||
preview.setTexture(Image("icons/TextureSync_Icon_256x256.jpeg"))
|
||||
btnSubmit.useMaxWidth = true
|
||||
|
||||
}
|
||||
}
|
@ -10,14 +10,10 @@ import tornadofx.*
|
||||
class FolderView : View("FolderView"){
|
||||
|
||||
override val root = flowpane {
|
||||
hgap = 10.0
|
||||
vgap = 10.0
|
||||
paddingAll = 5.0
|
||||
prefWidth = 750.0
|
||||
background = Background(BackgroundFill(Paint.valueOf("#cfcfcf"), CornerRadii.EMPTY, Insets.EMPTY))
|
||||
|
||||
style {
|
||||
|
||||
}
|
||||
hgap = 5.0
|
||||
vgap = 5.0
|
||||
paddingAll = 10.0
|
||||
background = Background(BackgroundFill(Paint.valueOf("#2b2b2b"), CornerRadii.EMPTY, Insets.EMPTY))
|
||||
}
|
||||
|
||||
}
|
@ -1,41 +1,103 @@
|
||||
package org.hso.texturesyncclient.view.mainView
|
||||
|
||||
import javafx.collections.ListChangeListener
|
||||
import javafx.scene.image.Image
|
||||
import com.jfoenix.controls.JFXButton
|
||||
import com.jfoenix.controls.JFXChipView
|
||||
import javafx.geometry.Insets
|
||||
|
||||
import javafx.scene.layout.Background
|
||||
import javafx.scene.layout.BackgroundFill
|
||||
import javafx.scene.layout.CornerRadii
|
||||
import javafx.scene.paint.Paint
|
||||
import tornadofx.*
|
||||
|
||||
class MainView : View() {
|
||||
class MainView : View("TextureSync") {
|
||||
|
||||
val cvSearch = JFXChipView<String>()
|
||||
private val btnImport = JFXButton("+")
|
||||
val folderView = find(FolderView::class)
|
||||
val detailView = find(DetailView::class)
|
||||
|
||||
private val mvc: MainViewController by inject()
|
||||
|
||||
override val root = borderpane {
|
||||
minWidth = 1000.0
|
||||
minHeight = 500.0
|
||||
fun repeatSearch() {
|
||||
mvc.cvSearchAction(cvSearch.chips)
|
||||
}
|
||||
|
||||
left = folderView.root
|
||||
right = detailView.root
|
||||
override val root = anchorpane {
|
||||
|
||||
background = Background(BackgroundFill(Paint.valueOf("#2b2b2b"), CornerRadii.EMPTY, Insets.EMPTY))
|
||||
prefWidth = FX.primaryStage.width
|
||||
prefHeight = FX.primaryStage.height
|
||||
|
||||
borderpane {
|
||||
right = detailView.root
|
||||
center = vbox {
|
||||
add(cvSearch)
|
||||
scrollpane {
|
||||
style = "-fx-background-color:transparent;"
|
||||
isFitToWidth = true
|
||||
isFitToHeight = true
|
||||
|
||||
add(folderView.root)
|
||||
}
|
||||
}
|
||||
|
||||
anchorpaneConstraints {
|
||||
topAnchor = 0
|
||||
bottomAnchor = 0
|
||||
rightAnchor = 0
|
||||
leftAnchor = 0
|
||||
}
|
||||
}
|
||||
|
||||
add(btnImport)
|
||||
|
||||
style {
|
||||
// style options
|
||||
cvSearch.promptText = "Suche"
|
||||
cvSearch.paddingAll = 5.0
|
||||
cvSearch.minHeight = 65.0
|
||||
cvSearch.style = "-fx-background-color: #53585b; -fx-text-inner-color: #b15b2e;"
|
||||
|
||||
btnImport.buttonType = JFXButton.ButtonType.RAISED
|
||||
btnImport.styleClass.add("jfx-floating-action-button")
|
||||
btnImport.anchorpaneConstraints {
|
||||
bottomAnchor = 5
|
||||
rightAnchor = 5
|
||||
}
|
||||
}
|
||||
|
||||
// actions
|
||||
// folderView.btn1.setOnAction {
|
||||
// mvc.setPreview3DTexture(Image("textures/sample_texture_1.jpg"))
|
||||
// mvc.setMeta("texture 1", "8MP", "Quelle: wikipedia")
|
||||
// mvc.setTags(observableList("Stein", "Rot", "super"))
|
||||
// }
|
||||
|
||||
detailView.cvTags.chips.addListener { change: ListChangeListener.Change<out String>? ->
|
||||
mvc.updateTags()
|
||||
cvSearch.chips.onChange {
|
||||
mvc.cvSearchAction(cvSearch.chips)
|
||||
}
|
||||
|
||||
btnImport.setOnAction {
|
||||
mvc.btnImportAction()
|
||||
}
|
||||
|
||||
// TODO: on chipview update on name update
|
||||
|
||||
detailView.cvTags.chips.onChange {
|
||||
detailView.btnSubmit.isVisible = true
|
||||
}
|
||||
|
||||
detailView.nameInfo.textProperty().onChange {
|
||||
detailView.btnSubmit.isVisible = true
|
||||
}
|
||||
|
||||
detailView.btnSubmit.setOnAction {
|
||||
mvc.updateTags()
|
||||
detailView.btnSubmit.isVisible = false
|
||||
}
|
||||
|
||||
//keyboard actions
|
||||
shortcut("Ctrl+I") {
|
||||
mvc.btnImportAction()
|
||||
}
|
||||
|
||||
shortcut("Ctrl+E") {
|
||||
mvc.scExport()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
@ -2,82 +2,105 @@ package org.hso.texturesyncclient.view.mainView
|
||||
|
||||
import javafx.collections.ObservableList
|
||||
import javafx.scene.image.Image
|
||||
import org.hso.texturesyncclient.controller.RootController
|
||||
import org.hso.texturesyncclient.model.GUIModel
|
||||
import org.hso.texturesyncclient.model.TextureFormat
|
||||
import org.hso.texturesyncclient.model.Texture
|
||||
import tornadofx.Controller
|
||||
import javafx.stage.DirectoryChooser
|
||||
import javax.swing.JColorChooser.showDialog
|
||||
import java.io.File
|
||||
|
||||
|
||||
class MainViewController : Controller() {
|
||||
|
||||
private val mv = find(MainView::class)
|
||||
private val rootc = find(RootController::class)
|
||||
|
||||
// FolderView elements
|
||||
private val folderView = mv.folderView.root
|
||||
|
||||
// DetailView elements
|
||||
private val preview = mv.detailView.preview
|
||||
private val metaLabel = mv.detailView.metaLabel
|
||||
private val cvTags = mv.detailView.cvTags
|
||||
|
||||
private var lockUpdate: Boolean = false //lock update func when the system changes the detailview chipview
|
||||
|
||||
// FolderView functions
|
||||
fun addElement(element: GUIModel) {
|
||||
folderView.children.add(element)
|
||||
}
|
||||
|
||||
fun addAllElements(elementList: List<GUIModel>) {
|
||||
folderView.children.addAll(elementList)
|
||||
}
|
||||
|
||||
|
||||
// DetailView functions
|
||||
fun setPreview3DTexture(img: Image) {
|
||||
preview.setTexture(img)
|
||||
}
|
||||
|
||||
fun setMeta(name: String, res: String, etc: String) {
|
||||
metaLabel.text = "Name: $name\nAuflösung: $res\nAnderes: $etc"
|
||||
fun setMeta(name: String, res: String, format: String, date: String) {
|
||||
with(mv.detailView) {
|
||||
nameInfo.text = name
|
||||
formatInfo.text = format
|
||||
resolutionInfo.text = res
|
||||
dateInfo.text = date
|
||||
}
|
||||
}
|
||||
|
||||
fun setTags(chips: ObservableList<String>) {
|
||||
lockUpdate = false //dont trigger update with onChange
|
||||
cvTags.chips.clear()
|
||||
cvTags.chips.addAll(chips)
|
||||
lockUpdate = true //allow update with onChange
|
||||
}
|
||||
|
||||
fun getTags(): ObservableList<String> {
|
||||
return cvTags.chips
|
||||
}
|
||||
|
||||
// update the tags for the selected element
|
||||
fun updateTags() {
|
||||
println(cvTags.chips)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* save the texture file to r
|
||||
* @param data the file as a byte array
|
||||
* @param name name for the file
|
||||
* @param format specific file format. jpeg or png
|
||||
*/
|
||||
fun exportTexture(data: ByteArray, name: String, format : TextureFormat){
|
||||
|
||||
|
||||
val directoryChooser = DirectoryChooser()
|
||||
|
||||
directoryChooser.title = "Export Verzeichnis wählen"
|
||||
|
||||
// TODO directoryChooser.setInitialDirectory(new File(System.getProperty("user.home")))
|
||||
|
||||
val dir = directoryChooser.showDialog(primaryStage)
|
||||
|
||||
if (dir != null) {
|
||||
|
||||
//copy data (bytesarray) with name and extension to dir
|
||||
|
||||
if (lockUpdate) { //the chipView was changed by the user
|
||||
println("Tags changed")
|
||||
rootc.updateTexture(
|
||||
tags = cvTags.chips.toTypedArray(),
|
||||
name = mv.detailView.nameInfo.text
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// MainView actions
|
||||
|
||||
fun cvSearchAction(tags: ObservableList<String>) {
|
||||
// show spinner, block ui
|
||||
folderView.children.clear()
|
||||
mv.cvSearch.isDisable = true
|
||||
setPreview3DTexture(Image("icons/TextureSync_Icon_256x256.jpeg")) // reset the 3DPreview to the logo
|
||||
RootController.selectedTexture = null
|
||||
|
||||
runAsync {
|
||||
rootc.queryElements(tags)
|
||||
} ui {
|
||||
// when search finished
|
||||
mv.cvSearch.isDisable = false
|
||||
}
|
||||
}
|
||||
|
||||
fun btnImportAction() {
|
||||
RootController.switchMainToImport()
|
||||
}
|
||||
|
||||
fun removeTextureFromView(data: Texture) {
|
||||
// stream all children nodes, filter them as GUIModel with data.id == data.id, for any found object if it's still present remove it from the folderView
|
||||
folderView.children.stream().filter { x -> (x as GUIModel).data.id == data.id }.findAny()
|
||||
.ifPresent { x -> folderView.children.remove(x) }
|
||||
}
|
||||
|
||||
fun setVisibleMetaTags(bool: Boolean) {
|
||||
if (bool) {
|
||||
mv.detailView.metadataPanel.isVisible = true
|
||||
mv.detailView.btnSubmit.isVisible = false
|
||||
cvTags.isVisible = true
|
||||
} else {
|
||||
mv.detailView.metadataPanel.isVisible = false
|
||||
mv.detailView.btnSubmit.isVisible = false
|
||||
cvTags.isVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
fun scExport() {
|
||||
RootController.selectedTexture?.let { rootc.exportTexture(RootController.selectedTexture!!) }
|
||||
}
|
||||
|
||||
}
|
@ -36,7 +36,6 @@ class Preview3D : View("Preview3D") {
|
||||
pointLightFront.rotate = 90.0
|
||||
}
|
||||
|
||||
|
||||
override val root = stackpane {
|
||||
|
||||
add(dBox).apply {
|
||||
@ -56,7 +55,7 @@ class Preview3D : View("Preview3D") {
|
||||
style {
|
||||
minWidth = 200.px
|
||||
minHeight = 200.px
|
||||
background = Background(BackgroundFill(Color.BLUEVIOLET, CornerRadii.EMPTY, Insets.EMPTY))
|
||||
background = Background(BackgroundFill(Color.valueOf("#3a3a3a"), CornerRadii.EMPTY, Insets.EMPTY))
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ import javafx.scene.layout.CornerRadii
|
||||
import javafx.scene.paint.Paint
|
||||
import tornadofx.*
|
||||
|
||||
class StartupView : View("StartupView") {
|
||||
class StartupView : View("TextureSync") {
|
||||
|
||||
val labelStatus = Label("Verbindung zum Server einrichten")
|
||||
val spinnerStatus = JFXSpinner()
|
||||
@ -22,6 +22,7 @@ class StartupView : View("StartupView") {
|
||||
|
||||
private val svc: StartupViewController by inject()
|
||||
|
||||
|
||||
override val root = borderpane {
|
||||
minWidth = 1000.0
|
||||
minHeight = 500.0
|
||||
|
@ -1,6 +1,5 @@
|
||||
package org.hso.texturesyncclient.view.startupView
|
||||
|
||||
import kotlinx.coroutines.withTimeout
|
||||
import org.hso.texturesyncclient.controller.RootController
|
||||
import tornadofx.Controller
|
||||
|
||||
@ -10,24 +9,54 @@ class StartupViewController : Controller() {
|
||||
private val sv = find(StartupView::class)
|
||||
private val rootc = find(RootController::class)
|
||||
|
||||
fun initConnection() {
|
||||
println("init StartupViewController")
|
||||
startConnectionUI()
|
||||
runAsync {
|
||||
rootc.initConnection(" ")
|
||||
} ui {
|
||||
// reset for later use
|
||||
endConnectionUI()
|
||||
}
|
||||
}
|
||||
|
||||
fun setServerAddress(address: String) {
|
||||
//sv.tfServerIP.text = address
|
||||
sv.tfServerIP.isFocusTraversable = false
|
||||
}
|
||||
|
||||
fun btnConnectAction(name: String) {
|
||||
startConnectionUI()
|
||||
runAsync {
|
||||
rootc.initConnection(name)
|
||||
} ui {
|
||||
// reset for later use
|
||||
endConnectionUI()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* show spinner and block textfied + button and set label
|
||||
*/
|
||||
private fun startConnectionUI() {
|
||||
sv.labelStatus.text = "Verbinden ..."
|
||||
sv.tfServerIP.isEditable = false
|
||||
sv.btnConnect.isDisable = true
|
||||
sv.spinnerStatus.isVisible = true
|
||||
|
||||
runAsync() {
|
||||
rootc.initConnection(name)
|
||||
} ui {
|
||||
// reset for later use
|
||||
sv.spinnerStatus.isVisible = false
|
||||
sv.labelStatus.text = "Verbindung zum Server einrichten"
|
||||
sv.tfServerIP.isEditable = true
|
||||
sv.btnConnect.isDisable = false
|
||||
sv.tfServerIP.clear()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* remove spinner and unblock textfied + button and set label
|
||||
*/
|
||||
private fun endConnectionUI() {
|
||||
sv.spinnerStatus.isVisible = false
|
||||
sv.labelStatus.text = "Verbindung zum Server einrichten"
|
||||
sv.tfServerIP.isEditable = true
|
||||
sv.btnConnect.isDisable = false
|
||||
sv.tfServerIP.clear()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
145
client/src/main/resources/css/Styles.css
Normal file
@ -0,0 +1,145 @@
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* Scroll Bar *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
.scroll-bar:vertical > .track-background, .scroll-bar:horizontal > .track-background {
|
||||
-fx-background-color: #2b2b2b;
|
||||
-fx-background-insets: 0.0;
|
||||
}
|
||||
|
||||
.scroll-bar:vertical > .thumb, .scroll-bar:horizontal > .thumb {
|
||||
-fx-background-color: #53585b;
|
||||
-fx-background-insets: 0.0;
|
||||
-fx-background-radius: 1.0;
|
||||
}
|
||||
|
||||
/* Up- and Down-Button Padding */
|
||||
.scroll-bar:vertical > .increment-button, .scroll-bar:vertical > .decrement-button {
|
||||
-fx-padding: 5 2 5 2;
|
||||
}
|
||||
|
||||
/* Left- and Right-Button Padding */
|
||||
.scroll-bar:horizontal > .increment-button, .scroll-bar:horizontal > .decrement-button {
|
||||
-fx-padding: 2 5 2 5;
|
||||
}
|
||||
|
||||
.scroll-bar > .increment-button, .scroll-bar > .decrement-button, .scroll-bar:hover > .increment-button, .scroll-bar:hover > .decrement-button {
|
||||
-fx-background-color: transparent;
|
||||
}
|
||||
|
||||
.scroll-bar > .increment-button > .increment-arrow, .scroll-bar > .decrement-button > .decrement-arrow {
|
||||
-fx-background-color: rgb(150.0, 150.0, 150.0);
|
||||
}
|
||||
|
||||
/* Up Arrow */
|
||||
.scroll-bar:vertical > .increment-button > .increment-arrow {
|
||||
-fx-shape: "M298 426h428l-214 214z";
|
||||
}
|
||||
|
||||
/* Down Arrow */
|
||||
.scroll-bar:vertical > .decrement-button > .decrement-arrow {
|
||||
-fx-shape: "M298 598l214-214 214 214h-428z";
|
||||
}
|
||||
|
||||
/* Right Arrow */
|
||||
.scroll-bar:horizontal > .increment-button > .increment-arrow {
|
||||
-fx-shape: "M0 428l0 -428l214 214l-214 214z";
|
||||
}
|
||||
|
||||
/* Left Arrow */
|
||||
.scroll-bar:horizontal > .decrement-button > .decrement-arrow {
|
||||
-fx-shape: "M214 0l0 428l-214 -214l214 -214z";
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* Scroll Pane *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
.scroll-pane {
|
||||
-fx-background-insets: 0;
|
||||
-fx-padding: 0;
|
||||
}
|
||||
|
||||
.scroll-pane:focused {
|
||||
-fx-background-insets: 0;
|
||||
}
|
||||
|
||||
.scroll-pane .corner {
|
||||
-fx-background-insets: 0;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* Import Button *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
.jfx-floating-action-button {
|
||||
-fx-background-color: #F1F1F1;
|
||||
-fx-background-radius: 50px;
|
||||
-fx-pref-height: 50px;
|
||||
-fx-pref-width: 50px;
|
||||
-fx-min-width: -fx-pref-width;
|
||||
-fx-max-width: -fx-pref-width;
|
||||
-fx-min-height: -fx-pref-height;
|
||||
-fx-max-height: -fx-pref-height;
|
||||
-jfx-button-type: RAISED;
|
||||
|
||||
-fx-text-inner-color: #2b2b2b;
|
||||
-fx-font-size: 20px;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* ChipView *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
.jfx-chip-view .jfx-chip > HBox {
|
||||
-fx-font-family: "Roboto Medium";
|
||||
-fx-font-size: 14.0;
|
||||
-fx-background-color: #E0E0E0;
|
||||
-fx-background-radius: 40px;
|
||||
-fx-padding: 5px 8px 5px 12px;
|
||||
-fx-pref-height: 32px;
|
||||
-fx-alignment: center-left;
|
||||
-fx-spacing: 8;
|
||||
}
|
||||
|
||||
.jfx-chip-view {
|
||||
-fx-text-inner-color: #E0E0E0;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* Buttons *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
.btn-blue {
|
||||
-fx-button-type: RAISED;
|
||||
-fx-background-color: #3c3f41;
|
||||
-fx-text-fill: #2b7bbb;
|
||||
-fx-padding: 10;
|
||||
}
|
||||
|
||||
.btn-orange {
|
||||
-fx-button-type: RAISED;
|
||||
-fx-background-color: #b15b2e;
|
||||
-fx-text-fill: #3c3f41;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* *
|
||||
* DetailView *
|
||||
* *
|
||||
******************************************************************************/
|
||||
|
||||
.metadata {
|
||||
-fx-font: 14px Verdana;
|
||||
-fx-text-fill: #E0E0E0;
|
||||
}
|
1
client/src/main/resources/icons/TextureSync_Icon.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg width="640" height="640" xmlns="http://www.w3.org/2000/svg"><defs><radialGradient id="a" cx="0.2" cy="-0.5" r="1.7"><stop offset="40%" stop-color="#8c8c8c"/><stop offset="100%" stop-color="#4c4c4c"/></radialGradient><radialGradient id="b" cx="0.2" cy="0.2" fx="0.34" fy="0.32" r="0.9"><stop offset="0%" stop-color="#fbf6f0"/><stop offset="50%" stop-color="#eadec4"/><stop offset="100%" stop-color="#aba390"/></radialGradient><radialGradient id="c" cx="0.2" cy="0.2" fx="0.34" fy="0.32" r="0.9"><stop offset="5%" stop-color="#fbf1cd"/><stop offset="50%" stop-color="#ffba00"/><stop offset="100%" stop-color="#bc8602"/></radialGradient></defs><path d="M158.58 102.89C144.48 110.79 143.14 130.93 155.49 141.35C169.62 153.27 184.17 164.75 199.13 175.69C138.27 240.95 105.0 337.14 105.0 426.0C105.0 525.49 179.5 590.92 278.99 591.0C342.5 591.05 406.69 553.62 449.59 506.78C501.0 450.65 531.88 370.14 532.0 294.02C532.06 254.61 524.06 213.61 505.46 178.86C487.92 146.11 450.51 118.09 413.38 117.01C388.12 116.27 362.24 125.41 340.98 139.06C331.11 145.4 321.78 152.65 313.04 160.6C313.88 131.47 312.82 102.25 309.97 73.31C308.35 56.96 293.5 46.31 277.86 51.34C236.62 64.58 196.37 81.72 158.58 102.89Z" fill="#000"/><path d="M165.42 115.11C158.26 119.12 158.24 125.36 164.51 130.65C201.33 161.71 241.11 189.73 283.36 212.86C290.09 216.54 295.26 213.92 296.04 206.29C300.48 162.65 300.34 118.34 296.03 74.69C295.17 65.95 290.5 61.98 282.14 64.66C241.79 77.63 202.4 94.39 165.42 115.11Z" fill="url(#a)"/><path d="M220.6 190.82C238.75 203.14 257.44 214.63 276.64 225.14C282.11 228.13 288.25 228.46 293.76 226.78L496.27 366.27C484.19 411.93 461.33 455.47 430.41 489.22C397.54 525.11 350.51 555.58 301.99 563.18L136.05 367.03C147.1 301.77 176.06 237.27 220.6 190.82Z" fill="url(#b)"/><path d="M293.76 226.78C302.1 224.23 309.01 217.1 309.96 207.71C310.21 205.29 310.44 202.86 310.66 200.43C323.57 185.24 338.43 171.6 355.02 160.94C371.91 150.09 392.56 142.41 412.62 142.99C440.73 143.82 469.27 166.35 482.54 191.14C499.13 222.12 506.06 258.84 506.0 293.98C505.96 317.93 502.59 342.39 496.27 366.27L330.0 369.0L301.99 563.18C294.35 564.38 286.68 565.01 279.01 565.0C192.51 564.93 131.0 512.5 131.0 426.0C131.0 406.59 132.7 386.78 136.05 367.03L290.0 332.0L293.76 226.78Z" fill="url(#c)"/></svg>
|
After Width: | Height: | Size: 2.2 KiB |
BIN
client/src/main/resources/icons/TextureSync_Icon_256x256.jpeg
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
client/src/main/resources/icons/TextureSync_Icon_256x256.png
Normal file
After Width: | Height: | Size: 17 KiB |
@ -1,24 +1,24 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<office:document xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0" xmlns:style="urn:oasis:names:tc:opendocument:xmlns:style:1.0" xmlns:text="urn:oasis:names:tc:opendocument:xmlns:text:1.0" xmlns:table="urn:oasis:names:tc:opendocument:xmlns:table:1.0" xmlns:draw="urn:oasis:names:tc:opendocument:xmlns:drawing:1.0" xmlns:fo="urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:meta="urn:oasis:names:tc:opendocument:xmlns:meta:1.0" xmlns:number="urn:oasis:names:tc:opendocument:xmlns:datastyle:1.0" xmlns:svg="urn:oasis:names:tc:opendocument:xmlns:svg-compatible:1.0" xmlns:chart="urn:oasis:names:tc:opendocument:xmlns:chart:1.0" xmlns:dr3d="urn:oasis:names:tc:opendocument:xmlns:dr3d:1.0" xmlns:math="http://www.w3.org/1998/Math/MathML" xmlns:form="urn:oasis:names:tc:opendocument:xmlns:form:1.0" xmlns:script="urn:oasis:names:tc:opendocument:xmlns:script:1.0" xmlns:config="urn:oasis:names:tc:opendocument:xmlns:config:1.0" xmlns:ooo="http://openoffice.org/2004/office" xmlns:ooow="http://openoffice.org/2004/writer" xmlns:oooc="http://openoffice.org/2004/calc" xmlns:dom="http://www.w3.org/2001/xml-events" xmlns:xforms="http://www.w3.org/2002/xforms" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rpt="http://openoffice.org/2005/report" xmlns:of="urn:oasis:names:tc:opendocument:xmlns:of:1.2" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:grddl="http://www.w3.org/2003/g/data-view#" xmlns:officeooo="http://openoffice.org/2009/office" xmlns:tableooo="http://openoffice.org/2009/table" xmlns:drawooo="http://openoffice.org/2010/draw" xmlns:calcext="urn:org:documentfoundation:names:experimental:calc:xmlns:calcext:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:field="urn:openoffice:names:experimental:ooo-ms-interop:xmlns:field:1.0" xmlns:formx="urn:openoffice:names:experimental:ooxml-odf-interop:xmlns:form:1.0" xmlns:css3t="http://www.w3.org/TR/css3-text/" office:version="1.2" office:mimetype="application/vnd.oasis.opendocument.text">
|
||||
<office:meta><meta:creation-date>2019-03-22T18:11:56.768840842</meta:creation-date><dc:date>2019-03-29T13:15:18.356408709</dc:date><meta:editing-duration>PT3H5M45S</meta:editing-duration><meta:editing-cycles>98</meta:editing-cycles><meta:generator>LibreOffice/6.2.2.1$Linux_X86_64 LibreOffice_project/20$Build-1</meta:generator><dc:title>Charter</dc:title><meta:document-statistic meta:table-count="5" meta:image-count="0" meta:object-count="0" meta:page-count="3" meta:paragraph-count="86" meta:word-count="244" meta:character-count="1896" meta:non-whitespace-character-count="1746"/><meta:user-defined meta:name="Version">1.2.0</meta:user-defined></office:meta>
|
||||
<office:meta><meta:creation-date>2019-03-22T18:11:56.768840842</meta:creation-date><dc:date>2019-06-14T18:27:49.303971292</dc:date><meta:editing-duration>PT3H6M</meta:editing-duration><meta:editing-cycles>99</meta:editing-cycles><meta:generator>LibreOffice/6.2.3.2$Linux_X86_64 LibreOffice_project/20$Build-2</meta:generator><dc:title>Charter</dc:title><meta:document-statistic meta:table-count="5" meta:image-count="0" meta:object-count="0" meta:page-count="3" meta:paragraph-count="86" meta:word-count="244" meta:character-count="1896" meta:non-whitespace-character-count="1746"/><meta:user-defined meta:name="Version">1.2.0</meta:user-defined></office:meta>
|
||||
<office:settings>
|
||||
<config:config-item-set config:name="ooo:view-settings">
|
||||
<config:config-item config:name="ViewAreaTop" config:type="long">38276</config:config-item>
|
||||
<config:config-item config:name="ViewAreaTop" config:type="long">67733</config:config-item>
|
||||
<config:config-item config:name="ViewAreaLeft" config:type="long">0</config:config-item>
|
||||
<config:config-item config:name="ViewAreaWidth" config:type="long">40924</config:config-item>
|
||||
<config:config-item config:name="ViewAreaHeight" config:type="long">17179</config:config-item>
|
||||
<config:config-item config:name="ViewAreaWidth" config:type="long">39160</config:config-item>
|
||||
<config:config-item config:name="ViewAreaHeight" config:type="long">17244</config:config-item>
|
||||
<config:config-item config:name="ShowRedlineChanges" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="InBrowseMode" config:type="boolean">false</config:config-item>
|
||||
<config:config-item-map-indexed config:name="Views">
|
||||
<config:config-item-map-entry>
|
||||
<config:config-item config:name="ViewId" config:type="string">view2</config:config-item>
|
||||
<config:config-item config:name="ViewLeft" config:type="long">18648</config:config-item>
|
||||
<config:config-item config:name="ViewTop" config:type="long">50045</config:config-item>
|
||||
<config:config-item config:name="ViewLeft" config:type="long">11077</config:config-item>
|
||||
<config:config-item config:name="ViewTop" config:type="long">74761</config:config-item>
|
||||
<config:config-item config:name="VisibleLeft" config:type="long">0</config:config-item>
|
||||
<config:config-item config:name="VisibleTop" config:type="long">38276</config:config-item>
|
||||
<config:config-item config:name="VisibleRight" config:type="long">40922</config:config-item>
|
||||
<config:config-item config:name="VisibleBottom" config:type="long">55453</config:config-item>
|
||||
<config:config-item config:name="VisibleTop" config:type="long">67733</config:config-item>
|
||||
<config:config-item config:name="VisibleRight" config:type="long">39158</config:config-item>
|
||||
<config:config-item config:name="VisibleBottom" config:type="long">84975</config:config-item>
|
||||
<config:config-item config:name="ZoomType" config:type="short">0</config:config-item>
|
||||
<config:config-item config:name="ViewLayoutColumns" config:type="short">1</config:config-item>
|
||||
<config:config-item config:name="ViewLayoutBookMode" config:type="boolean">false</config:config-item>
|
||||
@ -29,98 +29,98 @@
|
||||
</config:config-item-map-indexed>
|
||||
</config:config-item-set>
|
||||
<config:config-item-set config:name="ooo:configuration-settings">
|
||||
<config:config-item config:name="PrintPaperFromSetup" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintFaxName" config:type="string"/>
|
||||
<config:config-item config:name="PrintSingleJobs" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintProspectRTL" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintProspect" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintReversed" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintTextPlaceholder" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintTables" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PrintPageBackground" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PrintLeftPages" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PrintAnnotationMode" config:type="short">0</config:config-item>
|
||||
<config:config-item config:name="PrintPageBackground" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PrintControls" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PrintHiddenText" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintDrawings" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="IgnoreFirstLineIndentInNumbering" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrinterSetup" config:type="base64Binary"/>
|
||||
<config:config-item config:name="PrintAnnotationMode" config:type="short">0</config:config-item>
|
||||
<config:config-item config:name="PrintGraphics" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PrintRightPages" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PrintFaxName" config:type="string"/>
|
||||
<config:config-item config:name="PrintPaperFromSetup" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintTextPlaceholder" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="EmptyDbFieldHidesPara" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="ApplyParagraphMarkFormatToNumbering" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintReversed" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="TabOverMargin" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="EmbedAsianScriptFonts" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="EmbedLatinScriptFonts" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="DisableOffPagePositioning" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="EmbedOnlyUsedFonts" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="EmbedFonts" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="SurroundTextWrapSmall" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="BackgroundParaOverDrawings" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="ClippedPictures" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="FloattableNomargins" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="UnbreakableNumberings" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="EmbedSystemFonts" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="TabOverflow" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PrintTables" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PrintSingleJobs" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="SmallCapsPercentage66" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="CollapseEmptyCellPara" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="UseOldPrinterMetrics" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="UseOldNumbering" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="AddExternalLeading" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="TreatSingleColumnBreakAsPageBreak" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="IsLabelDocument" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="RsidRoot" config:type="int">192011</config:config-item>
|
||||
<config:config-item config:name="ConsiderTextWrapOnObjPos" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="TableRowKeep" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="TabsRelativeToIndent" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="UpdateFromTemplate" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="SaveVersionOnClose" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="UseFormerTextWrapping" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="ChartAutoUpdate" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="AddParaTableSpacingAtStart" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="AllowPrintJobCancel" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="AddParaTableSpacing" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="AddParaSpacingToTableCells" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="UseFormerLineSpacing" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="IsLabelDocument" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrinterName" config:type="string"/>
|
||||
<config:config-item config:name="OutlineLevelYieldsNumbering" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="UpdateFromTemplate" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PrintBlackFonts" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="TableRowKeep" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="IgnoreTabsAndBlanksForLineCalculation" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="EmbedComplexScriptFonts" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="UseOldPrinterMetrics" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="InvertBorderSpacing" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="SaveGlobalDocumentLinks" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="TabsRelativeToIndent" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="Rsid" config:type="int">7352283</config:config-item>
|
||||
<config:config-item config:name="EmbeddedDatabaseName" config:type="string"/>
|
||||
<config:config-item config:name="SaveThumbnail" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PrintProspectRTL" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintEmptyPages" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="ApplyUserData" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PrintHiddenText" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="AddParaTableSpacingAtStart" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="FieldAutoUpdate" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="UseOldNumbering" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="AddParaTableSpacing" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="MsWordCompTrailingBlanks" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="IgnoreFirstLineIndentInNumbering" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrinterPaperFromSetup" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="CharacterCompressionType" config:type="short">0</config:config-item>
|
||||
<config:config-item config:name="SaveVersionOnClose" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="ChartAutoUpdate" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PrinterIndependentLayout" config:type="string">high-resolution</config:config-item>
|
||||
<config:config-item config:name="IsKernAsianPunctuation" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="UseFormerObjectPositioning" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="TreatSingleColumnBreakAsPageBreak" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="MathBaselineAlignment" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="AddFrameOffsets" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="AddVerticalFrameOffsets" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="SubtractFlysAnchoredAtFlys" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="AddParaSpacingToTableCells" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="AddExternalLeading" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="CurrentDatabaseDataSource" config:type="string"/>
|
||||
<config:config-item config:name="AllowPrintJobCancel" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="ProtectForm" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="UseFormerLineSpacing" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintDrawings" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="UseFormerTextWrapping" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="UnxForceZeroExtLeading" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="TabAtLeftIndentForParagraphsInList" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="RedlineProtectionKey" config:type="base64Binary"/>
|
||||
<config:config-item config:name="PropLineSpacingShrinksFirstLine" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="ConsiderTextWrapOnObjPos" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="StylesNoDefault" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="LinkUpdateMode" config:type="short">1</config:config-item>
|
||||
<config:config-item config:name="AlignTabStopPosition" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="DoNotJustifyLinesWithManualBreak" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="EmbedOnlyUsedFonts" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="LinkUpdateMode" config:type="short">1</config:config-item>
|
||||
<config:config-item config:name="CurrentDatabaseCommandType" config:type="int">0</config:config-item>
|
||||
<config:config-item config:name="CurrentDatabaseCommand" config:type="string"/>
|
||||
<config:config-item config:name="CharacterCompressionType" config:type="short">0</config:config-item>
|
||||
<config:config-item config:name="SmallCapsPercentage66" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="ApplyUserData" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="StylesNoDefault" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="EmbeddedDatabaseName" config:type="string"/>
|
||||
<config:config-item config:name="FloattableNomargins" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="BackgroundParaOverDrawings" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrinterName" config:type="string"/>
|
||||
<config:config-item config:name="UseFormerObjectPositioning" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="TabOverMargin" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="SaveGlobalDocumentLinks" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="CurrentDatabaseDataSource" config:type="string"/>
|
||||
<config:config-item config:name="IsKernAsianPunctuation" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="SaveThumbnail" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PrinterPaperFromSetup" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrinterIndependentLayout" config:type="string">high-resolution</config:config-item>
|
||||
<config:config-item config:name="TabOverflow" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PrintGraphics" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="PropLineSpacingShrinksFirstLine" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="DoNotResetParaAttrsForNumFont" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="FieldAutoUpdate" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="IgnoreTabsAndBlanksForLineCalculation" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="RedlineProtectionKey" config:type="base64Binary"/>
|
||||
<config:config-item config:name="EmbedComplexScriptFonts" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="CurrentDatabaseCommandType" config:type="int">0</config:config-item>
|
||||
<config:config-item config:name="LoadReadonly" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="DoNotCaptureDrawObjsOnPage" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="CurrentDatabaseCommand" config:type="string"/>
|
||||
<config:config-item config:name="PrinterSetup" config:type="base64Binary"/>
|
||||
<config:config-item config:name="ClipAsCharacterAnchoredWriterFlyFrames" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintBlackFonts" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="DisableOffPagePositioning" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="SurroundTextWrapSmall" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="UnxForceZeroExtLeading" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="EmbedAsianScriptFonts" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="TabAtLeftIndentForParagraphsInList" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintRightPages" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="Rsid" config:type="int">7336954</config:config-item>
|
||||
<config:config-item config:name="MathBaselineAlignment" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="MsWordCompTrailingBlanks" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="InvertBorderSpacing" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="EmbedFonts" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="UnbreakableNumberings" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="AddFrameOffsets" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="ClippedPictures" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="EmbedLatinScriptFonts" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="EmbedSystemFonts" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="ApplyParagraphMarkFormatToNumbering" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="SubtractFlysAnchoredAtFlys" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="EmptyDbFieldHidesPara" config:type="boolean">true</config:config-item>
|
||||
<config:config-item config:name="ProtectForm" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="AddVerticalFrameOffsets" config:type="boolean">false</config:config-item>
|
||||
<config:config-item config:name="PrintEmptyPages" config:type="boolean">false</config:config-item>
|
||||
</config:config-item-set>
|
||||
</office:settings>
|
||||
<office:scripts>
|
||||
@ -122663,13 +122663,16 @@
|
||||
<style:style style:name="P35" style:family="paragraph" style:parent-style-name="Table_20_Contents" style:list-style-name="L3">
|
||||
<style:text-properties officeooo:rsid="006f78fd" officeooo:paragraph-rsid="006f78fd"/>
|
||||
</style:style>
|
||||
<style:style style:name="P36" style:family="paragraph" style:parent-style-name="Table_20_Contents" style:list-style-name="L4">
|
||||
<style:style style:name="P36" style:family="paragraph" style:parent-style-name="Table_20_Contents">
|
||||
<style:text-properties officeooo:rsid="006f78fd" officeooo:paragraph-rsid="00702fdb"/>
|
||||
</style:style>
|
||||
<style:style style:name="P37" style:family="paragraph" style:parent-style-name="Table_20_Contents" style:list-style-name="L4">
|
||||
<style:text-properties officeooo:rsid="0034c4ee" officeooo:paragraph-rsid="0034c4ee"/>
|
||||
</style:style>
|
||||
<style:style style:name="P37" style:family="paragraph" style:parent-style-name="Heading_20_1" style:master-page-name="">
|
||||
<style:style style:name="P38" style:family="paragraph" style:parent-style-name="Heading_20_1" style:master-page-name="">
|
||||
<style:paragraph-properties style:page-number="auto" fo:break-before="auto" fo:break-after="auto"/>
|
||||
</style:style>
|
||||
<style:style style:name="P38" style:family="paragraph" style:parent-style-name="Title" style:master-page-name="First_20_Page">
|
||||
<style:style style:name="P39" style:family="paragraph" style:parent-style-name="Title" style:master-page-name="First_20_Page">
|
||||
<style:paragraph-properties style:page-number="auto"/>
|
||||
<style:text-properties officeooo:paragraph-rsid="0004b30c"/>
|
||||
</style:style>
|
||||
@ -122960,7 +122963,7 @@
|
||||
<office:master-styles>
|
||||
<style:master-page style:name="Standard" style:page-layout-name="pm1">
|
||||
<style:footer>
|
||||
<text:p text:style-name="P1"><text:span text:style-name="T1">TextureSync</text:span> <text:tab/><text:title>Charter</text:title> <text:span text:style-name="T2">⸬ </text:span><text:span text:style-name="T1">Version </text:span><text:user-defined style:data-style-name="N0" text:name="Version">1.2.0</text:user-defined><text:tab/><text:span text:style-name="T3">Seite</text:span> <text:page-number text:select-page="current">2</text:page-number>/<text:page-count>3</text:page-count></text:p>
|
||||
<text:p text:style-name="P1"><text:span text:style-name="T1">TextureSync</text:span> <text:tab/><text:title>Charter</text:title> <text:span text:style-name="T2">⸬ </text:span><text:span text:style-name="T1">Version </text:span><text:user-defined style:data-style-name="N0" text:name="Version">1.2.0</text:user-defined><text:tab/><text:span text:style-name="T3">Seite</text:span> <text:page-number text:select-page="current">3</text:page-number>/<text:page-count>3</text:page-count></text:p>
|
||||
</style:footer>
|
||||
</style:master-page>
|
||||
<style:master-page style:name="First_20_Page" style:display-name="First Page" style:page-layout-name="pm2" style:next-style-name="Standard"/>
|
||||
@ -122977,7 +122980,7 @@
|
||||
<text:sequence-decl text:display-outline-level="0" text:name="Drawing"/>
|
||||
<text:sequence-decl text:display-outline-level="0" text:name="Figure"/>
|
||||
</text:sequence-decls>
|
||||
<text:p text:style-name="P38"/>
|
||||
<text:p text:style-name="P39"/>
|
||||
<text:p text:style-name="P17"/>
|
||||
<text:p text:style-name="Title"><text:title>Charter</text:title></text:p>
|
||||
<text:p text:style-name="Subtitle">TextureSync</text:p>
|
||||
@ -123094,7 +123097,7 @@
|
||||
<text:p text:style-name="Table_20_Contents">Wichtigste Funktionen</text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Tabelle2.B2" office:value-type="string">
|
||||
<text:list xml:id="list1098403870" text:style-name="L1">
|
||||
<text:list xml:id="list652413232" text:style-name="L1">
|
||||
<text:list-item>
|
||||
<text:p text:style-name="P30">Texturen anzeigen und verwalten</text:p>
|
||||
</text:list-item>
|
||||
@ -123118,7 +123121,7 @@
|
||||
<text:p text:style-name="Table_20_Contents">Akzeptanzkriterien</text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Tabelle2.B2" office:value-type="string">
|
||||
<text:list xml:id="list846724402" text:style-name="L2">
|
||||
<text:list xml:id="list868670440" text:style-name="L2">
|
||||
<text:list-item>
|
||||
<text:p text:style-name="P31">Das Durchsuchen darf nicht länger als 1 Sekunde bei 1000 Texturen dauern.</text:p>
|
||||
</text:list-item>
|
||||
@ -123206,7 +123209,7 @@
|
||||
<text:p text:style-name="P8">Projekt</text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Tabelle2.B2" office:value-type="string">
|
||||
<text:list xml:id="list1130294127" text:style-name="L3">
|
||||
<text:list xml:id="list4239375724" text:style-name="L3">
|
||||
<text:list-item>
|
||||
<text:p text:style-name="P33"><text:span text:style-name="T11">u</text:span>nzureichende Aufwandsschätzungen</text:p>
|
||||
</text:list-item>
|
||||
@ -123224,7 +123227,7 @@
|
||||
<text:p text:style-name="P8">Produkt</text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Tabelle2.B2" office:value-type="string">
|
||||
<text:list xml:id="list192018566" text:style-name="L4">
|
||||
<text:list xml:id="list1833401399" text:style-name="L4">
|
||||
<text:list-item>
|
||||
<text:p text:style-name="P34">Dateiverlust</text:p>
|
||||
</text:list-item>
|
||||
@ -123232,10 +123235,10 @@
|
||||
<text:p text:style-name="P34">Metadaten werden <text:span text:style-name="T11">vertauscht </text:span></text:p>
|
||||
</text:list-item>
|
||||
<text:list-item>
|
||||
<text:p text:style-name="P36">Verlust von Daten durch nicht-funktionierende Synchronisation</text:p>
|
||||
<text:p text:style-name="P37">Verlust von Daten durch nicht-funktionierende Synchronisation</text:p>
|
||||
</text:list-item>
|
||||
<text:list-item>
|
||||
<text:p text:style-name="P36">„single point of failure“ durch Server-Architektur</text:p>
|
||||
<text:p text:style-name="P37">„single point of failure“ durch Server-Architektur</text:p>
|
||||
</text:list-item>
|
||||
</text:list>
|
||||
</table:table-cell>
|
||||
@ -123342,18 +123345,18 @@
|
||||
</table:table-row>
|
||||
<table:table-row>
|
||||
<table:table-cell table:style-name="Tabelle5.A2" office:value-type="string">
|
||||
<text:p text:style-name="P15">1.2.0</text:p>
|
||||
<text:p text:style-name="P15">1.1.0</text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Tabelle5.B2" office:value-type="string">
|
||||
<text:p text:style-name="P15">Risiken überarbeitet, Einschränkungen bei Anzahl der Nutzer</text:p>
|
||||
<text:p text:style-name="P15">Changelog hinzugefügt</text:p>
|
||||
</table:table-cell>
|
||||
</table:table-row>
|
||||
<table:table-row>
|
||||
<table:table-cell table:style-name="Tabelle5.A3" office:value-type="string">
|
||||
<text:p text:style-name="P15">1.1.0</text:p>
|
||||
<text:p text:style-name="P36">1.2.0</text:p>
|
||||
</table:table-cell>
|
||||
<table:table-cell table:style-name="Tabelle5.B3" office:value-type="string">
|
||||
<text:p text:style-name="P15">Changelog hinzugefügt</text:p>
|
||||
<text:p text:style-name="P36">Risiken überarbeitet, Einschränkungen bei Anzahl der Nutzer</text:p>
|
||||
</table:table-cell>
|
||||
</table:table-row>
|
||||
</table:table>
|
||||
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 68 KiB |
@ -1,111 +1,116 @@
|
||||
<svg width="1337" height="1019" version="1.1" baseProfile="full" viewbox="0 0 1337 1019" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events" style="font-weight:bold; font-size:12pt; font-family:'Calibri', Helvetica, sans-serif;;stroke-width:3;stroke-linejoin:round;stroke-linecap:round"><path d="M161 209.9 L13.5 284.5 L13.5 837.5 L13.5 837.5 L81 859.7 L81 859.7 " style="stroke:#33322E;fill:none;stroke-dasharray:6 6;"></path>
|
||||
<path d="M66.7 860.6 L74.7 857.6 L70 850.5 L81 859.7 Z" style="stroke:#33322E;fill:#33322E;stroke-dasharray:none;"></path>
|
||||
<path d="M301 264.5 L301 284.5 L301 304.5 L301 304.5 " style="stroke:#33322E;fill:none;stroke-dasharray:6 6;"></path>
|
||||
<path d="M295.7 291.2 L301 297.8 L306.3 291.2 L301 304.5 Z" style="stroke:#33322E;fill:#33322E;stroke-dasharray:none;"></path>
|
||||
<path d="M441 170.1 L956.5 284.5 L956.5 327 L956.5 327 " style="stroke:#33322E;fill:none;stroke-dasharray:6 6;"></path>
|
||||
<path d="M951.2 313.7 L956.5 320.3 L961.8 313.7 L956.5 327 Z" style="stroke:#33322E;fill:#33322E;stroke-dasharray:none;"></path>
|
||||
<path d="M301 817.5 L301 837.5 L301 857.5 L301 857.5 " style="stroke:#33322E;fill:none;stroke-dasharray:6 6;"></path>
|
||||
<path d="M295.7 844.2 L301 850.8 L306.3 844.2 L301 857.5 Z" style="stroke:#33322E;fill:#33322E;stroke-dasharray:none;"></path>
|
||||
<path d="M956.5 795 L956.5 837.5 L521 900.3 L521 900.3 " style="stroke:#33322E;fill:none;stroke-dasharray:6 6;"></path>
|
||||
<path d="M533.4 893.1 L527.6 899.3 L535 903.7 L521 900.3 Z" style="stroke:#33322E;fill:#33322E;stroke-dasharray:none;"></path>
|
||||
<rect x="81.5" y="888.5" height="118" width="440" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
|
||||
<path d="M81.5 888.5 L81.5 857.5 L145 857.5 L145 888.5 Z" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></path>
|
||||
<text x="89.5" y="879" style="font-weight:normal;">model</text>
|
||||
<path d="M81.5 888.5 L521.5 888.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<rect x="94.5" y="901.5" height="92" width="159" style="stroke:#33322E;fill:#fdf6e3;stroke-dasharray:none;"></rect>
|
||||
<text x="112.3" y="923" style="">«enumeration»</text>
|
||||
<text x="102.8" y="938" style="">+ TextureFormat</text>
|
||||
<path d="M94.5 947.5 L253.5 947.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="102.5" y="969" style="font-weight:normal;">JPEG</text>
|
||||
<text x="102.5" y="984" style="font-weight:normal;">PNG</text>
|
||||
<rect x="293.5" y="916.5" height="62" width="102" style="stroke:#33322E;fill:#fdf6e3;stroke-dasharray:none;"></rect>
|
||||
<text x="301.8" y="938" style="">+ Texture</text>
|
||||
<path d="M293.5 947.5 L395.5 947.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="301.5" y="969" style="font-weight:normal;">...</text>
|
||||
<rect x="435.5" y="916.5" height="62" width="73" style="stroke:#33322E;fill:#fdf6e3;stroke-dasharray:none;"></rect>
|
||||
<text x="443.5" y="938" style="">Sha256</text>
|
||||
<path d="M435.5 947.5 L508.5 947.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="443.5" y="969" style="font-weight:normal;">...</text>
|
||||
<rect x="588.5" y="358.5" height="437" width="736" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
|
||||
<path d="M588.5 358.5 L588.5 327.5 L709 327.5 L709 358.5 Z" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></path>
|
||||
<text x="596.5" y="349" style="font-weight:normal;">persistency</text>
|
||||
<path d="M588.5 358.5 L1324.5 358.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<rect x="601.5" y="451.5" height="62" width="121" style="stroke:#33322E;fill:#fdf6e3;stroke-dasharray:none;"></rect>
|
||||
<text x="609.8" y="473" style="">+ DataStore</text>
|
||||
<path d="M601.5 482.5 L722.5 482.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="609.5" y="504" style="font-weight:normal;">...</text>
|
||||
<rect x="762.5" y="402.5" height="190" width="223" style="stroke:#33322E;fill:#fdf6e3;stroke-dasharray:none;"></rect>
|
||||
<path d="M762.5 402.5 L762.5 371.5 L835.5 371.5 L835.5 402.5 Z" style="stroke:#33322E;fill:#fdf6e3;stroke-dasharray:none;"></path>
|
||||
<text x="770.5" y="393" style="font-weight:normal;">result</text>
|
||||
<path d="M762.5 402.5 L985.5 402.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<rect x="775.5" y="415.5" height="62" width="197" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
|
||||
<text x="783.8" y="437" style="">+ TextureFileResult</text>
|
||||
<path d="M775.5 446.5 L972.5 446.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="783.5" y="468" style="font-weight:normal;">...</text>
|
||||
<rect x="780.5" y="517.5" height="62" width="187" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
|
||||
<text x="788.5" y="539" style="">+ TextureFileError</text>
|
||||
<path d="M780.5 548.5 L967.5 548.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="788.5" y="570" style="font-weight:normal;">...</text>
|
||||
<rect x="1025.5" y="453.5" height="88" width="109" style="stroke:#33322E;fill:#fdf6e3;stroke-dasharray:none;"></rect>
|
||||
<path d="M1025.5 453.5 L1025.5 422.5 L1098.5 422.5 L1098.5 453.5 Z" style="stroke:#33322E;fill:#fdf6e3;stroke-dasharray:none;"></path>
|
||||
<text x="1033.5" y="444" style="font-weight:normal;">search</text>
|
||||
<path d="M1025.5 453.5 L1134.5 453.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<rect x="1038.5" y="466.5" height="62" width="83" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
|
||||
<text x="1046.8" y="488" style="">+ Query</text>
|
||||
<path d="M1038.5 497.5 L1121.5 497.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="1046.5" y="519" style="font-weight:normal;">...</text>
|
||||
<rect x="848.5" y="663.5" height="119" width="463" style="stroke:#33322E;fill:#fdf6e3;stroke-dasharray:none;"></rect>
|
||||
<path d="M848.5 663.5 L848.5 632.5 L988 632.5 L988 663.5 Z" style="stroke:#33322E;fill:#fdf6e3;stroke-dasharray:none;"></path>
|
||||
<text x="856.5" y="654" style="font-weight:normal;">image_convert</text>
|
||||
<path d="M848.5 663.5 L1311.5 663.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<rect x="861.5" y="676.5" height="62" width="159" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
|
||||
<text x="869.8" y="698" style="">+ ConvertConfig</text>
|
||||
<path d="M861.5 707.5 L1020.5 707.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="869.5" y="729" style="font-weight:normal;">...</text>
|
||||
<path d="M848.5 751.5 L1311.5 751.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="856.5" y="773" style="font-weight:normal;">+ generate_preview(input, format, config) : ...</text>
|
||||
<rect x="53.5" y="335.5" height="482" width="495" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
|
||||
<path d="M53.5 335.5 L53.5 304.5 L145.5 304.5 L145.5 335.5 Z" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></path>
|
||||
<text x="61.5" y="326" style="font-weight:normal;">protocol</text>
|
||||
<path d="M53.5 335.5 L548.5 335.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<rect x="66.5" y="479.5" height="62" width="178" style="stroke:#33322E;fill:#fdf6e3;stroke-dasharray:none;"></rect>
|
||||
<text x="74.8" y="501" style="font-weight:normal;font-style:italic;">+ ProtocolHandler</text>
|
||||
<path d="M66.5 510.5 L244.5 510.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="74.5" y="532" style="font-weight:normal;">...</text>
|
||||
<rect x="71.5" y="711.5" height="62" width="168" style="stroke:#33322E;fill:#fdf6e3;stroke-dasharray:none;"></rect>
|
||||
<text x="79.5" y="733" style="">+ ProtocolConfig</text>
|
||||
<path d="M71.5 742.5 L239.5 742.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="79.5" y="764" style="font-weight:normal;">...</text>
|
||||
<rect x="284.5" y="379.5" height="292" width="251" style="stroke:#33322E;fill:#fdf6e3;stroke-dasharray:none;"></rect>
|
||||
<path d="M284.5 379.5 L284.5 348.5 L357.5 348.5 L357.5 379.5 Z" style="stroke:#33322E;fill:#fdf6e3;stroke-dasharray:none;"></path>
|
||||
<text x="292.5" y="370" style="font-weight:normal;">result</text>
|
||||
<path d="M284.5 379.5 L535.5 379.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<rect x="326.5" y="392.5" height="62" width="168" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
|
||||
<text x="334.5" y="414" style="">+ ProtocolResult</text>
|
||||
<path d="M326.5 423.5 L494.5 423.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="334.5" y="445" style="font-weight:normal;">...</text>
|
||||
<rect x="330.5" y="494.5" height="62" width="159" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
|
||||
<text x="338.8" y="516" style="">+ ProtocolError</text>
|
||||
<path d="M330.5 525.5 L489.5 525.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="338.5" y="547" style="font-weight:normal;">...</text>
|
||||
<rect x="297.5" y="596.5" height="62" width="225" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
|
||||
<text x="305.5" y="618" style="">+ ReplaceTextureStatus</text>
|
||||
<path d="M297.5 627.5 L522.5 627.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="305.5" y="649" style="font-weight:normal;">...</text>
|
||||
<path d="M53.5 786.5 L548.5 786.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="61.5" y="808" style="font-weight:normal;">+ listen_forever(handler : &ProtocolHandler) : ...</text>
|
||||
<rect x="161.5" y="44.5" height="220" width="280" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
|
||||
<path d="M161.5 44.5 L161.5 13.5 L215.5 13.5 L215.5 44.5 Z" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></path>
|
||||
<text x="169.5" y="35" style="font-weight:normal;">main</text>
|
||||
<path d="M161.5 44.5 L441.5 44.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<path d="M301.5 119.5 L301.5 139.5 L301.5 159.5 L301.5 159.5 " style="stroke:#33322E;fill:none;stroke-dasharray:6 6;"></path>
|
||||
<rect x="241.5" y="57.5" height="62" width="121" style="stroke:#33322E;fill:#fdf6e3;stroke-dasharray:none;"></rect>
|
||||
<text x="249.8" y="79" style="">ServerState</text>
|
||||
<path d="M241.5 88.5 L362.5 88.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="249.5" y="110" style="font-weight:normal;">...</text>
|
||||
<path d="M174.5 159.5 L420.5 159.5 L428.5 167.5 L428.5 220.5 L174.5 220.5 L174.5 159.5 Z" style="stroke:#33322E;fill:#fdf6e3;stroke-dasharray:none;"></path>
|
||||
<path d="M420.5 159.5 L420.5 167.5 L428.5 167.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="182.5" y="196" style="font-weight:normal;">implements</text>
|
||||
<text x="182.5" y="211" style="font-weight:normal;">protocol::ProtocolHandler</text>
|
||||
<path d="M161.5 233.5 L441.5 233.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="169.5" y="255" style="font-weight:normal;">+ main()</text></svg>
|
||||
<svg width="1513" height="1004" version="1.1" baseProfile="full" viewbox="0 0 1513 1004" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events" style="font-weight:bold; font-size:12pt; font-family:'Calibri', Helvetica, sans-serif;;stroke-width:3;stroke-linejoin:round;stroke-linecap:round"><path d="M229 180.3 L-26.5 269.5 L-26.5 546 L-26.5 546 L-26.5 822.5 L-26.5 822.5 L149 864.4 L149 864.4 " style="stroke:#33322E;fill:none;stroke-dasharray:6 6;"></path>
|
||||
<path d="M134.8 866.5 L142.5 862.9 L137.3 856.1 L149 864.4 Z" style="stroke:#33322E;fill:#33322E;stroke-dasharray:none;"></path>
|
||||
<path d="M369 249.5 L369 269.5 L369 289.5 L369 289.5 " style="stroke:#33322E;fill:none;stroke-dasharray:6 6;"></path>
|
||||
<path d="M363.7 276.2 L369 282.8 L374.3 276.2 L369 289.5 Z" style="stroke:#33322E;fill:#33322E;stroke-dasharray:none;"></path>
|
||||
<path d="M509 156.8 L1132.5 269.5 L1132.5 312 L1132.5 312 " style="stroke:#33322E;fill:none;stroke-dasharray:6 6;"></path>
|
||||
<path d="M1127.2 298.7 L1132.5 305.3 L1137.8 298.7 L1132.5 312 Z" style="stroke:#33322E;fill:#33322E;stroke-dasharray:none;"></path>
|
||||
<path d="M369 802.5 L369 822.5 L369 842.5 L369 842.5 " style="stroke:#33322E;fill:none;stroke-dasharray:6 6;"></path>
|
||||
<path d="M363.7 829.2 L369 835.8 L374.3 829.2 L369 842.5 Z" style="stroke:#33322E;fill:#33322E;stroke-dasharray:none;"></path>
|
||||
<path d="M1132.5 780 L1132.5 822.5 L589 889.8 L589 889.8 " style="stroke:#33322E;fill:none;stroke-dasharray:6 6;"></path>
|
||||
<path d="M601.6 882.8 L595.6 889 L602.9 893.4 L589 889.8 Z" style="stroke:#33322E;fill:#33322E;stroke-dasharray:none;"></path>
|
||||
<rect x="13.5" y="320.5" height="482" width="711" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
|
||||
<path d="M13.5 320.5 L13.5 289.5 L105.5 289.5 L105.5 320.5 Z" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></path>
|
||||
<text x="21.5" y="311" style="font-weight:normal;">protocol</text>
|
||||
<path d="M13.5 320.5 L724.5 320.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<rect x="26.5" y="464.5" height="62" width="178" style="stroke:#33322E;fill:#fdf6e3;stroke-dasharray:none;"></rect>
|
||||
<text x="115.5" y="486" style="font-weight:normal;font-style:italic;text-anchor: middle;">+ ProtocolHandler</text>
|
||||
<path d="M26.5 495.5 L204.5 495.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="34.5" y="517" style="font-weight:normal;">...</text>
|
||||
<rect x="31.5" y="696.5" height="62" width="168" style="stroke:#33322E;fill:#fdf6e3;stroke-dasharray:none;"></rect>
|
||||
<text x="115.5" y="718" style="text-anchor: middle;">+ ProtocolConfig</text>
|
||||
<path d="M31.5 727.5 L199.5 727.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="39.5" y="749" style="font-weight:normal;">...</text>
|
||||
<rect x="239.5" y="727.5" height="31" width="472" style="stroke:#33322E;fill:#fdf6e3;stroke-dasharray:none;"></rect>
|
||||
<path d="M239.5 727.5 L239.5 696.5 L360 696.5 L360 727.5 Z" style="stroke:#33322E;fill:#fdf6e3;stroke-dasharray:none;"></path>
|
||||
<text x="247.5" y="718" style="font-weight:normal;">autoconnect</text>
|
||||
<path d="M239.5 727.5 L711.5 727.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="247.5" y="749" style="font-weight:normal;">+ start_autoconnect_server(cfg : ProtocolConfig)</text>
|
||||
<rect x="350.5" y="364.5" height="292" width="251" style="stroke:#33322E;fill:#fdf6e3;stroke-dasharray:none;"></rect>
|
||||
<path d="M350.5 364.5 L350.5 333.5 L423.5 333.5 L423.5 364.5 Z" style="stroke:#33322E;fill:#fdf6e3;stroke-dasharray:none;"></path>
|
||||
<text x="358.5" y="355" style="font-weight:normal;">result</text>
|
||||
<path d="M350.5 364.5 L601.5 364.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<rect x="392.5" y="377.5" height="62" width="168" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
|
||||
<text x="476.5" y="399" style="text-anchor: middle;">+ ProtocolResult</text>
|
||||
<path d="M392.5 408.5 L560.5 408.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="400.5" y="430" style="font-weight:normal;">...</text>
|
||||
<rect x="396.5" y="479.5" height="62" width="159" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
|
||||
<text x="476" y="501" style="text-anchor: middle;">+ ProtocolError</text>
|
||||
<path d="M396.5 510.5 L555.5 510.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="404.5" y="532" style="font-weight:normal;">...</text>
|
||||
<rect x="363.5" y="581.5" height="62" width="225" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
|
||||
<text x="476" y="603" style="text-anchor: middle;">+ ReplaceTextureStatus</text>
|
||||
<path d="M363.5 612.5 L588.5 612.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="371.5" y="634" style="font-weight:normal;">...</text>
|
||||
<path d="M13.5 771.5 L724.5 771.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="21.5" y="793" style="font-weight:normal;">+ listen_forever(handler : &ProtocolHandler) : ...</text>
|
||||
<rect x="229.5" y="44.5" height="205" width="280" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
|
||||
<path d="M229.5 44.5 L229.5 13.5 L283.5 13.5 L283.5 44.5 Z" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></path>
|
||||
<text x="237.5" y="35" style="font-weight:normal;">main</text>
|
||||
<path d="M229.5 44.5 L509.5 44.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<path d="M369.5 119.5 L369.5 139.5 L369.5 159.5 L369.5 159.5 " style="stroke:#33322E;fill:none;stroke-dasharray:6 6;"></path>
|
||||
<rect x="309.5" y="57.5" height="62" width="121" style="stroke:#33322E;fill:#fdf6e3;stroke-dasharray:none;"></rect>
|
||||
<text x="370" y="79" style="text-anchor: middle;">ServerState</text>
|
||||
<path d="M309.5 88.5 L430.5 88.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="317.5" y="110" style="font-weight:normal;">...</text>
|
||||
<path d="M242.5 159.5 L488.5 159.5 L496.5 167.5 L496.5 205.5 L242.5 205.5 L242.5 159.5 Z" style="stroke:#33322E;fill:#fdf6e3;stroke-dasharray:none;"></path>
|
||||
<path d="M488.5 159.5 L488.5 167.5 L496.5 167.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="250.5" y="181" style="font-weight:normal;">implements</text>
|
||||
<text x="250.5" y="196" style="font-weight:normal;">protocol::ProtocolHandler</text>
|
||||
<path d="M229.5 218.5 L509.5 218.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="237.5" y="240" style="font-weight:normal;">+ main()</text>
|
||||
<rect x="149.5" y="873.5" height="118" width="440" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
|
||||
<path d="M149.5 873.5 L149.5 842.5 L213 842.5 L213 873.5 Z" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></path>
|
||||
<text x="157.5" y="864" style="font-weight:normal;">model</text>
|
||||
<path d="M149.5 873.5 L589.5 873.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<rect x="162.5" y="886.5" height="92" width="159" style="stroke:#33322E;fill:#fdf6e3;stroke-dasharray:none;"></rect>
|
||||
<text x="242" y="908" style="text-anchor: middle;">«enumeration»</text>
|
||||
<text x="242" y="923" style="text-anchor: middle;">+ TextureFormat</text>
|
||||
<path d="M162.5 932.5 L321.5 932.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="170.5" y="954" style="font-weight:normal;">JPEG</text>
|
||||
<text x="170.5" y="969" style="font-weight:normal;">PNG</text>
|
||||
<rect x="361.5" y="901.5" height="62" width="102" style="stroke:#33322E;fill:#fdf6e3;stroke-dasharray:none;"></rect>
|
||||
<text x="412.5" y="923" style="text-anchor: middle;">+ Texture</text>
|
||||
<path d="M361.5 932.5 L463.5 932.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="369.5" y="954" style="font-weight:normal;">...</text>
|
||||
<rect x="503.5" y="901.5" height="62" width="73" style="stroke:#33322E;fill:#fdf6e3;stroke-dasharray:none;"></rect>
|
||||
<text x="540" y="923" style="text-anchor: middle;">Sha256</text>
|
||||
<path d="M503.5 932.5 L576.5 932.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="511.5" y="954" style="font-weight:normal;">...</text>
|
||||
<rect x="764.5" y="343.5" height="437" width="736" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
|
||||
<path d="M764.5 343.5 L764.5 312.5 L885 312.5 L885 343.5 Z" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></path>
|
||||
<text x="772.5" y="334" style="font-weight:normal;">persistency</text>
|
||||
<path d="M764.5 343.5 L1500.5 343.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<rect x="1024.5" y="648.5" height="119" width="463" style="stroke:#33322E;fill:#fdf6e3;stroke-dasharray:none;"></rect>
|
||||
<path d="M1024.5 648.5 L1024.5 617.5 L1164 617.5 L1164 648.5 Z" style="stroke:#33322E;fill:#fdf6e3;stroke-dasharray:none;"></path>
|
||||
<text x="1032.5" y="639" style="font-weight:normal;">image_convert</text>
|
||||
<path d="M1024.5 648.5 L1487.5 648.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<rect x="1037.5" y="661.5" height="62" width="159" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
|
||||
<text x="1117" y="683" style="text-anchor: middle;">+ ConvertConfig</text>
|
||||
<path d="M1037.5 692.5 L1196.5 692.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="1045.5" y="714" style="font-weight:normal;">...</text>
|
||||
<path d="M1024.5 736.5 L1487.5 736.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="1032.5" y="758" style="font-weight:normal;">+ generate_preview(input, format, config) : ...</text>
|
||||
<rect x="777.5" y="436.5" height="62" width="121" style="stroke:#33322E;fill:#fdf6e3;stroke-dasharray:none;"></rect>
|
||||
<text x="838" y="458" style="text-anchor: middle;">+ DataStore</text>
|
||||
<path d="M777.5 467.5 L898.5 467.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="785.5" y="489" style="font-weight:normal;">...</text>
|
||||
<rect x="938.5" y="387.5" height="190" width="223" style="stroke:#33322E;fill:#fdf6e3;stroke-dasharray:none;"></rect>
|
||||
<path d="M938.5 387.5 L938.5 356.5 L1011.5 356.5 L1011.5 387.5 Z" style="stroke:#33322E;fill:#fdf6e3;stroke-dasharray:none;"></path>
|
||||
<text x="946.5" y="378" style="font-weight:normal;">result</text>
|
||||
<path d="M938.5 387.5 L1161.5 387.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<rect x="951.5" y="400.5" height="62" width="197" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
|
||||
<text x="1050" y="422" style="text-anchor: middle;">+ TextureFileResult</text>
|
||||
<path d="M951.5 431.5 L1148.5 431.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="959.5" y="453" style="font-weight:normal;">...</text>
|
||||
<rect x="956.5" y="502.5" height="62" width="187" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
|
||||
<text x="1050" y="524" style="text-anchor: middle;">+ TextureFileError</text>
|
||||
<path d="M956.5 533.5 L1143.5 533.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="964.5" y="555" style="font-weight:normal;">...</text>
|
||||
<rect x="1201.5" y="438.5" height="88" width="109" style="stroke:#33322E;fill:#fdf6e3;stroke-dasharray:none;"></rect>
|
||||
<path d="M1201.5 438.5 L1201.5 407.5 L1274.5 407.5 L1274.5 438.5 Z" style="stroke:#33322E;fill:#fdf6e3;stroke-dasharray:none;"></path>
|
||||
<text x="1209.5" y="429" style="font-weight:normal;">search</text>
|
||||
<path d="M1201.5 438.5 L1310.5 438.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<rect x="1214.5" y="451.5" height="62" width="83" style="stroke:#33322E;fill:#eee8d5;stroke-dasharray:none;"></rect>
|
||||
<text x="1256" y="473" style="text-anchor: middle;">+ Query</text>
|
||||
<path d="M1214.5 482.5 L1297.5 482.5" style="stroke:#33322E;fill:none;stroke-dasharray:none;"></path>
|
||||
<text x="1222.5" y="504" style="font-weight:normal;">...</text></svg>
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 12 KiB |
@ -41,6 +41,11 @@
|
||||
[+ ProtocolConfig|...]
|
||||
|
||||
[+ ProtocolHandler] -/- [+ ProtocolConfig]
|
||||
[<package> autoconnect |
|
||||
+ start_autoconnect_server(cfg : ProtocolConfig)
|
||||
]
|
||||
|
||||
[result] -/- [autoconnect]
|
||||
|
||||
[<package> result|
|
||||
[+ ProtocolResult|...]
|
||||
@ -56,8 +61,8 @@
|
||||
|
||||
|
||||
[<package> main|
|
||||
[ServerState|...] -- [<note>
|
||||
implements
|
||||
[ServerState|...]
|
||||
[ServerState] -- [<note> implements
|
||||
protocol::ProtocolHandler
|
||||
]
|
||||
|
|
||||
@ -70,4 +75,3 @@
|
||||
|
||||
[protocol] --> [model]
|
||||
[persistency] --> [model]
|
||||
|
||||
|
15
doc/kurzanleitungen/client/inhalt.txt
Normal file
@ -0,0 +1,15 @@
|
||||
- herunterladen (Release Gitea)
|
||||
- installiern (Java VM)
|
||||
- starten
|
||||
- connecten
|
||||
- neue textur einfügen
|
||||
- query mit Tags
|
||||
- query mit Name
|
||||
- query mit Auflösung
|
||||
- query mit Datum
|
||||
- query mit Kombination
|
||||
- exportieren
|
||||
- ändern
|
||||
- löschen
|
||||
- settings file löschen
|
||||
|
7
doc/kurzanleitungen/server/inhalt.txt
Normal file
@ -0,0 +1,7 @@
|
||||
- herunterladen (Release Gitea)
|
||||
- installiern (service daemon)
|
||||
- starten
|
||||
- loggen
|
||||
- stoppen
|
||||
- backups machen
|
||||
- backups wieder einspielen
|
1
doc/praesentation/Resourcen/Artefakte.drawio
Normal file
@ -0,0 +1 @@
|
||||
<mxfile modified="2019-06-18T13:37:07.094Z" host="www.draw.io" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36" etag="_ZMFen25GhU1ZPReOHi7" version="10.7.7" type="device"><diagram name="Page-1" id="10a91c8b-09ff-31b1-d368-03940ed4cc9e">7ZxZd+I2FMc/DY/p8Y55zDqd00nPnEmXyaOw5SUIy5XlAPn0lbC8YZihAVtyCnmIdSUbWf+fpcvVhYl5u1x/IiCNHrEP0cTQ/PXEvJsYhm7OLPaPWzaFxXFnhSEksS8a1Yan+A0KoyaseezDrNWQYoxonLaNHk4S6NGWDRCCV+1mAUbtd01BCDuGJw+grvXv2KeRsOqaVlf8CuMwEm/t2qJiDrxFSHCeiPebGGawfRXVS1BeS7TPIuDjVcNk3k/MW4IxLY6W61uI+NiWw1ac93Cgtuo3gQk95oQXNJ9Dffroxes5RvZv1w/foit+wrZzdFMOCPTZ+IgiJjTCIU4Auq+tN7X1C8Ypa6Yz4wukdCPkBTnFzBTRJRK1cB3T7+xYE8fP/PgXW5Tu1o2qu01ZSCjZfC8vwAuNs3ixPm1bKs97yZdpeQMJTniPPbyMPVHt5eQV+uKyxd3zWz44qsKU4Zx4opVjuDNTd11PC8DUdIyrElxAQkh/NNyV7ux5gngJWb/ZeQQiQOPXdh+AADus2tXisgOh73/R2nyH1q3Rag7jnlFWnIuBtTalam1dtB5Qa0uq1vZF6wG1tqVq7XwMrQfWTJepmamyZpVOz42anzyfjUfyufVEKvF8Tk/UenvqNSFg02iQ4jihWePKX7mBNViXvRXOlfgUpGt22xnfbe9oP2rPDooe1NRVt3ICiFOVQTSOJ3Fgolyps4errmj6RN0ZXxtItEM9fAUoF+8yMRzE+noT4O0UUCvp/JPjsuIq2472NWtg2em6rmRHIf9/TSgMwILC8nKsZ8UVi/oOJqsopvApBduxXBGQtkXchi62kHB5qugEr0JgDtFNFd+4xQiTGpiMEryApXFimL4zd2ynqinDKPxKQYxQo2UQBIbniTt+AMsYcTz+gsQHCRBmQZ3O1LsBKA4TVvAYI5BUN7kHmlfIhmfdMHWlF7XmrD31lvGZVR39sRzhUkeNwE85Y5/9CT8dGN3ZB8xtBAgftXPjop+Ii+O5cB4cg4sPoBucjkuHjT0EHcSl4qNcqc0uL3rZZhBejJ54+QIyCpMIBlQ5ZFxjbjpHzTC+DV3fUgsZQ5ONjNkTMl8DFHuRqtT409lc045bl6BzhnXpvNRMZVNj9UTNtziLF5iNLNpk5/dmTqVm7tqWfRw1rgdVo8a0ZFNj9zXXEPwCFzRFIFGOmZkzNcFR6xPU2Qo1VYsZS5fNjNMTM4/cpWEjlamJzbgWKNdRzq0xOtopExt5185HVehz5+Nw1PSYuMqp4dXTBP9oW9gjENyRKvhH28cegeByw90fbTN7BILPZAo+7cn1e4Ax89Iy7gapFi93tq+j3D6b/x3v9vFy4/Ti1ZM76GiqBUbdnlD6RPC8J5T+X8EKa3eXXDoy5uyy3JxpuSmDCD9db6Q6lJZ+EXxowXWp6ZHWJUgwvOJSk5+tS5RgeMWlpsValzDB8IobMhUvu3l2Z/8PmPFEz8tHxj52EGxbMf9fNw5ilNaqn4Or68UbTClI3vhu1EG+KnN6+Wx52u6U5Upn63DWTYettuK9APiZjXVI2NSMk2w0DI4rV3CXQfnJGPrhHJ73MTgAqI/Yz9FoCB1X6kdnD196kpl+OF9IWUKfNmwGXY4G0XFlz3YWcunZSXpf6Ul3gMIEkgggmiehcpsL4+Jmdz9KfnqSfoa9zb1fBfod0rcVJIuUYIoXmPdEMXTGldnWQUf+qtjXXuZT7kXq5VyPy83fxUUBN392Oi7Tfbj8+Vk5VsblcO+y0qc3w4r1z0kVXxqvf7PLvP8X</diagram></mxfile>
|
BIN
doc/praesentation/Resourcen/Artefakte.png
Normal file
After Width: | Height: | Size: 104 KiB |
BIN
doc/praesentation/Resourcen/Artefakte_selected.png
Normal file
After Width: | Height: | Size: 126 KiB |
BIN
doc/praesentation/Resourcen/Kotlin_logo_wordmark.png
Normal file
After Width: | Height: | Size: 1.0 MiB |
BIN
doc/praesentation/Resourcen/issue.png
Normal file
After Width: | Height: | Size: 90 KiB |
BIN
doc/praesentation/Resourcen/issue_28.png
Normal file
After Width: | Height: | Size: 52 KiB |
BIN
doc/praesentation/Resourcen/planner.png
Normal file
After Width: | Height: | Size: 67 KiB |
BIN
doc/praesentation/Resourcen/releases.png
Normal file
After Width: | Height: | Size: 48 KiB |
BIN
doc/praesentation/Resourcen/screenshot.png
Normal file
After Width: | Height: | Size: 681 KiB |
BIN
doc/praesentation/Resourcen/screenshot_bad.png
Normal file
After Width: | Height: | Size: 317 KiB |
286512
doc/praesentation/TextureSync.fodp
Normal file
BIN
doc/praesentation/TextureSync.pdf
Normal file
34
doc/praesentation/vorgaben.txt
Normal file
@ -0,0 +1,34 @@
|
||||
- kleine Präsentation über Projekt
|
||||
- Idee (Problem der Verwaltung vieler Texturen)
|
||||
- Ziele (einfach, lokale Daten, gemeinschafftlicher Zugriff)
|
||||
- Gelerntes (gute Planung, Tests, Versionsverwaltung, Issue-Tracker)
|
||||
|
||||
|
||||
Jede Gruppe hat 20 Min für eine Präsentation des Projektes:
|
||||
|
||||
Welche waren die Ziele im Project Charter und was wurde erreicht
|
||||
Kurze Präsentation der Artefakte (Lastenheft, Pflichtenheft, Grobdesign, System und Akzeptanztests, Traceability)
|
||||
Welche Technologien wurden benutzt und warum
|
||||
Welche Lehren zieht man / best practices für Projekt 2
|
||||
Kurze Demo des Endproduktes
|
||||
|
||||
|
||||
############ Traceability ############
|
||||
|
||||
Lastenheft F#1
|
||||
Pflichtenheft MK#7
|
||||
Grobdesign 2.2 View --> Import
|
||||
Systemtest ST#1 bis ST#6
|
||||
Akzeptanztests AT#1
|
||||
|
||||
|
||||
|
||||
TODO (Hendrik)
|
||||
Planung ist alles
|
||||
Aufgabenpakete (abhängig, parallel)
|
||||
Fehler sofort dokumentieren und beheben
|
||||
Issue Tracker
|
||||
Versionsverwaltung
|
||||
Tests
|
||||
Wenn es geht nichts neues probieren, bewährtes beibehalten
|
||||
In Endnutzer hineinversetzten
|
BIN
doc/tests/akzeptanztest/akzeptanztest-v1.0.0.pdf
Normal file
BIN
doc/tests/akzeptanztest/akzeptanztest-v1.0.1.pdf
Normal file
BIN
doc/tests/integrationstest/integrationstest-v1.0.0.pdf
Normal file
BIN
doc/tests/integrationstest/integrationstest-v1.0.1.pdf
Normal file
BIN
doc/tests/modultest/modultest-v1.0.0.pdf
Normal file
BIN
doc/tests/modultest/modultest-v1.0.1.pdf
Normal file
BIN
doc/tests/systemtest/systemtest-v1.0.0.pdf
Normal file
BIN
doc/tests/systemtest/systemtest-v1.0.1.pdf
Normal file
@ -4,9 +4,9 @@
|
||||
<phases/>
|
||||
<calendars>
|
||||
<day-types>
|
||||
<day-type id="0" name="Working" description="A default working day"/>
|
||||
<day-type id="1" name="Nonworking" description="A default non working day"/>
|
||||
<day-type id="2" name="Use base" description="Use day from base calendar"/>
|
||||
<day-type id="0" name="Arbeiten" description="Ein Vorgabe-Arbeitstag"/>
|
||||
<day-type id="1" name="Nicht Arbeiten" description="Ein Vorgabetag, an dem nicht gearbeitet wird"/>
|
||||
<day-type id="2" name="Basis verwenden" description="Tag vom Basiskalender verwenden"/>
|
||||
</day-types>
|
||||
<calendar id="1" name="Vorgabe">
|
||||
<default-week mon="1" tue="0" wed="0" thu="1" fri="1" sat="0" sun="1"/>
|
||||
@ -16,7 +16,9 @@
|
||||
<interval start="1300" end="1700"/>
|
||||
</overridden-day-type>
|
||||
</overridden-day-types>
|
||||
<days/>
|
||||
<days>
|
||||
<day date="20190613" type="day-type" id="0"/>
|
||||
</days>
|
||||
</calendar>
|
||||
</calendars>
|
||||
<tasks>
|
||||
@ -92,88 +94,82 @@
|
||||
<predecessor id="1" predecessor-id="10" type="FS"/>
|
||||
</predecessors>
|
||||
</task>
|
||||
<task id="19" name="Realisation" note="" work="1065600" start="20190423T000000Z" end="20190511T170000Z" work-start="20190423T080000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
|
||||
<task id="19" name="Realisation" note="" work="1641600" start="20190416T133640Z" end="20190612T170000Z" work-start="20190416T133640Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
|
||||
<predecessors>
|
||||
<predecessor id="1" predecessor-id="18" type="FS"/>
|
||||
</predecessors>
|
||||
<task id="20" name="Einrichtung von Tests" note="" work="28800" start="20190423T000000Z" end="20190423T170000Z" work-start="20190423T080000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
|
||||
<task id="20" name="Einrichtung von Tests" note="" work="28800" start="20190416T133640Z" end="20190417T133640Z" work-start="20190416T133640Z" percent-complete="100" priority="0" type="normal" scheduling="fixed-work">
|
||||
<predecessors>
|
||||
<predecessor id="1" predecessor-id="11" type="FS"/>
|
||||
</predecessors>
|
||||
<task id="21" name="Client Tests" note="" work="28800" start="20190423T000000Z" end="20190423T170000Z" work-start="20190423T080000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
|
||||
</task>
|
||||
<task id="21" name="Client" note="" work="1123200" start="20190416T133640Z" end="20190612T170000Z" work-start="20190416T133640Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
|
||||
<predecessors>
|
||||
<predecessor id="1" predecessor-id="11" type="FS"/>
|
||||
</predecessors>
|
||||
<task id="22" name="UI" note="" work="288000" start="20190416T133640Z" end="20190423T133640Z" work-start="20190416T133640Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
|
||||
<task id="23" name="Previews" note="" work="86400" start="20190416T133640Z" end="20190423T133640Z" work-start="20190416T133640Z" percent-complete="100" priority="0" type="normal" scheduling="fixed-work"/>
|
||||
<task id="24" name="Filter UI" note="" work="86400" start="20190416T133640Z" end="20190423T133640Z" work-start="20190416T133640Z" percent-complete="100" priority="0" type="normal" scheduling="fixed-work"/>
|
||||
<task id="25" name="3D Visualisierung" note="" work="57600" start="20190416T133640Z" end="20190420T133640Z" work-start="20190416T133640Z" percent-complete="100" priority="0" type="normal" scheduling="fixed-work"/>
|
||||
<task id="26" name="Dateien hinzufügen" note="" work="28800" start="20190416T133640Z" end="20190417T133640Z" work-start="20190416T133640Z" percent-complete="100" priority="0" type="normal" scheduling="fixed-work"/>
|
||||
<task id="27" name="Dateien lokal abspeichern" note="" work="28800" start="20190416T133640Z" end="20190417T133640Z" work-start="20190416T133640Z" percent-complete="100" priority="0" type="normal" scheduling="fixed-work"/>
|
||||
</task>
|
||||
<task id="28" name="Grund Architektur" note="" work="662400" start="20190423T000000Z" end="20190612T170000Z" work-start="20190423T080000Z" percent-complete="100" priority="0" type="normal" scheduling="fixed-work">
|
||||
<constraint type="start-no-earlier-than" time="20190423T000000Z"/>
|
||||
</task>
|
||||
<task id="29" name="Protokoll implementieren" note="" work="172800" start="20190423T000000Z" end="20190504T170000Z" work-start="20190423T080000Z" percent-complete="100" priority="0" type="normal" scheduling="fixed-work">
|
||||
<constraint type="start-no-earlier-than" time="20190423T000000Z"/>
|
||||
</task>
|
||||
</task>
|
||||
<task id="22" name="Client" note="" work="547200" start="20190423T000000Z" end="20190504T170000Z" work-start="20190423T080000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
|
||||
<task id="30" name="Server" note="" work="489600" start="20190423T000000Z" end="20190511T170000Z" work-start="20190423T080000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
|
||||
<predecessors>
|
||||
<predecessor id="1" predecessor-id="11" type="FS"/>
|
||||
</predecessors>
|
||||
<task id="23" name="UI" note="" work="288000" start="20190423T170000Z" end="20190430T170000Z" work-start="20190424T080000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
|
||||
<predecessors>
|
||||
<predecessor id="1" predecessor-id="21" type="FS"/>
|
||||
</predecessors>
|
||||
<task id="24" name="Previews" note="" work="86400" start="20190423T170000Z" end="20190430T170000Z" work-start="20190424T080000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work"/>
|
||||
<task id="25" name="Filter UI" note="" work="86400" start="20190423T170000Z" end="20190430T170000Z" work-start="20190424T080000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work"/>
|
||||
<task id="26" name="3D Visualisierung" note="" work="57600" start="20190423T170000Z" end="20190427T170000Z" work-start="20190424T080000Z" percent-complete="50" priority="0" type="normal" scheduling="fixed-work"/>
|
||||
<task id="27" name="Dateien hinzufügen" note="" work="28800" start="20190423T170000Z" end="20190424T170000Z" work-start="20190424T080000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work"/>
|
||||
<task id="28" name="Dateien lokal abspeichern" note="" work="28800" start="20190423T170000Z" end="20190424T170000Z" work-start="20190424T080000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work"/>
|
||||
</task>
|
||||
<task id="29" name="Grund Architektur" note="" work="86400" start="20190423T000000Z" end="20190427T170000Z" work-start="20190423T080000Z" percent-complete="70" priority="0" type="normal" scheduling="fixed-work">
|
||||
<constraint type="start-no-earlier-than" time="20190423T000000Z"/>
|
||||
</task>
|
||||
<task id="30" name="Protokoll implementieren" note="" work="172800" start="20190423T000000Z" end="20190504T170000Z" work-start="20190423T080000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
|
||||
<constraint type="start-no-earlier-than" time="20190423T000000Z"/>
|
||||
</task>
|
||||
</task>
|
||||
<task id="31" name="Server" note="" work="489600" start="20190423T000000Z" end="20190511T170000Z" work-start="20190423T080000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
|
||||
<predecessors>
|
||||
<predecessor id="1" predecessor-id="11" type="FS"/>
|
||||
</predecessors>
|
||||
<task id="32" name="Dateien speichern/verwalten" note="" work="172800" start="20190423T000000Z" end="20190504T170000Z" work-start="20190423T080000Z" percent-complete="100" priority="0" type="normal" scheduling="fixed-work">
|
||||
<task id="31" name="Dateien speichern/verwalten" note="" work="172800" start="20190423T000000Z" end="20190504T170000Z" work-start="20190423T080000Z" percent-complete="100" priority="0" type="normal" scheduling="fixed-work">
|
||||
<constraint type="must-start-on" time="20190423T000000Z"/>
|
||||
</task>
|
||||
<task id="33" name="Protokoll implementieren" note="" work="172800" start="20190423T000000Z" end="20190504T170000Z" work-start="20190423T080000Z" percent-complete="100" priority="0" type="normal" scheduling="fixed-work">
|
||||
<task id="32" name="Protokoll implementieren" note="" work="172800" start="20190423T000000Z" end="20190504T170000Z" work-start="20190423T080000Z" percent-complete="100" priority="0" type="normal" scheduling="fixed-work">
|
||||
<constraint type="must-start-on" time="20190423T000000Z"/>
|
||||
</task>
|
||||
<task id="34" name="Suche implementieren" note="" work="86400" start="20190504T170000Z" end="20190511T170000Z" work-start="20190507T080000Z" percent-complete="100" priority="0" type="normal" scheduling="fixed-work">
|
||||
<task id="33" name="Suche implementieren" note="" work="86400" start="20190504T170000Z" end="20190511T170000Z" work-start="20190507T080000Z" percent-complete="100" priority="0" type="normal" scheduling="fixed-work">
|
||||
<predecessors>
|
||||
<predecessor id="1" predecessor-id="32" type="FS"/>
|
||||
<predecessor id="1" predecessor-id="31" type="FS"/>
|
||||
</predecessors>
|
||||
</task>
|
||||
<task id="35" name="Hauptlogik implementieren" note="Hauptsächlich Glue-Code " work="57600" start="20190504T170000Z" end="20190508T170000Z" work-start="20190507T080000Z" percent-complete="100" priority="0" type="normal" scheduling="fixed-work">
|
||||
<task id="34" name="Hauptlogik implementieren" note="Hauptsächlich Glue-Code " work="57600" start="20190504T170000Z" end="20190508T170000Z" work-start="20190507T080000Z" percent-complete="100" priority="0" type="normal" scheduling="fixed-work">
|
||||
<predecessors>
|
||||
<predecessor id="1" predecessor-id="33" type="FS"/>
|
||||
<predecessor id="1" predecessor-id="32" type="FS"/>
|
||||
<predecessor id="1" predecessor-id="31" type="FS"/>
|
||||
</predecessors>
|
||||
</task>
|
||||
</task>
|
||||
</task>
|
||||
<task id="36" name="End of Implementation" note="" work="0" start="20190511T170000Z" end="20190511T170000Z" work-start="20190511T170000Z" percent-complete="0" priority="0" type="milestone" scheduling="fixed-work">
|
||||
<task id="35" name="End of Implementation" note="" work="0" start="20190612T170000Z" end="20190612T170000Z" work-start="20190612T170000Z" percent-complete="0" priority="0" type="milestone" scheduling="fixed-work">
|
||||
<predecessors>
|
||||
<predecessor id="1" predecessor-id="31" type="FS"/>
|
||||
<predecessor id="1" predecessor-id="22" type="FS"/>
|
||||
<predecessor id="1" predecessor-id="30" type="FS"/>
|
||||
<predecessor id="1" predecessor-id="21" type="FS"/>
|
||||
<predecessor id="1" predecessor-id="20" type="FS"/>
|
||||
</predecessors>
|
||||
</task>
|
||||
<task id="37" name="Abnahme" note="" work="374400" start="20190511T170000Z" end="20190611T170000Z" work-start="20190514T080000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
|
||||
<task id="38" name="Modultests [M]" note="" work="115200" start="20190511T170000Z" end="20190521T170000Z" work-start="20190514T080000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
|
||||
<task id="36" name="Abnahme" note="" work="115200" start="20190612T170000Z" end="20190613T170000Z" work-start="20190613T080000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
|
||||
<task id="37" name="Modultests [M]" note="" work="28800" start="20190612T170000Z" end="20190613T170000Z" work-start="20190613T080000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
|
||||
<predecessors>
|
||||
<predecessor id="1" predecessor-id="36" type="FS"/>
|
||||
<predecessor id="1" predecessor-id="35" type="FS"/>
|
||||
</predecessors>
|
||||
</task>
|
||||
<task id="39" name="Integrationstests [M]" note="" work="115200" start="20190521T170000Z" end="20190529T170000Z" work-start="20190522T080000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
|
||||
<task id="38" name="Integrationstests [M]" note="" work="28800" start="20190612T170000Z" end="20190613T170000Z" work-start="20190613T080000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
|
||||
<predecessors>
|
||||
<predecessor id="1" predecessor-id="38" type="FS"/>
|
||||
<predecessor id="1" predecessor-id="35" type="FS"/>
|
||||
</predecessors>
|
||||
</task>
|
||||
<task id="40" name="Systemtests [M]" note="" work="115200" start="20190529T170000Z" end="20190608T170000Z" work-start="20190601T080000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
|
||||
<task id="39" name="Systemtests [M]" note="" work="28800" start="20190612T170000Z" end="20190613T170000Z" work-start="20190613T080000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
|
||||
<predecessors>
|
||||
<predecessor id="1" predecessor-id="39" type="FS"/>
|
||||
<predecessor id="1" predecessor-id="35" type="FS"/>
|
||||
</predecessors>
|
||||
</task>
|
||||
<task id="41" name="Akzeptanztest [M]" note="" work="28800" start="20190608T170000Z" end="20190611T170000Z" work-start="20190611T080000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
|
||||
<task id="40" name="Akzeptanztest [M]" note="" work="28800" start="20190612T170000Z" end="20190613T170000Z" work-start="20190613T080000Z" percent-complete="0" priority="0" type="normal" scheduling="fixed-work">
|
||||
<predecessors>
|
||||
<predecessor id="1" predecessor-id="40" type="FS"/>
|
||||
<predecessor id="1" predecessor-id="35" type="FS"/>
|
||||
</predecessors>
|
||||
</task>
|
||||
</task>
|
||||
@ -186,16 +182,17 @@
|
||||
<resource id="4" name="Lukas" short-name="L" type="1" units="0" email="" note="" std-rate="0"/>
|
||||
</resources>
|
||||
<allocations>
|
||||
<allocation task-id="33" resource-id="1" units="100"/>
|
||||
<allocation task-id="29" resource-id="1" units="100"/>
|
||||
<allocation task-id="32" resource-id="1" units="100"/>
|
||||
<allocation task-id="8" resource-id="1" units="100"/>
|
||||
<allocation task-id="13" resource-id="1" units="100"/>
|
||||
<allocation task-id="11" resource-id="1" units="100"/>
|
||||
<allocation task-id="5" resource-id="1" units="50"/>
|
||||
<allocation task-id="29" resource-id="2" units="50"/>
|
||||
<allocation task-id="28" resource-id="2" units="50"/>
|
||||
<allocation task-id="8" resource-id="2" units="10"/>
|
||||
<allocation task-id="17" resource-id="2" units="100"/>
|
||||
<allocation task-id="5" resource-id="2" units="50"/>
|
||||
<allocation task-id="29" resource-id="3" units="50"/>
|
||||
<allocation task-id="28" resource-id="3" units="50"/>
|
||||
<allocation task-id="15" resource-id="3" units="100"/>
|
||||
<allocation task-id="12" resource-id="3" units="100"/>
|
||||
<allocation task-id="10" resource-id="3" units="100"/>
|
||||
|
38
orga/statusberichte/Statusbericht_KW23.md
Normal file
@ -0,0 +1,38 @@
|
||||
|
||||
#Statusbericht KW23 - TextureSync
|
||||
|
||||
Sehr geehrter Herr Nikolaropoulos,
|
||||
|
||||
hier der Statusbericht der vergangenen Woche TextureSync.
|
||||
|
||||
#Vergangene Arbeitswoche
|
||||
|
||||
* Robin Willmann erstelle die Netzwerk-Schnittstelle für den Client
|
||||
* Jannik Seiler und Hendrik Schutter erstellten die Logik des Clients
|
||||
* Lukas Fürderer erstellt einen Demo-Datensatz mit 1000 Texturen welche die Metadaten auf dem Bild darstellen
|
||||
* Robin Willmann erstellt das Wunschkriterium "Autoconnect" welches den Server automatisch findet
|
||||
|
||||
#Nächste Arbeitswoche
|
||||
|
||||
* Texturen ändern und Änderungen an den Server schicken
|
||||
* Alle Texturen beim Start anzeigen
|
||||
* Gefunden Texturen sofort anzeigen, nicht erst wenn alle gefunden sind
|
||||
* UI Style der MainView anpassen
|
||||
* Erste gefundene Textur initial ausgewählt
|
||||
* Beginn der Test des Clients und der Server-Client Kommunikation
|
||||
* Erstellen der Kurzanleitung
|
||||
|
||||
#Aktuelle Informationen über das Projekt
|
||||
|
||||
* Website mit aktuellem Projektplan: https://planner.mosad.xyz/TextureSync.html
|
||||
* Repository mit Code und Dokumenten: https://git.mosad.xyz/localhorst/TextureSync
|
||||
|
||||
Sollten Sie Fragen haben, können Sie sich gern bei uns melden.
|
||||
|
||||
Mit freundlichen Grüßen
|
||||
|
||||
Hendrik Schutter und Team TextureSync
|
||||
|
||||
|
||||
|
||||
|
42
orga/statusberichte/Statusbericht_KW24.md
Normal file
@ -0,0 +1,42 @@
|
||||
|
||||
#Statusbericht KW24 - TextureSync
|
||||
|
||||
Sehr geehrter Herr Nikolaropoulos,
|
||||
|
||||
hier der Statusbericht der vergangenen Woche TextureSync.
|
||||
|
||||
#Vergangene Arbeitswoche
|
||||
|
||||
* Der Client wurde fertiggestellt und um mehrere Wunschfunktionen ergänzt.
|
||||
* Es wurden zwei Releases erstellt und getestet.
|
||||
* Es wurden Kurzanleitungen für Server und Client erstellt.
|
||||
|
||||
#Nächste Arbeitswoche
|
||||
|
||||
* Die Präsentation für den 21.06.2019 fertigstellen.
|
||||
|
||||
#Aktuelle Informationen über das Projekt
|
||||
|
||||
Die Release 1.0.1 ist unsere erste Stable Release.
|
||||
|
||||
Alle Dokumente sind auf Moodle hochgeladen und somit von uns offiziell abgegeben.
|
||||
|
||||
Sie können sich gerne unsere Kurzanleitungen anschauen um einen ersten Eindruck der Software zu bekommen:
|
||||
|
||||
https://git.mosad.xyz/localhorst/TextureSync/wiki
|
||||
|
||||
Die eingebetteteten YouTube-Clips sind unsere Demos.
|
||||
|
||||
* Website mit aktuellem Projektplan: https://planner.mosad.xyz/TextureSync.html
|
||||
* Repository mit Code und Dokumenten: https://git.mosad.xyz/localhorst/TextureSync
|
||||
|
||||
Sollten Sie Fragen haben, können Sie sich gern bei uns melden.
|
||||
Wir freuen uns auf nächste Woche Freitag um Ihnen die fertige Software präsentieren zu können.
|
||||
|
||||
Mit freundlichen Grüßen
|
||||
|
||||
Hendrik Schutter und Team TextureSync
|
||||
|
||||
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "texture-sync-server"
|
||||
version = "0.1.0"
|
||||
version = "1.0.2"
|
||||
authors = ["CodeSteak <codesteak@shellf.art>"]
|
||||
edition = "2018"
|
||||
|
||||
|
@ -24,6 +24,8 @@ use std::path::*;
|
||||
fn main() -> std::io::Result<()> {
|
||||
lovecraft::invoke();
|
||||
|
||||
println!("\n\n\t=== TextureSync Server {} ===\t\n\n\n", env!("CARGO_PKG_VERSION"));
|
||||
|
||||
let data_path = Path::new("./data");
|
||||
println!("loading files from {:?}", data_path);
|
||||
let server_state = ServerState::new(data_path)?;
|
||||
@ -35,6 +37,10 @@ fn main() -> std::io::Result<()> {
|
||||
network_conf.listen_addr, network_conf.port
|
||||
);
|
||||
|
||||
match self::protocol::start_autoconnect_server_async(&network_conf) {
|
||||
Ok(_) => println!("Started Autoconnect Server"),
|
||||
Err(e) => println!("Starting Autoconnect Server failed: {:?}", e),
|
||||
}
|
||||
self::protocol::listen_forever(server_state, &network_conf)?;
|
||||
|
||||
Ok(())
|
||||
|
55
server/texture-sync-server/src/protocol/autoconnect/mod.rs
Normal file
@ -0,0 +1,55 @@
|
||||
use super::ProtocolConfig;
|
||||
|
||||
use std::io;
|
||||
use std::net::Ipv6Addr;
|
||||
use std::net::SocketAddr;
|
||||
use std::net::UdpSocket;
|
||||
use std::str::FromStr;
|
||||
use std::thread;
|
||||
|
||||
pub fn start_autoconnect_server_async(cfg: &ProtocolConfig) -> io::Result<()> {
|
||||
let cfg = cfg.clone();
|
||||
|
||||
let multi_sock = UdpSocket::bind((cfg.listen_addr.as_str(), cfg.port))?;
|
||||
|
||||
multi_sock.join_multicast_v6(
|
||||
&Ipv6Addr::from_str(&cfg.autoconnect_multicastv6_addr).map_err(|_| {
|
||||
io::Error::new(
|
||||
io::ErrorKind::InvalidInput,
|
||||
"Configured IPv6 addr. is invalid.",
|
||||
)
|
||||
})?,
|
||||
0,
|
||||
)?;
|
||||
|
||||
thread::spawn(move || {
|
||||
let mut buffer = vec![0u8; 8096];
|
||||
loop {
|
||||
match multi_sock.recv_from(&mut buffer) {
|
||||
Ok((pkg_size, sender)) => {
|
||||
let _ = respond(&multi_sock, &buffer[0..pkg_size], &sender, &cfg);
|
||||
}
|
||||
Err(e) => {
|
||||
println!("[Autoconnect] failed with : {:?}", e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn respond(
|
||||
sock: &UdpSocket,
|
||||
input: &[u8],
|
||||
sender: &SocketAddr,
|
||||
cfg: &ProtocolConfig,
|
||||
) -> io::Result<()> {
|
||||
if input == b"TextureSync" {
|
||||
sock.send_to(&u16::to_be_bytes(cfg.port), sender)?;
|
||||
println!("[Autoconnect] Got request from : {}", sender);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
@ -16,8 +16,9 @@ where
|
||||
// set timeouts.
|
||||
// We ignore errors here, so they will be caught in the clients thread.
|
||||
let _ = connection.as_mut().map(|stream| {
|
||||
stream.set_read_timeout(Duration::from_secs(config.read_timeout_s).into())?;
|
||||
stream.set_write_timeout(Duration::from_secs(config.write_timeout_s).into())?;
|
||||
let _ = stream.set_read_timeout(Duration::from_secs(config.read_timeout_s).into());
|
||||
let _ = stream.set_write_timeout(Duration::from_secs(config.write_timeout_s).into());
|
||||
stream
|
||||
});
|
||||
|
||||
let handler = handler.clone();
|
||||
|
@ -1,6 +1,9 @@
|
||||
mod implementation;
|
||||
|
||||
pub use self::implementation::*;
|
||||
|
||||
mod autoconnect;
|
||||
pub use self::autoconnect::start_autoconnect_server_async;
|
||||
|
||||
use std::fmt::Display;
|
||||
|
||||
use crate::model::*;
|
||||
@ -53,6 +56,7 @@ pub struct ProtocolConfig {
|
||||
pub read_timeout_s: u64,
|
||||
pub write_timeout_s: u64,
|
||||
pub listen_addr: String,
|
||||
pub autoconnect_multicastv6_addr: String,
|
||||
}
|
||||
|
||||
impl ProtocolConfig {
|
||||
@ -65,9 +69,10 @@ impl Default for ProtocolConfig {
|
||||
fn default() -> ProtocolConfig {
|
||||
ProtocolConfig {
|
||||
port: 10796,
|
||||
read_timeout_s: 60,
|
||||
read_timeout_s: 3 * 600, // 30 Min.
|
||||
write_timeout_s: 75,
|
||||
listen_addr: "::".to_owned(),
|
||||
autoconnect_multicastv6_addr: "ff02::dd42:c0fe".to_owned(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ impl QueryFilter {
|
||||
|
||||
SpecialInName(name) => Score::RequiredMatch(
|
||||
//
|
||||
texture.name.contains(name),
|
||||
texture.name.to_lowercase().contains(&name.to_lowercase()),
|
||||
),
|
||||
|
||||
SpecialBeforeDate(date) => Score::RequiredMatch(texture.added_on <= *date),
|
||||
|
2
testdata/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
bin
|
||||
outputdir
|
22
testdata/create.sh
vendored
Executable file
@ -0,0 +1,22 @@
|
||||
#!/bin/bash
|
||||
|
||||
# use the correct working directory
|
||||
cd $(dirname "$0")
|
||||
|
||||
# compile java code
|
||||
mkdir -p bin
|
||||
javac -sourcepath src -d bin src/creation/Creator.java
|
||||
|
||||
# recreate output dir
|
||||
rm -rf outputdir
|
||||
mkdir -p outputdir/data/textures
|
||||
|
||||
# create textures
|
||||
java -classpath bin creation.Creator
|
||||
|
||||
# place a server binary in the output directory
|
||||
(
|
||||
cd ../server/texture-sync-server
|
||||
cargo build --release
|
||||
)
|
||||
cp ../server/texture-sync-server/target/release/texture-sync-server ./outputdir/
|
184
testdata/src/creation/Creator.java
vendored
Normal file
@ -0,0 +1,184 @@
|
||||
package creation;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.file.Files;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
public class Creator {
|
||||
public static void main(String[] args) {
|
||||
Random r = new Random(1);
|
||||
|
||||
String basedir = "outputdir/data";
|
||||
String[] names = NameCreator.generateNames(r);
|
||||
|
||||
try {
|
||||
FileOutputStream collectionOutput = new FileOutputStream(basedir + "/collection.json");
|
||||
PrintWriter collectionWriter = new PrintWriter(collectionOutput);
|
||||
collectionWriter.write("{\n \"textures\": [\n ");
|
||||
boolean first = true;
|
||||
int i=1;
|
||||
for (String name : names) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
collectionWriter.write(",\n ");
|
||||
}
|
||||
System.out.println("Creating " + name + " (texture " + i + " of " + names.length + ")");
|
||||
storeImage(name, basedir, r, collectionWriter);
|
||||
i++;
|
||||
}
|
||||
collectionWriter.write("\n ]\n}\n");
|
||||
collectionWriter.close();
|
||||
collectionOutput.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean randBool(Random r, double probability) {
|
||||
return r.nextDouble() < probability;
|
||||
}
|
||||
|
||||
private static void storeImage(String name, String outputdir, Random r, PrintWriter collectionWriter)
|
||||
throws IOException {
|
||||
int size = 256 * (1 << r.nextInt(6));
|
||||
NamedColor[] shuffling = Dictionary.colors.clone();
|
||||
NamedColor[] mycolors = new NamedColor[3];
|
||||
for (int i = 0; i < 3; i++) {
|
||||
int otherPos = r.nextInt(7 - i) + i;
|
||||
mycolors[i] = shuffling[otherPos];
|
||||
shuffling[otherPos] = shuffling[i];
|
||||
}
|
||||
MyDate date = randomDate(r);
|
||||
|
||||
boolean grey = randBool(r, 0.01);
|
||||
boolean border = randBool(r, 0.1);
|
||||
boolean bold = randBool(r, 0.03);
|
||||
boolean italic = randBool(r, 0.07);
|
||||
BufferedImage img = createImage(size, name, mycolors, date, grey, border, bold, italic);
|
||||
|
||||
File tmpLocation = new File(outputdir + "/tempimage");
|
||||
ImageIO.write(img, name.substring(name.length() - 3), tmpLocation);
|
||||
String hash = hashFile(tmpLocation);
|
||||
String ending = name.substring(name.length() - 3);
|
||||
if (ending.equals("jpg")) {
|
||||
ending = "jpeg";
|
||||
}
|
||||
tmpLocation.renameTo(new File(outputdir + "/textures/" + hash));
|
||||
String tags = mycolors[0].name + "\", \"" + mycolors[1].name + "\", \"" + mycolors[2].name;
|
||||
if (grey) {
|
||||
tags += "\", \"grey";
|
||||
}
|
||||
if (border) {
|
||||
tags += "\", \"border";
|
||||
}
|
||||
if (bold) {
|
||||
tags += "\", \"bold";
|
||||
}
|
||||
if (italic) {
|
||||
tags += "\", \"italic";
|
||||
}
|
||||
collectionWriter.write("{\n");
|
||||
collectionWriter.write(" \"id\": \"" + UUID.randomUUID() + "\",\n");
|
||||
collectionWriter.write(" \"name\": \"" + name + "\",\n");
|
||||
collectionWriter.write(" \"tags\": [\"" + tags + "\"],\n");
|
||||
collectionWriter.write(" \"format\": \"" + ending + "\",\n");
|
||||
collectionWriter.write(" \"resolution\": [" + size + ", " + size + "],\n");
|
||||
collectionWriter.write(" \"added_on\": " + date.asJsonArray() + ",\n");
|
||||
collectionWriter.write(" \"texture_hash\": \"" + hash + "\"\n");
|
||||
collectionWriter.write(" }");
|
||||
}
|
||||
|
||||
private static MyDate randomDate(Random r) {
|
||||
int back = (int) (-300 * Math.log(r.nextDouble()));
|
||||
return new MyDate(3, 6, 2019).ago(back);
|
||||
}
|
||||
|
||||
private final static char[] hexArray = "0123456789ABCDEF".toCharArray();
|
||||
|
||||
public static String bytesToHex(byte[] bytes) {
|
||||
char[] hexChars = new char[bytes.length * 2];
|
||||
for (int j = 0; j < bytes.length; j++) {
|
||||
int v = bytes[j] & 0xFF;
|
||||
hexChars[j * 2] = hexArray[v >>> 4];
|
||||
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
|
||||
}
|
||||
return new String(hexChars);
|
||||
}
|
||||
|
||||
private static String hashFile(File f) throws IOException {
|
||||
byte[] data = Files.readAllBytes(f.toPath());
|
||||
try {
|
||||
MessageDigest hash = MessageDigest.getInstance("SHA-256");
|
||||
byte[] hashval = hash.digest(data);
|
||||
return bytesToHex(hashval);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static BufferedImage createImage(int size, String name, NamedColor[] colors, MyDate date, boolean grey,
|
||||
boolean border, boolean bold, boolean italic) {
|
||||
BufferedImage img = new BufferedImage(size, size, BufferedImage.TYPE_INT_RGB);
|
||||
Graphics g = img.getGraphics();
|
||||
|
||||
// background
|
||||
if (grey) {
|
||||
g.setColor(Color.LIGHT_GRAY);
|
||||
} else {
|
||||
g.setColor(Color.WHITE);
|
||||
}
|
||||
g.fillRect(0, 0, size, size);
|
||||
|
||||
// border
|
||||
if (border) {
|
||||
int w = size / 40;
|
||||
int l = size - 2*w;
|
||||
g.setColor(Color.BLACK);
|
||||
g.fillRect(w, w, l, w);
|
||||
g.fillRect(w, w, w, l);
|
||||
g.fillRect(w, l, l, w);
|
||||
g.fillRect(l, w, w, l);
|
||||
}
|
||||
|
||||
// text
|
||||
int fontstyle = Font.PLAIN;
|
||||
if (bold) {
|
||||
fontstyle |= Font.BOLD;
|
||||
}
|
||||
if (italic) {
|
||||
fontstyle |= Font.ITALIC;
|
||||
}
|
||||
Font f = new Font("Arial", fontstyle, size / 10);
|
||||
g.setFont(f);
|
||||
g.setColor(Color.BLACK);
|
||||
int width = g.getFontMetrics().stringWidth(name);
|
||||
int basey = size / 2 + g.getFontMetrics().getAscent();
|
||||
int textheight = g.getFontMetrics().getHeight();
|
||||
g.drawString(name, (size - width) / 2, basey);
|
||||
g.drawString(size + " x " + size, (size - width) / 2, basey + textheight);
|
||||
g.drawString(date.asReadableString(), (size - width) / 2, basey + 2 * textheight);
|
||||
|
||||
// color dots
|
||||
for (int i = 0; i < 3; i++) {
|
||||
NamedColor current = colors[i];
|
||||
g.setColor(current.color);
|
||||
g.fillOval((1 + 3 * i) * size / 10, size * 3 / 10, size / 5, size / 5);
|
||||
}
|
||||
|
||||
return img;
|
||||
}
|
||||
}
|
16
testdata/src/creation/Dictionary.java
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
package creation;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
public class Dictionary {
|
||||
public static final String[] adjectives = { "fast", "slow", "long", "short", "fat", "big", "small", "angry",
|
||||
"awful", "calm", "clever", "crazy", "dirty", "excited", "evil", "kind", "lucky", "poor", "shy", "soft",
|
||||
"tall", };
|
||||
public static final String[] animals = { "bat", "bee", "camel", "cat", "chicken", "cod", "deer", "dog", "duck",
|
||||
"fly", "fox", "frog", "horse", "koala", "lion", "mouse", "owl", "pig", "rabbit", "rat", "tiger", "turtle",
|
||||
"wolf", "zebra" };
|
||||
public static final NamedColor[] colors = { new NamedColor("red", Color.RED),
|
||||
new NamedColor("orange", Color.ORANGE), new NamedColor("yellow", Color.YELLOW),
|
||||
new NamedColor("green", Color.GREEN), new NamedColor("blue", Color.BLUE),
|
||||
new NamedColor("magenta", Color.MAGENTA), new NamedColor("black", Color.BLACK), };
|
||||
}
|
66
testdata/src/creation/MyDate.java
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
package creation;
|
||||
|
||||
public class MyDate {
|
||||
public final int day, month, year;
|
||||
|
||||
public MyDate(int day, int month, int year) {
|
||||
if (!isValid(day, month, year)) {
|
||||
throw new IllegalArgumentException("Invalid date");
|
||||
}
|
||||
this.day = day;
|
||||
this.month = month;
|
||||
this.year = year;
|
||||
}
|
||||
|
||||
public String asJsonArray() {
|
||||
return "[" + year + ", " + month + ", " + day + "]";
|
||||
}
|
||||
|
||||
public String asReadableString() {
|
||||
return twoDigit(day) + "." + twoDigit(month) + "." + twoDigit(year);
|
||||
}
|
||||
|
||||
public MyDate previous() {
|
||||
if (isValid(day - 1, month, year)) {
|
||||
return new MyDate(day - 1, month, year);
|
||||
}
|
||||
for (int possibleDay = 31; possibleDay >= 28; possibleDay--) {
|
||||
if (isValid(possibleDay, month - 1, year)) {
|
||||
return new MyDate(possibleDay, month - 1, year);
|
||||
}
|
||||
}
|
||||
return new MyDate(31, 12, year - 1);
|
||||
}
|
||||
|
||||
public MyDate ago(int number) {
|
||||
MyDate d = this;
|
||||
for (int i=0; i<number; i++) {
|
||||
d = d.previous();
|
||||
}
|
||||
return d;
|
||||
}
|
||||
|
||||
private static String twoDigit(int x) {
|
||||
String s = ""+x;
|
||||
while (s.length() < 2) {
|
||||
s = "0"+s;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
private boolean isValid(int day, int month, int year) {
|
||||
int[] monthLengths = new int[] { 31, isLeapYear(year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
|
||||
if (month < 1 || month > 12) {
|
||||
return false;
|
||||
}
|
||||
int length = monthLengths[month - 1];
|
||||
if (day < 1 || day > length) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isLeapYear(int year) {
|
||||
return (year % 4 == 0) && (year % 100 != 0) || (year % 400 == 0);
|
||||
}
|
||||
}
|
35
testdata/src/creation/NameCreator.java
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
package creation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Random;
|
||||
|
||||
public class NameCreator {
|
||||
private static void shuffle(ArrayList<String> list, Random r) {
|
||||
for (int low=0; low<list.size()-1; low++) {
|
||||
int high = r.nextInt(list.size() - low) + low;
|
||||
|
||||
// swap
|
||||
String sLow = list.get(low);
|
||||
String sHigh = list.get(high);
|
||||
list.set(low, sHigh);
|
||||
list.set(high, sLow);
|
||||
}
|
||||
}
|
||||
|
||||
public static String[] generateNames(Random r) {
|
||||
ArrayList<String> names = new ArrayList<>();
|
||||
for (String ending : new String[] {"jpg", "png"}) {
|
||||
for (String animal : Dictionary.animals) {
|
||||
for (String adjective : Dictionary.adjectives) {
|
||||
String name = adjective + "-" + animal + "." + ending;
|
||||
names.add(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
shuffle(names, r);
|
||||
while (names.size() > 1000) {
|
||||
names.remove(names.size() - 1);
|
||||
}
|
||||
return names.toArray(new String[1000]);
|
||||
}
|
||||
}
|
13
testdata/src/creation/NamedColor.java
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
package creation;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
public class NamedColor {
|
||||
public final String name;
|
||||
public final Color color;
|
||||
|
||||
public NamedColor(String name, Color color) {
|
||||
this.name = name;
|
||||
this.color = color;
|
||||
}
|
||||
}
|