Giter Club home page Giter Club logo

ktx's Introduction

GitHub Build Kotlin libGDX Maven Central

KTX

Kotlin extensions for libGDX.

Table of contents

Introduction

KTX is a Kotlin game framework extending libGDX. It aims to make libGDX as Kotlin-friendly as possible without completely rewriting the API. It provides modular utilities and extensions for selected parts of libGDX with poor Kotlin support.

Examples of Kotlin language features used to improve usability, performance, and readability of libGDX include:

  • Operator overloads for collections and mathematical operations.
  • Extension methods expanding and improving the original libGDX APIs without the use of inheritance.
  • Inline methods with reduced runtime overhead for various listeners, builders, and loggers.
  • Nullable types which improve typing information of selected interfaces and functions.
  • Default parameters reducing boilerplate code and providing sensible defaults for various operations.
  • Type-safe builders for GUI, interface styling, ECS, and physics engine setup.
  • Default interface methods simplifying their implementation.
  • Coroutines context providing concurrency utilities and non-blocking asset loading.
  • Reified types simplifying usage of methods normally consuming Class parameters.

See the Choosing KTX article for pros and cons of this framework.

Modules

KTX was designed to be modular from day one. In fact, some of its libraries consist of just a single Kotlin file. You can include the selected KTX modules based on the needs of your application.

Module Description
ktx-actors Scene2D GUI extensions for stages, actors, actions, and event listeners.
ktx-ai Type-safe Kotlin builders and utilities for gdxAI.
ktx-app ApplicationListener implementations and general application utilities.
ktx-artemis Artemis-odb entity-component-system utilities.
ktx-ashley Ashley entity-component-system utilities.
ktx-assets Resources management utilities.
ktx-assets-async Non-blocking asset loading using coroutines.
ktx-async Coroutines context based on libGDX threading model.
ktx-box2d Box2D physics engine utilities.
ktx-collections Extensions for libGDX custom collections.
ktx-freetype FreeType fonts loading utilities.
ktx-freetype-async Non-blocking FreeType fonts loading using coroutines.
ktx-graphics Utilities related to rendering tools and graphics.
ktx-i18n Internationalization API utilities.
ktx-inject A dependency injection system with low overhead and no reflection usage.
ktx-json Utilities for libGDX JSON serialization API.
ktx-log Minimal runtime overhead cross-platform logging using inlined functions.
ktx-math Operator functions for libGDX math API and general math utilities.
ktx-preferences Improved API for accessing and saving preferences.
ktx-reflect Utilities for libGDX reflection API.
ktx-scene2d Type-safe Kotlin builders for Scene2D GUI.
ktx-script Kotlin scripting engine for desktop applications.
ktx-style Type-safe Kotlin builders for Scene2D widget styles extending Skin API.
ktx-tiled Utilities for Tiled maps.
ktx-vis Type-safe Kotlin builders for VisUI.
ktx-vis-style Type-safe Kotlin builders for VisUI widget styles.

Installation

New projects

New projects with support for KTX can be generated with the gdx-liftoff tool. In contrary to the official gdx-setup tool, gdx-liftoff provides greater support for libGDX extensions and a wider set of platforms, as well as custom project templates. You can download the latest release of the tool here.

Click on the sections below for instructions on how to set up a new KTX project with gdx-liftoff.

General

Fill the basic information about your project such as its name, root package or main class name. Provide an empty folder to generate the project into. If you want to target the Android platform, define the path to the Android SDK.

The following sections describe each tab of the setup tool available below the basic project info.


Platforms

KTX supports the following platforms:

  • Core: mandatory shared module.
  • Desktop: the default desktop platform based on LWJGL3.
  • Android: native Android mobile platform.
  • iOS: mobile platform using RoboVM to support iOS.
  • HTML (TeaVM): unofficial experimental web platform using TeaVM.
  • Headless: a desktop platform without a graphical interface.
  • Server: a separate server application without libGDX APIs.
  • Shared: a module for sharing code between the Server and Core.

The following platforms are unsupported or untested:

  • HTML: the default web platform. Supports only Java projects.
  • Desktop (Legacy): legacy desktop platform built upon LWJGL2. Might not work with modern JVMs.
  • iOS Multi-OS Engine: an alternative iOS platform. Untested.

Languages

You can select the Kotlin language support to ensure it is correctly set up in the generated project. If a Kotlin project template is selected, it will automatically add the necessary Kotlin libraries and plugins.


Extensions

This section includes the official libGDX extensions. Each of these should be compatible with Kotlin projects. However, some extensions might be unavailable on specific platforms. In particular, the TeaVM backend might be unable to compile libraries relying on native code or reflection.


Third-party extensions

This section contains all verified third-party extensions for libGDX. All KTX modules are listed in this tab.

To include a KTX module, scroll down to the KTX libraries list and click on the corresponding checkbox. This will ensure that the module is properly installed and includes all of its dependencies in the latest available versions.

The gdx-liftoff tool will also set up a Gradle property named ktxVersion that will be shared across all KTX libraries. To upgrade your project after a KTX release, update to the latest version in the gradle.properties file.


Templates

Choosing a template for the project determines the initial implementation of the libGDX ApplicationListener, as well as the application launchers on each platform. Some templates also showcase specific parts of the framework, such as the Scene2D GUI or event handling. You can generate several projects and check out various templates, but for working with Kotlin and KTX these are the best starting points:

  • Kotlin: a basic project template that generates Kotlin application launchers.
  • Kotlin Logo: a simple project that generates Kotlin application launchers and draws the libGDX logo on the screen.
  • Kotlin + KTX (recommended): a project template that generates the ApplicationListener using KTX utilities. When launched, the application draws the KTX logo on the screen. Some modules that require additional setup, such as ktx-async, are properly initiated by the template if selected.

Advanced

This section can be used to specify versions of core dependencies. If you are just starting with libGDX, these settings can be mostly left untouched. However, if you wish to have a basic Scene2D GUI Skin that you can use to test the available widgets, mark the Add GUI assets checkbox.


Example KTX projects:

  • ktx-sample-project: includes all KTX modules and the official libGDX extensions. Targets the desktop and mobile platforms.
  • ktx-sample-web-project: includes most KTX modules that are at least partially supported by the web platform, as well as the official libGDX extensions. Targets the desktop, mobile and web platforms.

When using the official gdx-setup tool instead of the recommended gdx-liftoff, generate a project with Kotlin support and refer to the next section.

Existing projects

KTX libraries can be added to existing Kotlin libGDX projects. Please refer to the libGDX wiki for more information on how to add Kotlin support to a libGDX application.

All KTX modules are uploaded to Maven Central and are fully compatible with the Gradle build tool, which is used in libGDX projects by default.

The libraries are published under the io.github.libktx group and are named with the ktx- prefix. You can find a complete list of KTX modules in the previous section. As an example, including the app module with the ktx-app identifier would require the following changes in your build.gradle or build.gradle.kts file:

build.gradle Gradle Groovy DSL
// Groovy DSL:
ext {
  // Update this version to match the latest KTX release:
  ktxVersion = '1.12.1-rc1'
}

dependencies {
  api group: 'io.github.libktx', name: 'ktx-app', version: ktxVersion
}
build.gradle.kts Gradle Kotlin DSL
// Update this version to match the latest KTX release:
val ktxVersion = "1.12.1-rc1"

dependencies {
  api(group = "io.github.libktx", name = "ktx-app", version = ktxVersion)
}

KTX modules should generally be added to the dependencies of the shared core module of your libGDX application.

You can find the latest KTX version on Maven Central:

Maven Central

Platforms

KTX currently supports the following platforms:

Platform Status Description
Desktop Complete All major desktop platforms are supported by the official libGDX LWJGL3 backend.
Android Complete Supported natively by the official libGDX Android backend.
iOS Complete Supported by the official libGDX iOS backend using RoboVM.
Web Experimental Partially supported by the unofficial web backend using TeaVM.

Note that platforms other than desktop might provide limited support for features such as reflection, coroutines or Java standard library emulation. In particular, mobile platforms might not support the entire Java standard library including the newer additions, while the web platform currently does not support Kotlin coroutines or more advanced reflection features. Please refer to the documentation of the respective libGDX backends, as well as the tools that they are based on.

Versioning

Each KTX version is based on the matching libGDX release. KTX uses suffixes to differentiate multiple releases made against a single libGDX version. The -rc suffix is reserved for stable releases.

Unfortunately, libGDX does not follow the semantic versioning guidelines. Both minor and patch versions can introduce breaking changes. Please read the libGDX and KTX change logs before updating. When choosing the appropriate KTX version, always pick the latest release matching your current libGDX version.

You can browse through our official releases on Maven and on GitHub.

Although KTX technically uses beta release tags, the official releases are considered suitable for production use. All modules are thoroughly tested with comprehensive test suites.

Latest changes

The master branch is the default branch of the repository. It represents the latest stable release of KTX. It ensures that the documentation in the repository is in sync with the latest released version.

The newest changes can be found on the develop branch instead.

The preview snapshot releases with the latest changes are uploaded automatically to the https://oss.sonatype.org/content/repositories/snapshots/ repository. To use them in your application, add the following Maven repository, and change the suffix of the KTX version to -SNAPSHOT:

build.gradle Gradle Groovy DSL
repositories {
  // Include your other repositories here.
  maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
}

ext {
  // Update this version to match the latest libGDX release:
  ktxVersion = '1.12.1-SNAPSHOT'
}
build.gradle.kts Gradle Kotlin DSL
repositories {
  // Include your other repositories here.
  maven("https://oss.sonatype.org/content/repositories/snapshots/")
}

// Update this version to match the latest libGDX release:
val ktxVersion = "1.12.1-SNAPSHOT"

The full version of the latest snapshot release can be found on the develop branch, and usually matches the latest stable libGDX release. Snapshot releases for the nightly libGDX builds are not available.

Note that even the snapshots are rather stable, as the libraries are not pushed to Maven Central unless they pass their extensive test suites. However, the public APIs in snapshot libraries might be changed prior to a stable release.

Documentation

Official guides

Each module contains a README.md file with a list of all its features and a guide with useful code snippets. Browse through the directories in the root folder to find out more about each library.

Source documentation

All functionalities are documented with Kotlin KDocs. You can access the source documentation by:

  • Viewing the generated Dokka files hosted on the project website.
  • Extracting the doc folders with Dokka files from the release archives.
  • Reading the sources directly.

Links

KTX wiki lists some useful resources that can help you get started.

Most official guides and code examples in this repository assume that the reader is at least a bit familiar with the libGDX API. If you are just getting to know the framework, it might be helpful to go through the official libGDX wiki, and convert some Java examples to Kotlin.

Suggestions, questions, typo fixes, documentation improvements and code contributions are always welcome.

Do not hesitate to start a discussion with questions about the framework. Feel free to advertise your KTX project, propose new features, discuss game jams, or even create a personal devlog.

If you would like to contribute, please read the contribution guideline, and browse through the active issues. The develop is the active development branch. When creating pull requests, make sure to choose develop as the target branch.

You can check the list of the contributors via GitHub insights or the contributors list.

Licensing

This project is dedicated to public domain.

Working from sources

See this section of the contribution guideline to get started.

ktx's People

Contributors

crykn avatar cypherdare avatar czyzby avatar deviodesign avatar divelix avatar dwursteisen avatar elect86 avatar fejd avatar focuspo1nt avatar jakewilson avatar jcornaz avatar jkly avatar kali-dali avatar kanpov avatar keturn avatar kotcrab avatar kvonspiczak avatar maltaisn avatar manabreak avatar megamiun avatar mmilenkov avatar mrplow442 avatar nukesz avatar proorange avatar pylvain avatar quillraven avatar sreich avatar tomgrill avatar vrudas avatar yhslai avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ktx's Issues

Logging utilities

  • Global logging methods.
  • Logger implementation.
  • Unit tests.

Thanks to inline functions, Kotlin can make it possible to use nearly-zero overhead logging mechanism that does not rely on varargs to provide string templates. The existing solution is - unfortunately - not compatible with LibGDX logging.

Release 1.9.6-b3

  • Upload to Maven Central.
  • Create a new GitHub release.
  • Extract library version to a separate file.


The next planned release aims to clean up tests code style, remove deprecated utilities and add support for Box2D and coroutines-based asset loading.

Application utilities module

  • ApplicationAdapter equivalent.
  • Game/Screen equivalent. This turns out more opinionated than it should be, it basically forces the developer to use Scene2D - preventing from basing everything on an entity system or using a different screen transition tool.
  • LetterboxingViewport.
  • Documentation (README, guide, usage examples).
  • Unit tests.

LibGDX default ApplicationListener implementations are pretty basic and often force the user to set up boilerplate code like screen clearing or fixed rendering time step manually. This is desired for some advanced users, but most would like to focus on the game logic itself. A more advanced ApplicationListener implementation that handles smooth view transitions and manages Stage with a SpriteBatch could be very useful.

Documentation improvements

  • Contributing guideline.
  • Links to related LibGDX articles.
  • Gradle/Maven dependencies info.

Basically, all KTX guides assume prior knowledge of LibGDX API. Some people are likely to get there because of Kotlin rather LibGDX. It would be nice to include some links to the original LibGDX APIs explanations that are being improved by each KTX module - for example, ktx-scene2d could link to Scene2D wiki article.

Also, there is no CONTRIBUTING.md file and Maven dependencies data in existing README files.

Coroutines support

  • Add ktx-async module.
  • Implement coroutine context for LibGDX applications that executes tasks on the main rendering thread, using Gdx.app.postRunnable to resume coroutines executed on other threads.
  • Implement utility functions for asynchronous tasks based on coroutines executed on a configurable AsyncExecutor instance.
  • Add HttpRequest utilities based on coroutines.
  • Simplify LibGDX Timer API. Currently it does not support lambdas.
  • Add suspending delay method, which does not block the main rendering thread using LibGDX Timer.
  • skipFrame suspending method that resumes on the next frame using Gdx.app.postRunnable API.
  • Implement tasks cancellation.
  • Documentation with usage examples.

Implement ktx-async module with asynchronous operations utilities and Kotlin 1.1 coroutines support.

improving scene2d label,textbutton property notation for setting text

this is something that's been bothering me...

so you know how kotlin transforms get/set methods from java into property accessors. well, one place this screws up is in label and textbutton of scene2d.

label.text = "test" // won't work, can't assign string to stringbuilder

this exists because label has getText():StringBuilder, and setText(String)..which is a bit strange of an api to begin with.

in other words, from what i've seen, your only way of setting text is:

label.setText("")

the only workaround i could think of is something like this...(both textbutton and label are affected, not sure what else..haven't looked

private var Label.label: String
    get() = text.toString()
    set(str) = setText(str)
private var TextButton.label: String
    get() = text.toString()
    set(str) = setText(str)

got any better ideas? i really don't like the method syntax, property syntax is much more straightforward esp when everything else uses it (consistency). i have yet to find other properties that have similar problems.

Expose KotlinApplication#timeSinceLastRender to allow interpolation/extrapolation

KotlinApplication's implementation of fixed timestep doesn't take interpolation/extrapolation between frames into account. For example, if the fixedTimeStep is 0.05, but the first Gdx.graphics.rawDeltaTime is 0.08, then it only calls render(0.05) for one time. In the other words, it renders the state of the game at 0.05s, at the 0.08s of the real world.

It's not that bad, but we can at least let the user decide whether he wants interpolation/extrapolation for a smoother result.

Since KotlinApplication doesn't expose timeSinceLastRender, I have to copy&paste its code and implement my own ApplicationListener this way:

  override final fun render() {
    timeSinceLastRender = Math.min(timeSinceLastRender + Gdx.graphics.rawDeltaTime, maxDeltaTime)
    while (timeSinceLastRender >= fixedTimeStep) {
      timeSinceLastRender -= fixedTimeStep
      clearScreen(0f, 0f, 0f, 1f)
      update(fixedTimeStep)
    }
    smoothRender(timeSinceLastRender / fixedTimeStep) // Extrapolation
  }

Upload 1.9.4-b1 to Maven Central

  • Upload 1.9.4-b1 version to Maven Central.
  • Update info and add "shield" to README.md with current version.
  • Add zipped data to releases.

Travis CI integration

  • Automatic snapshot uploads.
  • Pull request verification.
  • master branch testing.
  • Dokka documentation publishing.

Javadocs and Dokka documentation

Standard Gradle javadoc task fails to build a proper Javadoc archive. However, we can use Dokka to generate Javadocs from Kotlin sources. It would be nice to automatically generate Dokka documentation and post it with GitHub Pages using the gh-pages branch after every push to master.

Skin utilities module

  • Type-safe builders for Scene2D widgets styles.
  • Utility functions that make accessing and modifying Skin assets easier.
  • Unit tests.

ktx-style would a type-safe alternative to constructing Scene2D styles with error-prone JSON files.

Explore Kotlin 1.1 features

  • Builder scoping DSL could greatly improve Scene2D and VisUI building APIs. #44 #46
  • Builder scoping DSL for Skin building methods. #45
  • Type aliases for LibGDX collections could help avoiding collisions with Kotlin classes (Array, for example), as well as providing class names consistent with the factory methods.
  • Coroutines could be potentially used for asset loading and delegating tasks to main loop (similarly to official Swing utilities library). #41 #47
  • Some properties might be inlined (especially extensions).

Math module implementation

Hi guys,

I am a little bit confused about the implementation of the math module.
Applying operators on vectors/matrices does not behave as expected since the result which gets returned is the left handed variable instead of a new vector/matrix.
For example:

val x = Vector2(1f, 1f)
val y = x * 2f

will get y the correct result and x won't be Vector2(1f, 1f) anymore but Vector2(2f, 2f), i.e. x == y is true.

To bypass that problem you would do something like this: val y = Vector2(x) * 2f so x keeps it's original values.

I understand that the operators are simple aliases for the Vector/Matrix methods and the gdx implementation does not return new instances for obvious performance reasons.

So do you think this problem could be solved with some kind of pooling to prevent performance issues?
I would appreciate at least some kind of flag, which can be set for the math module so new instances get returned by default.

-Trixt0r

Math support

  • Overloaded operators and simple factory methods.
    • Vector2.
    • Vector3.
    • Matrix3.
    • Matrix4.
  • Global methods for random numbers using MathUtil (?).

Merging LibGDX utilities from Ore Infinium

  • Common LibGDX interfaces adapters.
  • Math extensions.
  • ktx-graphics? Utilities for Batch and ShaderProgram in ktx-app.

i'm gonna find some time to push some of my helper functions from my game upwards into here so other people can benefit from my headaches.

some of them are rather useful, some of them are useless even for me ( experiments ๐Ÿ˜‰ )

see here https://github.com/sreich/ore-infinium/blob/master/core/src/com/ore/infinium/util/LibgdxExtensions.kt

i also have artemis ones that would be useful(delegate system initters and some other things), but i'll push that to the backburner because i want to see where i go with those

mostly the interface ones of libgdx, but there's a few others that might be useful to someone other than my crazy self, we'll have to see

the interfaces exist because if you have to inherit from it or create an object of it, it's waaay easier. since libgdx doesn't use default interface java keyword, meaning you have to implement everything. it also gives guarantees on nullability which i found annoying for some of them (like Actor? but turns out it's never not null, so my kotlin wrappers give you that info back) and so on.

i'm sure i'll find ones in the future as i'm working and move them upwards where it makes sense.

there's also a couple in here https://github.com/sreich/ore-infinium/blob/master/core/src/com/ore/infinium/util/KotlinExtensions.kt (obvs ignore the ones that are clearly useless. mutableListOfNulls actually made it into stdlib in a form recently, i believe)

ones of interest are probably

  • interfaces
  • random.nextint(range)
    *shaderprogram.use {}
    *Texture.flipx, flipy
    ...

any thoughts? care to take a glance and call out ones you think others might find useful, or use my best judgement and give a PR awaiting feedback on it?

Release 1.9.4-b2

  • Upload to Maven Central.
  • Add to releases section.
  • Update gdx-setup.
  • Update change log.

clearscreen (glclear)

thinking clearscreen should instead have an optional arg for opacity. often times you want to set the opacity (fbos especially) and cannot do that. adding it as an optional arg should maintain BC (though i suppose that doesn't matter since ktx is not 1.0 yet)

Static instances removal

  • I18n bundle - with idiomatic Kotlin NLS enums (see README file), the static nls methods are not as useful.
  • Dependency injection context - after full context initiation, context should not be accessed directly. Dynamically created instances should be accessible through injected providers.
  • Asset manager - with dependency injection from ktx-inject, static access to resources can be harmful and hide code dependencies. Fully loaded assets should be passed directly to the objects and functions that depend on them.

  • Add deprecation warnings to the mentioned instances and functions that rely on them.
  • Provide alternative utility methods for AssetManager usage, allowing the users to migrate to the new API before the next release.
  • Rewrite ktx-i18n documentation.
  • Rewrite ktx-inject documentation.

Most static variables are pretty much justified and optional to use. For example, you can still benefit from gdx-assets utilities without the global AssetManager, but most applications will want to use only a single one anyway.

I don't think the global Skin variable should be removed from ktx-scene2d though, as it greatly simplifies and improves readability of UI building methods.

@kotcrab @sreich @MrPlow442 I'd love to hear your thoughts on this.

KTX tools module

An equivalent to gdx-tools that could automate some KTX-related operations. Potential candidates:

  • Conversion of .properties files to type-safe i18n enums from ktx-i18n.
  • Conversion of skin .json definition to type-safe builders from ktx-style.
  • Generating type-safe ktx-style builders from an existing Skin instance.
  • Extraction of texture region names from .atlas files, which could be saved as a type-safe enum. Useful for both ktx-style builders and general TextureAtlas usage.

Could be implemented as standalone library or Gradle plugin. Or both. The first approach seems the most flexible.

@kotcrab

Collections support

  • Array utilities: factory methods, extensions, Iterable/kotlin.Array conversions.
  • ObjectSet utilities: factory methods, extensions, Iterable/kotlin.Array conversions.
  • ObjectMap utilities.
    • Iterable/kotlin.Array conversions.
    • Factory methods.
    • Extension utility methods.
    • Support for map[key] = value operator.
  • List utilities.
    • PooledList implementation: PooledLinkedList done right.
  • Unit tests.
    • Arrays.
    • Sets.
    • Maps.
    • Lists.

Update contribution guide

Contribution guide should contain a note about updating the changelog file and README of the modified project.

Box2D utilities module

  • Review gdx-box2d-kotlin code.
  • Migrate gdx-box2d-kotlin to ktx-box2d module.
  • FixtureDef factory methods supporting different shapes.
  • earthGravity property.
  • World factory methods.
  • Documentation listing all extensions. Usage examples.
  • Provide an easy way to access built Fixture instances.
  • Improve FixtureDef.filter API.
  • Address FixtureDef.shape issue.
  • Add userData properties to extended body and fixture definitions.

@Jkly

Assets support

  • Global AssetManager container.
  • Strongly-typed utility functions for easy asset loading.
  • AssetDescriptor building utilities.
  • Easy conversion of strings to FileHandle instances (an alternative to a rather obscure Gdx.files.internal and so on).
  • Disposable utilities.
  • Pool utilities.
  • Unit tests.
    • Disposable.
    • Pool.
    • Asset loading.
    • FileHandle.

Scene2D support

  • Interfaces and abstract bases.
    • KWidget (for all parental widgets)
    • KTable (for Table-extending widgets)
    • KTree (for widgets operating on Tree.Node instances)
    • KGroup (for regular WidgetGroup actors)
  • Factory methods of root actors.
    • Table-based (Table, Window, Dialog, ButtonGroup wrapper)
    • Tree
    • WidgetGroup-based (most other parental actors)
  • Kotlin type-safe builders for Scene2D widgets.
    • Non-parental actors.
      • Actor (should we even support this one?)
      • Image
      • Label
      • List (it's not exactly parental, a simple text adding extension method should be enough)
      • ProgressBar
      • SelectBox (similarly to list)
      • Slider
      • TextArea
      • TextField
      • Touchpad
    • Parental actors.
      • ButtonGroup
      • Button
      • CheckBox
      • Container
      • Dialog
      • HorizontalGroup
      • ImageButton
      • ImageTextButton
      • ScrollPane
      • SplitPane
      • Stack
      • Table
      • TextButton
      • Tree (hierarchies should be supported somehow, each actor in a tree hierarchy might be a node)
      • VerticalGroup
      • Window
    • Special-case actors.
      • TextTooltip
      • Tooltip
  • README - overview of the supported features.
    • Guide.
    • Usage examples.
    • Misc.
  • Units tests.

Actors extensions

  • Actor extension methods for common listeners.
  • Actor.isShown()
  • Operators overloading for adding actors to stages and groups.
  • Actions chaining methods (then?) which automatically create action sequences.
  • Operators overloading for actions.
  • Unit tests.
    • Actors.
    • Listeners.
    • Actions.

Refactoring Vis module

This is attempt to improve current Vis builder API. Current implementation when building actors that belong to Table-type widget will return Cell<Actor> which is convenient if you need to modify cell properties but most of the time you also need to get Actor itself which requires not so convenient .actor call.
New API will introduce .cell {} that will allow to modify Cell properties but still return Actor.

This requires to re-purpose Actor.userObject for storing Cell. However .cell {} syntax is only valid in UI builder, allowing user to re user field later. Also, fallback method searching actor hierarchy search would be implemented.

.cell {} syntax is only valid in current DSL block thanks to two things:

  • only parental Actors that will put Cell instance in Actor.userObject provide API to call .cell{} (thanks to possibility to put extension methods in interface)
  • nested DSL blocks won't be able to call it thanks to @DslMarker annotation

This is simple subset of current API with those changes implemented. Notice that there is only one WidgetFactory interface for both Table-type and standard WidgetGroup actors. Usage of @DslMarker requires to extend all widget classes such as VisLabel, that was not needed before.

inline fun table(init: KVisTable.() -> Unit): Table = actor(KVisTable(), init)

inline fun horizontalGroup(init: KHorizontalGroup.() -> Unit): KHorizontalGroup = actor(KHorizontalGroup(), init)

inline fun <T : Actor> actor(actor: T, init: T.() -> Unit): T {
    actor.init()
    return actor
}

@VisDslMarker
class KVisLabel(text: CharSequence, styleName: String) : VisLabel(text, styleName)

@VisDslMarker
class KVisTable : VisTable(false), WidgetFactory, CellAccessor {
    override fun <T : Actor> addActorToWidgetGroup(actor: T): T {
        val cell = add(actor)
        actor.userObject = cell
        return actor
    }
}

@VisDslMarker
class KHorizontalGroup : HorizontalGroup(), WidgetFactory {
    override fun <T : Actor> addActorToWidgetGroup(actor: T): T {
        addActor(actor)
        return actor
    }
}

interface CellAccessor {
    fun <T : Actor> T.cell(): Cell<T> {
        return this.userObject as Cell<T> //TODO fallback to actor hierarchy search
    }

    fun <T : Actor> T.cell(initCell: Cell<T>.() -> Unit): T {
        cell().initCell()
        return this
    }

    // Alternatively to above
    fun <T : Actor> T.cell(grow: Boolean, padding: Float = 0f /* other fields ...*/): T {
        if (grow) cell().grow()
        cell().pad(padding)
        return this
    }

    fun <T : Actor> T.row(): T {
        cell().row()
        return this
    }


    @Deprecated("Calls to cell() can't be nested", level = DeprecationLevel.ERROR)
    fun <T : Actor> Cell<T>.cell() {
    }

    @Deprecated("Calls to cell() can't be nested", level = DeprecationLevel.ERROR)
    fun <T : Actor> Cell<T>.cell(initCell: Cell<T>.() -> Unit) {
    }
}


interface WidgetFactory {
    fun label(text: String, styleName: String = DEFAULT_STYLE, init: KVisLabel.() -> Unit = {}): VisLabel
            = actor(KVisLabel(text, styleName), init)

    fun table(init: KVisTable.() -> Unit = {}): VisTable = actor(KVisTable(), init)

    fun horizontalGroup(init: KHorizontalGroup.() -> Unit = {}): HorizontalGroup = actor(KHorizontalGroup(), init)

    fun <T : Actor> actor(actor: T, init: T.() -> Unit): T {
        val result: T = addActorToWidgetGroup(actor)
        actor.init()
        return result
    }

    fun <T : Actor> addActorToWidgetGroup(actor: T): T
}

@DslMarker
annotation class VisDslMarker

Usage examples

fun createUI() {
    var extLabel: VisLabel? = null

    table {
        extLabel = label("A")
        val label: VisLabel = label("A") {
            setAlignment(Align.left)
            // cell {} // this is invalid because of DslMarker
        }
        val labelCell: Cell<VisLabel> = label("A").cell() // get cell explicitly
        val label2: VisLabel = label("A").cell { growX() } // modify some values of cell but still get actor
        val label4: VisLabel = label("A").cell(grow = true, padding = 2f) // alternative way to modify cell properties

        horizontalGroup {
            // both invalid because of DslMarker
            // label2.cell()
            // label("A").cell()
        }
    }

    horizontalGroup {
        val label: VisLabel = label("A")
        //label("A").cell() // invalid, IDE won't suggest cell here
    }

    // invalid, IDE won't suggest cell here
    //extLabel.cell()
    //extLabel.cell { growX() }
}

@czyzby Any thoughts, do you see any shortcomings of this approach?

Coroutines-based asset manager

Asynchronous asset loading based on coroutines would both benefit simplify the API usage - it would look as if assets are loaded synchronously - as well as the asset manager internal implementation.


It would be possible to turn this pseudocode example:

fun create() {
  assetManager.apply {
     load<String>("my.txt") // Returns nothing. :C
     load<Texture>("logo.png")
     load<String>("other.txt")
     load<I18NBundle>("i18n.properties")
     // Schedule loading of other assets.
  }
}

fun render() {
  if (assetManager.update()) {
    // Assets loaded!
    finishLoading()
  }
  updateProgressLabel(assetManager.progress)
}

fun finishLoading() {
   assetManager.apply {
     myText = get<String>("my.txt")
     logo = get<Texture>("logo.png")
     otherText = get<String>("other.txt")
     bundle = get<I18NBundle>("i18n.properties")
     // Extract other assets.
  }
  goToNextView()
}

...into this pseudocode example:

fun create() {
  assetManager.apply {
     // Each call suspends and resumes on the rendering thread,
     // never blocking the rendering process.
     myText = load<String>("my.txt")
     logo = load<Texture>("logo.png")
     otherText = load<String>("other.txt")
     bundle = load<I18NBundle>("i18n.properties")
  }
  // Resumes after all assets are loaded:
  goToNextView()
}

fun render() {
  updateProgressLabel(assetManager.statusMessage)
}

@kotcrab Thoughts on this? Should we include coroutines in ktx-assets, add the new manager to ktx-async (general LibGDX asynchronous operations utilities and basic coroutines support) or create a separate module?

I18n support

  • Global nls functions.
  • Utility I18NBundle container.
  • BundleLine interface, providing i18n support with zero boilerplate.
  • Unit tests.

Color utilities

  • color factory method with optional alpha defaulting to 1f.
  • Color.copy extension method, supporting optional values overriding.

ktx-tools gradle plugin buildsystem issues

i'm working on the gradle plugin which will end up running a code generator on whatever we want (my focus is on fonts, sounds, textures not in particular order).

i could use some help on this issue right now. if you check out my fork, https://github.com/sreich/ktx/tree/feature/ktx-tools

running install on ktx-tools fails..not sure why it's failing at that task though. once i get this building a "hello world" then i can begin work on it. i'm inexperienced in gradle, but had it working fine before combining it into ktx-tools.

:tools:dokkaZip
:tools:compileKotlin UP-TO-DATE
:tools:compileJava NO-SOURCE
:tools:copyMainKotlinClasses FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':tools:copyMainKotlinClasses'.
> kotlin.KotlinNullPointerException (no error message)

* Try:
Run with --info or --debug option to get more log output.

* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':tools:copyMainKotlinClasses'.
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:84)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:55)
	at org.gradle.api.internal.tasks.execution.SkipUpToDateTaskExecuter.execute(SkipUpToDateTaskExecuter.java:62)
	at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:58)
	at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:88)
	at org.gradle.api.internal.tasks.execution.ResolveTaskArtifactStateTaskExecuter.execute(ResolveTaskArtifactStateTaskExecuter.java:46)
	at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:51)
	at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:54)
	at org.gradle.api.internal.tasks.execution.ExecuteAtMostOnceTaskExecuter.execute(ExecuteAtMostOnceTaskExecuter.java:43)
	at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:34)
	at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker$1.execute(DefaultTaskGraphExecuter.java:236)
	at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker$1.execute(DefaultTaskGraphExecuter.java:228)
	at org.gradle.internal.Transformers$4.transform(Transformers.java:169)
	at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:106)
	at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:61)
	at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:228)
	at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter$EventFiringTaskWorker.execute(DefaultTaskGraphExecuter.java:215)
	at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.processTask(AbstractTaskPlanExecutor.java:77)
	at org.gradle.execution.taskgraph.AbstractTaskPlanExecutor$TaskExecutorWorker.run(AbstractTaskPlanExecutor.java:58)
	at org.gradle.execution.taskgraph.DefaultTaskPlanExecutor.process(DefaultTaskPlanExecutor.java:32)
	at org.gradle.execution.taskgraph.DefaultTaskGraphExecuter.execute(DefaultTaskGraphExecuter.java:113)
	at org.gradle.execution.SelectedTaskExecutionAction.execute(SelectedTaskExecutionAction.java:37)
	at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:37)
	at org.gradle.execution.DefaultBuildExecuter.access$000(DefaultBuildExecuter.java:23)
	at org.gradle.execution.DefaultBuildExecuter$1.proceed(DefaultBuildExecuter.java:43)
	at org.gradle.execution.DryRunBuildExecutionAction.execute(DryRunBuildExecutionAction.java:32)
	at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:37)
	at org.gradle.execution.DefaultBuildExecuter.execute(DefaultBuildExecuter.java:30)
	at org.gradle.initialization.DefaultGradleLauncher$RunTasksAction.execute(DefaultGradleLauncher.java:256)
	at org.gradle.initialization.DefaultGradleLauncher$RunTasksAction.execute(DefaultGradleLauncher.java:253)
	at org.gradle.internal.Transformers$4.transform(Transformers.java:169)
	at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:106)
	at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:56)
	at org.gradle.initialization.DefaultGradleLauncher.doBuildStages(DefaultGradleLauncher.java:175)
	at org.gradle.initialization.DefaultGradleLauncher.doBuild(DefaultGradleLauncher.java:119)
	at org.gradle.initialization.DefaultGradleLauncher.run(DefaultGradleLauncher.java:102)
	at org.gradle.launcher.exec.GradleBuildController.run(GradleBuildController.java:71)
	at org.gradle.tooling.internal.provider.runner.BuildModelActionRunner.run(BuildModelActionRunner.java:50)
	at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
	at org.gradle.tooling.internal.provider.runner.RunAsBuildOperationBuildActionRunner$1.execute(RunAsBuildOperationBuildActionRunner.java:43)
	at org.gradle.tooling.internal.provider.runner.RunAsBuildOperationBuildActionRunner$1.execute(RunAsBuildOperationBuildActionRunner.java:40)
	at org.gradle.internal.Transformers$4.transform(Transformers.java:169)
	at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:106)
	at org.gradle.internal.progress.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:56)
	at org.gradle.tooling.internal.provider.runner.RunAsBuildOperationBuildActionRunner.run(RunAsBuildOperationBuildActionRunner.java:40)
	at org.gradle.tooling.internal.provider.runner.SubscribableBuildActionRunner.run(SubscribableBuildActionRunner.java:75)
	at org.gradle.launcher.exec.ChainingBuildActionRunner.run(ChainingBuildActionRunner.java:35)
	at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:41)
	at org.gradle.launcher.exec.InProcessBuildActionExecuter.execute(InProcessBuildActionExecuter.java:26)
	at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:75)
	at org.gradle.tooling.internal.provider.ContinuousBuildActionExecuter.execute(ContinuousBuildActionExecuter.java:49)
	at org.gradle.tooling.internal.provider.ServicesSetupBuildActionExecuter.execute(ServicesSetupBuildActionExecuter.java:49)
	at org.gradle.tooling.internal.provider.ServicesSetupBuildActionExecuter.execute(ServicesSetupBuildActionExecuter.java:31)
	at org.gradle.launcher.daemon.server.exec.ExecuteBuild.doBuild(ExecuteBuild.java:67)
	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
	at org.gradle.launcher.daemon.server.exec.WatchForDisconnection.execute(WatchForDisconnection.java:47)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
	at org.gradle.launcher.daemon.server.exec.ResetDeprecationLogger.execute(ResetDeprecationLogger.java:26)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
	at org.gradle.launcher.daemon.server.exec.RequestStopIfSingleUsedDaemon.execute(RequestStopIfSingleUsedDaemon.java:34)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
	at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:74)
	at org.gradle.launcher.daemon.server.exec.ForwardClientInput$2.call(ForwardClientInput.java:72)
	at org.gradle.util.Swapper.swap(Swapper.java:38)
	at org.gradle.launcher.daemon.server.exec.ForwardClientInput.execute(ForwardClientInput.java:72)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
	at org.gradle.launcher.daemon.server.exec.LogAndCheckHealth.execute(LogAndCheckHealth.java:55)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
	at org.gradle.launcher.daemon.server.exec.LogToClient.doBuild(LogToClient.java:60)
	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
	at org.gradle.launcher.daemon.server.exec.EstablishBuildEnvironment.doBuild(EstablishBuildEnvironment.java:72)
	at org.gradle.launcher.daemon.server.exec.BuildCommandOnly.execute(BuildCommandOnly.java:36)
	at org.gradle.launcher.daemon.server.api.DaemonCommandExecution.proceed(DaemonCommandExecution.java:120)
	at org.gradle.launcher.daemon.server.exec.StartBuildOrRespondWithBusy$1.run(StartBuildOrRespondWithBusy.java:50)
	at org.gradle.launcher.daemon.server.DaemonStateCoordinator$1.run(DaemonStateCoordinator.java:297)
	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
	at org.gradle.internal.concurrent.StoppableExecutorImpl$1.run(StoppableExecutorImpl.java:46)
Caused by: kotlin.KotlinNullPointerException
	at org.jetbrains.kotlin.gradle.tasks.SyncOutputTask.siblingInJavaDir(SyncOutputTask.kt:162)
	at org.jetbrains.kotlin.gradle.tasks.SyncOutputTask.siblingInJavaDir$default(SyncOutputTask.kt:161)
	at org.jetbrains.kotlin.gradle.tasks.SyncOutputTask.processIncrementally(SyncOutputTask.kt:125)
	at org.jetbrains.kotlin.gradle.tasks.SyncOutputTask.access$processIncrementally(SyncOutputTask.kt:57)
	at org.jetbrains.kotlin.gradle.tasks.SyncOutputTask$execute$2.execute(SyncOutputTask.kt:93)
	at org.jetbrains.kotlin.gradle.tasks.SyncOutputTask$execute$2.execute(SyncOutputTask.kt:57)
	at org.gradle.api.internal.changedetection.changes.ChangesOnlyIncrementalTaskInputs.doOutOfDate(ChangesOnlyIncrementalTaskInputs.java:46)
	at org.gradle.api.internal.changedetection.changes.StatefulIncrementalTaskInputs.outOfDate(StatefulIncrementalTaskInputs.java:39)
	at org.gradle.api.internal.changedetection.changes.ChangesOnlyIncrementalTaskInputs.outOfDate(ChangesOnlyIncrementalTaskInputs.java:27)
	at org.jetbrains.kotlin.gradle.tasks.SyncOutputTask.execute(SyncOutputTask.kt:93)
	at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:73)
	at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$IncrementalTaskAction.doExecute(DefaultTaskClassInfoStore.java:163)
	at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$StandardTaskAction.execute(DefaultTaskClassInfoStore.java:134)
	at org.gradle.api.internal.project.taskfactory.DefaultTaskClassInfoStore$StandardTaskAction.execute(DefaultTaskClassInfoStore.java:123)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:95)
	at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:76)
	... 78 more

Add scoped cell and node accessors for table and tree children

  • cell inCell extension property returning Cell storing the actor. Usable only in scope of KTable parent.
  • node inNode extension property returning Node storing the actor. Usable only in scope of KTree parent.
  • cell extension method allowing to customize Cell storing the actor, returning the actor for fluent API. Usable only in scope of KTable parent.
  • node extension method allowing to customize Node storing the actor, returning the actor for fluent API. Usable only in scope of KTree parent.

Store actor's container using userObject API of Actor. Add scoped utility extension methods that allow to easily customize and access actor containers.

VisUI support

  • Kotlin type-safe builders for VisUI widgets.
    • Non-parental actors.
      • Actor (maybe?)
      • VisImage
      • VisLabel
      • VisList
      • VisProgressBar
      • VisSelectBox
      • VisSlider
      • VisTextArea
        • HighlightTextArea
        • ScrollableTextArea
      • VisTextField
      • VisValidatableTextField
      • Touchpad
      • BusyBar
      • Separator
      • LinkLabel
    • Parental actors.
      • Button
      • VisCheckBox
      • VisRadioButton
      • VisDialog (just use VisWindow)
      • HorizontalGroup
      • HorizontalFlowGroup
      • VerticalGroup
      • VerticalFlowGroup
      • VisImageButton
      • VisImageTextButton
      • Stack
      • VisTable
      • VisTree
      • VisWindow
      • BasicColorPicker
      • ExtendedColorPicker
      • ToastTable
      • GridGroup
      • FloatingGroup
      • Spinner
    • Special-case classes.
      • ButtonGroup
      • Tooltip (the VisUI one)
      • MessageToast
      • MenuItem (parental but adding something to menu items is usually not done since it disrupts popup menu layout)
      • PopupMenu (parental but MenuItem must be added using its custom method)
      • ListView
      • DragPane / Draggable
      • FormValidator (not an actor but some API might be nice)
      • TabbedPane / Tab
      • ButtonBar
      • VisScrollPane (parental but standard addActor is not supported)
      • VisSplitPane (parental but standard addActor is not supported)
      • MultiSplitPane (parental but standard addActor is not supported)
      • Container (parental but supports only single child)
      • CollapsibleWidget (parental but supports only single child)
  • README - overview of the supported features.
  • Unit tests.

INCOMPATIBLE with ktx-scene2d to avoid collisions. Similarly named methods (for example, textButton) might return different widgets in each library.

Actor.onKeyUp() and Actor.onKeyDown() extension functions

onKey() is all fine and well, but it has a couple of downsides.

  • It only supports letter/number keys - you can't e.g. bind Esc to close a menu.
  • Holding down a key will continuously fire events instead of just one, on the initial key-down.
  • Doesn't detect key releases.

The proposed functions would bind an InputListener with the onKeyDown() or onKeyUp() methods overriden.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.