├── .gitignore ├── LICENSE.md ├── README.md ├── androidApp ├── build.gradle.kts └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── dev │ │ └── thelumiereguy │ │ └── creative_coding_compose │ │ └── android │ │ └── MainActivity.kt │ └── res │ ├── drawable-v24 │ └── ic_launcher_foreground.xml │ ├── drawable │ └── ic_launcher_background.xml │ ├── mipmap-anydpi-v26 │ ├── ic_launcher.xml │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-mdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xxhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ ├── mipmap-xxxhdpi │ ├── ic_launcher.webp │ └── ic_launcher_round.webp │ └── values │ ├── colors.xml │ ├── strings.xml │ └── themes.xml ├── build.gradle.kts ├── compose_common ├── build.gradle.kts └── src │ ├── androidMain │ └── kotlin │ │ └── dev │ │ └── thelumiereguy │ │ └── creative_coding_compose │ │ └── examples │ │ ├── bouncy_balls_sound │ │ └── BouncyBalls.kt │ │ └── circle_pattern │ │ └── CirclePatternComposable.kt │ ├── commonMain │ └── kotlin │ │ └── dev │ │ └── thelumiereguy │ │ └── creative_coding_compose │ │ ├── CreativeCodingExampleScreen.kt │ │ ├── examples │ │ ├── animated_shapes │ │ │ └── AnimatedShapes.kt │ │ ├── basic_flow_field │ │ │ └── BasicFlowField.kt │ │ ├── cantor_circles │ │ │ └── CantorCirclePattern.kt │ │ ├── circle_pattern │ │ │ └── DrawCirclePatternComposable.kt │ │ ├── cubic_sphere │ │ │ └── CubicSphere.kt │ │ ├── dot_product_shading │ │ │ └── DotProductShading.kt │ │ ├── flow_field_patterns │ │ │ ├── FlowFieldPattern1.kt │ │ │ └── FlowFieldPattern2.kt │ │ ├── mandela_patterns │ │ │ └── MandelaPattern1.kt │ │ ├── particles │ │ │ └── ParticleSet.kt │ │ ├── rippled_cubes │ │ │ └── RippledCubes.kt │ │ ├── stroked_ripples │ │ │ └── StrokedRipples.kt │ │ └── torus │ │ │ └── Torus.kt │ │ ├── helper_composables │ │ ├── persistable_canvas │ │ │ └── PersistableCanvas.kt │ │ └── stacker │ │ │ └── Stacker.kt │ │ ├── theme │ │ ├── Color.kt │ │ ├── Shape.kt │ │ ├── Theme.kt │ │ └── Type.kt │ │ └── utils │ │ └── noise.kt │ ├── desktopMain │ └── kotlin │ │ └── Main.kt │ ├── jsMain │ ├── kotlin │ │ ├── BrowserViewportWindow.kt │ │ └── Main.kt │ └── resources │ │ └── index.html │ └── uikitMain │ └── kotlin │ └── main.uikit.kt ├── docs ├── .gitignore ├── .travis.yml ├── 404.html ├── Gemfile ├── LICENSE ├── _artworks │ ├── 01.md │ ├── 02.md │ ├── 03.md │ ├── 04.md │ ├── 05.md │ ├── 06.md │ ├── 07.md │ ├── 08.md │ ├── 09.md │ ├── 10.md │ ├── 11.md │ ├── 12.md │ ├── 13.md │ ├── 14.md │ ├── 15.md │ ├── 16.md │ ├── 17.md │ ├── 18.md │ ├── 19.md │ ├── 20.md │ ├── 21.md │ └── 22.md ├── _config.yml ├── _includes │ ├── footer.html │ ├── google-analytics.html │ ├── head.html │ └── header.html ├── _layouts │ └── creativecanvas.html ├── assets │ ├── css │ │ ├── fontawesome-all.min.css │ │ ├── images │ │ │ ├── arrow.svg │ │ │ ├── close.svg │ │ │ └── spinner.svg │ │ ├── main.css │ │ └── noscript.css │ ├── js │ │ ├── breakpoints.min.js │ │ ├── browser.min.js │ │ ├── jquery.min.js │ │ ├── jquery.poptrox.min.js │ │ ├── main.js │ │ └── util.js │ ├── sass │ │ ├── base │ │ │ ├── _page.scss │ │ │ ├── _reset.scss │ │ │ └── _typography.scss │ │ ├── components │ │ │ ├── _actions.scss │ │ │ ├── _button.scss │ │ │ ├── _form.scss │ │ │ ├── _icon.scss │ │ │ ├── _icons.scss │ │ │ ├── _list.scss │ │ │ ├── _panel.scss │ │ │ ├── _poptrox-popup.scss │ │ │ └── _table.scss │ │ ├── layout │ │ │ ├── _footer.scss │ │ │ ├── _header.scss │ │ │ ├── _main.scss │ │ │ └── _wrapper.scss │ │ ├── libs │ │ │ ├── _breakpoints.scss │ │ │ ├── _functions.scss │ │ │ ├── _mixins.scss │ │ │ ├── _vars.scss │ │ │ └── _vendor.scss │ │ ├── main.scss │ │ └── noscript.scss │ └── webfonts │ │ ├── fa-brands-400.eot │ │ ├── fa-brands-400.svg │ │ ├── fa-brands-400.ttf │ │ ├── fa-brands-400.woff │ │ ├── fa-brands-400.woff2 │ │ ├── fa-regular-400.eot │ │ ├── fa-regular-400.svg │ │ ├── fa-regular-400.ttf │ │ ├── fa-regular-400.woff │ │ ├── fa-regular-400.woff2 │ │ ├── fa-solid-900.eot │ │ ├── fa-solid-900.svg │ │ ├── fa-solid-900.ttf │ │ ├── fa-solid-900.woff │ │ └── fa-solid-900.woff2 └── index.md ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── k5-compose ├── .gitignore ├── build.gradle.kts └── src │ └── main │ └── kotlin │ ├── K5ComposeMain.kt │ └── examples │ └── test_k5.kt ├── processing ├── .gitignore ├── build.gradle.kts └── src │ └── main │ ├── kotlin │ └── dev │ │ └── thelumiereguy │ │ ├── examples │ │ ├── arc_waves │ │ │ └── ArcWaves.kt │ │ ├── circle_pattern │ │ │ └── CirclePattern.kt │ │ ├── circleception │ │ │ └── Circleception.kt │ │ ├── color_pinwheel │ │ │ └── ColorPinWheel.kt │ │ ├── divine_intervention │ │ │ └── DivineIntervention.kt │ │ ├── fractal_spiralograph │ │ │ ├── FractalSpirograph.kt │ │ │ └── Orbit.kt │ │ ├── hexagonal_mandela │ │ │ └── HexagonalMandela.kt │ │ ├── iris │ │ │ └── Iris.kt │ │ ├── phyllotaxis │ │ │ └── Phyllotaxis.kt │ │ ├── squiggly_wiggly │ │ │ └── SquigglyWiggly.kt │ │ └── whirlpool │ │ │ └── Whirlpool.kt │ │ ├── main.kt │ │ └── util │ │ ├── plotShape.kt │ │ └── transformations.kt │ └── libs │ └── processing-core.jar └── settings.gradle.kts /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | .idea 4 | .DS_Store 5 | build 6 | captures 7 | .externalNativeBuild 8 | .cxx 9 | /.gradle/ 10 | /build/ 11 | /.idea/ 12 | /local.properties 13 | xcuserdata 14 | /kotlin-js-store/yarn.lock 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CreativeCodingExamples 2 | 3 | ![List of Awesome List Badge](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg) 4 | 5 | ### [https://thelumiereguy.github.io/CreativeCodingExamples/](https://thelumiereguy.github.io/CreativeCodingExamples/) 6 | 7 | A collection of Creative Coding examples by me & other artists. We've used Jetbrains Compose and Processing (Java Edition) to create these. 8 | 9 | 10 | 11 | ### Contributions 12 | 13 | We would like you to play with this repository and contribute and show-off your awesome art pieces here :p. 14 | 15 | - You can fork repository, can tweak code, play with it and can submit your artwork as a PR. 16 | - You have three options that you can use to create your artwork 17 | 1. [**k5-compose**](https://github.com/CuriousNikhil/k5-compose) - A p5.js port for Compose Desktop 18 | 2. [**Processing**](https://processing.org/) - Processing [Java Edition] 19 | 3. [**Jetpack Compose**](https://developer.android.com/jetpack/compose) - Jetpack Compose on Mobile 20 | 4. [**Jetbrains Compose**](https://www.jetbrains.com/lp/compose-mpp/) - Jetbrains Compose for Android, iOS, Web, Desktop 21 | - All above three modules are separated in the repository. 22 | 23 | **Please check the [contribution guide in wiki](https://github.com/thelumiereguy/CreativeCodingExamples/wiki/Contribution-Guide) to upload your artworks!** 24 | 25 | Can't wait to see your creations! Cheers! 26 | 27 | ## 🔖 License 28 | 29 | ``` 30 | Copyright 2022 Piyush Pradeepkumar 31 | 32 | Licensed under the Apache License, Version 2.0 (the "License"); 33 | you may not use this file except in compliance with the License. 34 | You may obtain a copy of the License at 35 | 36 | http://www.apache.org/licenses/LICENSE-2.0 37 | 38 | Unless required by applicable law or agreed to in writing, software 39 | distributed under the License is distributed on an "AS IS" BASIS, 40 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 41 | See the License for the specific language governing permissions and 42 | limitations under the License. 43 | ``` 44 | 45 | 46 | -------------------------------------------------------------------------------- /androidApp/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | id("com.android.application") 3 | kotlin("android") 4 | } 5 | 6 | repositories { 7 | maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") 8 | google() 9 | mavenCentral() 10 | } 11 | 12 | android { 13 | namespace = "dev.thelumiereguy.creative_coding_compose.android" 14 | compileSdk = 33 15 | defaultConfig { 16 | applicationId = "dev.thelumiereguy.creative_coding_compose.android" 17 | minSdk = 21 18 | targetSdk = 33 19 | versionCode = 1 20 | versionName = "1.0" 21 | } 22 | buildFeatures { 23 | compose = true 24 | } 25 | composeOptions { 26 | kotlinCompilerExtensionVersion = "1.3.0" 27 | } 28 | compileOptions { 29 | sourceCompatibility = JavaVersion.VERSION_11 30 | targetCompatibility = JavaVersion.VERSION_11 31 | } 32 | kotlinOptions { 33 | jvmTarget = "11" 34 | } 35 | packagingOptions { 36 | resources { 37 | excludes += "/META-INF/{AL2.0,LGPL2.1}" 38 | } 39 | } 40 | buildTypes { 41 | getByName("release") { 42 | isMinifyEnabled = false 43 | } 44 | } 45 | } 46 | 47 | dependencies { 48 | implementation(project(":compose_common")) 49 | implementation("androidx.compose.ui:ui:1.2.1") 50 | implementation("androidx.compose.ui:ui-tooling:1.2.1") 51 | implementation("androidx.compose.ui:ui-tooling-preview:1.2.1") 52 | implementation("androidx.compose.foundation:foundation:1.2.1") 53 | implementation("androidx.compose.material:material:1.2.1") 54 | implementation("androidx.activity:activity-compose:1.6.0") 55 | } -------------------------------------------------------------------------------- /androidApp/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /androidApp/src/main/java/dev/thelumiereguy/creative_coding_compose/android/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package dev.thelumiereguy.creative_coding_compose.android 2 | 3 | import android.app.Activity 4 | import android.os.Build 5 | import android.os.Bundle 6 | import android.view.View 7 | import android.view.WindowManager 8 | import androidx.activity.ComponentActivity 9 | import androidx.activity.compose.setContent 10 | import androidx.compose.foundation.background 11 | import androidx.compose.foundation.layout.fillMaxSize 12 | import androidx.compose.ui.Modifier 13 | import androidx.compose.ui.graphics.Color 14 | import dev.thelumiereguy.creative_coding_compose.CreativeCodingExampleScreen 15 | 16 | class MainActivity : ComponentActivity() { 17 | override fun onCreate(savedInstanceState: Bundle?) { 18 | super.onCreate(savedInstanceState) 19 | setContent { 20 | CreativeCodingExampleScreen( 21 | Modifier 22 | .fillMaxSize() 23 | .background(Color.Black) 24 | ) 25 | } 26 | } 27 | } 28 | 29 | 30 | fun Activity.setFullScreen() { 31 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { 32 | window.setFlags( 33 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, 34 | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN 35 | ) 36 | window.attributes.layoutInDisplayCutoutMode = 37 | WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES 38 | } 39 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { 40 | window.setDecorFitsSystemWindows(false) 41 | } else { 42 | window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_IMMERSIVE 43 | // Set the content to appear under the system bars so that the 44 | // content doesn't resize when the system bars hide and show. 45 | or View.SYSTEM_UI_FLAG_LAYOUT_STABLE 46 | or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 47 | or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN 48 | // Hide the nav bar and status bar 49 | or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION 50 | or View.SYSTEM_UI_FLAG_FULLSCREEN) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /androidApp/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /androidApp/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | 12 | 14 | 16 | 18 | 20 | 22 | 24 | 26 | 28 | 30 | 32 | 34 | 36 | 38 | 40 | 42 | 44 | 46 | 48 | 50 | 52 | 54 | 56 | 58 | 60 | 62 | 64 | 66 | 68 | 70 | 72 | 74 | 75 | -------------------------------------------------------------------------------- /androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /androidApp/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /androidApp/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thelumiereguy/CreativeCodingExamples/94372d747bcaae6a710753832e8d7fddc3d0ccc4/androidApp/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /androidApp/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thelumiereguy/CreativeCodingExamples/94372d747bcaae6a710753832e8d7fddc3d0ccc4/androidApp/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /androidApp/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thelumiereguy/CreativeCodingExamples/94372d747bcaae6a710753832e8d7fddc3d0ccc4/androidApp/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /androidApp/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thelumiereguy/CreativeCodingExamples/94372d747bcaae6a710753832e8d7fddc3d0ccc4/androidApp/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /androidApp/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thelumiereguy/CreativeCodingExamples/94372d747bcaae6a710753832e8d7fddc3d0ccc4/androidApp/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /androidApp/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thelumiereguy/CreativeCodingExamples/94372d747bcaae6a710753832e8d7fddc3d0ccc4/androidApp/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /androidApp/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thelumiereguy/CreativeCodingExamples/94372d747bcaae6a710753832e8d7fddc3d0ccc4/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /androidApp/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thelumiereguy/CreativeCodingExamples/94372d747bcaae6a710753832e8d7fddc3d0ccc4/androidApp/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thelumiereguy/CreativeCodingExamples/94372d747bcaae6a710753832e8d7fddc3d0ccc4/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thelumiereguy/CreativeCodingExamples/94372d747bcaae6a710753832e8d7fddc3d0ccc4/androidApp/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /androidApp/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /androidApp/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | CreativeCodingCompose 3 | -------------------------------------------------------------------------------- /androidApp/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | -------------------------------------------------------------------------------- /build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | //trick: for the same plugin versions in all sub-modules 3 | kotlin("multiplatform") apply false 4 | kotlin("android") apply false 5 | id("com.android.application") apply false 6 | id("com.android.library") apply false 7 | id("org.jetbrains.compose") apply false 8 | } 9 | 10 | group = "dev.thelumiereguy" 11 | version = "1.0.0" -------------------------------------------------------------------------------- /compose_common/build.gradle.kts: -------------------------------------------------------------------------------- 1 | import org.jetbrains.compose.desktop.application.dsl.TargetFormat 2 | 3 | plugins { 4 | kotlin("multiplatform") 5 | id("com.android.library") 6 | id("org.jetbrains.compose") 7 | } 8 | 9 | 10 | kotlin { 11 | android() 12 | js(IR) { 13 | browser() 14 | useCommonJs() 15 | binaries.executable() 16 | } 17 | /** 18 | * Courtesy of latest examples from 19 | * https://github.com/JetBrains/compose-jb/tree/59d4e677b61902d7e465b1c6ce24b95379e55271/experimental/examples/falling-balls-mpp 20 | */ 21 | iosX64("uikitX64") { 22 | binaries { 23 | executable { 24 | entryPoint = "main" 25 | freeCompilerArgs = freeCompilerArgs + listOf( 26 | "-linker-option", "-framework", "-linker-option", "Metal", 27 | "-linker-option", "-framework", "-linker-option", "CoreText", 28 | "-linker-option", "-framework", "-linker-option", "CoreGraphics" 29 | ) 30 | } 31 | } 32 | } 33 | iosArm64("uikitArm64") { 34 | binaries { 35 | executable { 36 | entryPoint = "main" 37 | freeCompilerArgs = freeCompilerArgs + listOf( 38 | "-linker-option", "-framework", "-linker-option", "Metal", 39 | "-linker-option", "-framework", "-linker-option", "CoreText", 40 | "-linker-option", "-framework", "-linker-option", "CoreGraphics" 41 | ) 42 | // TODO: the current compose binary surprises LLVM, so disable checks for now. 43 | freeCompilerArgs = freeCompilerArgs + "-Xdisable-phases=VerifyBitcode" 44 | } 45 | } 46 | } 47 | jvm("desktop") { 48 | compilations.all { 49 | kotlinOptions.jvmTarget = "11" 50 | } 51 | 52 | } 53 | 54 | sourceSets { 55 | val commonMain by getting { 56 | dependencies { 57 | implementation(compose.ui) 58 | implementation(compose.foundation) 59 | implementation(compose.material) 60 | implementation(compose.runtime) 61 | implementation("dev.romainguy:kotlin-math:1.4.0") 62 | } 63 | } 64 | val commonTest by getting { 65 | dependencies { 66 | implementation(kotlin("test")) 67 | } 68 | } 69 | val androidMain by getting { 70 | dependencies { 71 | api(compose.preview) 72 | } 73 | } 74 | val nativeMain by creating { 75 | dependsOn(commonMain) 76 | } 77 | val uikitMain by creating { 78 | dependsOn(nativeMain) 79 | } 80 | val uikitX64Main by getting { 81 | dependsOn(uikitMain) 82 | } 83 | val uikitArm64Main by getting { 84 | dependsOn(uikitMain) 85 | } 86 | val desktopMain by getting { 87 | dependsOn(commonMain) 88 | dependencies { 89 | implementation(compose.desktop.currentOs) 90 | api(compose.preview) 91 | } 92 | } 93 | val jsMain by getting { 94 | dependencies { 95 | implementation(compose.web.core) 96 | implementation(compose.runtime) 97 | } 98 | } 99 | } 100 | } 101 | 102 | repositories { 103 | maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") 104 | google() 105 | mavenCentral() 106 | } 107 | 108 | compose.experimental { 109 | web.application {} 110 | uikit.application { 111 | bundleIdPrefix = "dev.thelumiereguy" 112 | projectName = "CreativeCodingExamples" 113 | deployConfigurations { 114 | simulator("IPhone13Pro") { 115 | //Usage: ./gradlew iosDeployIPhone8Debug 116 | device = org.jetbrains.compose.experimental.dsl.IOSDevices.IPHONE_13_PRO 117 | } 118 | connectedDevice("Device") { 119 | // Usage: ./gradlew iosDeployDeviceRelease 120 | // this.teamId = "" 121 | } 122 | } 123 | } 124 | } 125 | 126 | compose.desktop { 127 | application { 128 | mainClass = "MainKt" 129 | nativeDistributions { 130 | targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb) 131 | packageName = "ComposeMPPTest" 132 | packageVersion = "1.0.0" 133 | } 134 | } 135 | } 136 | 137 | android { 138 | namespace = "dev.thelumiereguy.creative_coding_compose" 139 | compileSdk = 33 140 | defaultConfig { 141 | minSdk = 21 142 | targetSdk = 33 143 | } 144 | } -------------------------------------------------------------------------------- /compose_common/src/androidMain/kotlin/dev/thelumiereguy/creative_coding_compose/examples/bouncy_balls_sound/BouncyBalls.kt: -------------------------------------------------------------------------------- 1 | package dev.thelumiereguy.creative_coding_compose.examples.bouncy_balls_sound 2 | 3 | import android.media.AudioManager 4 | import android.media.ToneGenerator 5 | import android.os.Handler 6 | import android.os.Looper 7 | import androidx.compose.animation.core.LinearEasing 8 | import androidx.compose.animation.core.RepeatMode 9 | import androidx.compose.animation.core.animateFloat 10 | import androidx.compose.animation.core.infiniteRepeatable 11 | import androidx.compose.animation.core.rememberInfiniteTransition 12 | import androidx.compose.animation.core.tween 13 | import androidx.compose.foundation.Canvas 14 | import androidx.compose.foundation.background 15 | import androidx.compose.foundation.layout.BoxWithConstraints 16 | import androidx.compose.runtime.Composable 17 | import androidx.compose.runtime.remember 18 | import androidx.compose.ui.Modifier 19 | import androidx.compose.ui.geometry.Offset 20 | import androidx.compose.ui.graphics.Color 21 | import androidx.compose.ui.graphics.PathEffect 22 | import androidx.compose.ui.graphics.drawscope.translate 23 | import dev.romainguy.kotlin.math.radians 24 | import kotlin.math.cos 25 | import kotlin.math.sin 26 | import kotlin.random.Random 27 | 28 | private const val maxIterations = 6 29 | 30 | @Composable 31 | fun BouncyBalls(modifier: Modifier) { 32 | BoxWithConstraints( 33 | Modifier 34 | .background(Color.Black) 35 | ) { 36 | 37 | val infiniteTransition = rememberInfiniteTransition() 38 | 39 | val startAngle = remember { 40 | radians(-45f) 41 | } 42 | 43 | val endAngle = remember { 44 | radians(-135f) 45 | } 46 | 47 | val toneManager = remember { 48 | ToneGenerator( 49 | AudioManager.STREAM_MUSIC, 100 50 | ) 51 | } 52 | 53 | val handler = remember { 54 | Handler(Looper.getMainLooper()) 55 | } 56 | 57 | val sideCount = (0..maxIterations).map { 58 | infiniteTransition.animateFloat( 59 | initialValue = startAngle, 60 | targetValue = endAngle, 61 | animationSpec = infiniteRepeatable( 62 | tween( 63 | 1000 + (it * 100), 64 | easing = LinearEasing 65 | ), 66 | repeatMode = RepeatMode.Reverse 67 | ) 68 | ) 69 | } 70 | 71 | val leftBound = remember { 72 | Offset( 73 | x = getXCoord(endAngle - 0.05f, maxIterations + 1), 74 | y = getYCoord(endAngle - 0.05f, maxIterations + 1), 75 | ) 76 | } 77 | 78 | val rightBound = remember { 79 | Offset( 80 | x = getXCoord(startAngle + 0.05f, maxIterations + 1), 81 | y = getYCoord(startAngle + 0.05f, maxIterations + 1), 82 | ) 83 | } 84 | 85 | 86 | Canvas( 87 | modifier = Modifier.then( 88 | modifier 89 | ), 90 | ) { 91 | translate( 92 | constraints.maxWidth / 2f, 93 | constraints.maxHeight / 1.5f, 94 | ) { 95 | 96 | var previousPos: Offset? = null 97 | 98 | repeat(maxIterations) { 99 | 100 | val index = it + 1 101 | 102 | val angle = sideCount[it].value 103 | 104 | val x = getXCoord(angle, index) 105 | val y = getYCoord(angle, index) 106 | 107 | 108 | /** 109 | * Plays a tone 110 | * toneType is randomized between 0 to 24 (constants from [ToneGenerator]) 111 | */ 112 | if (angle > startAngle - 0.01f) { 113 | handler.post { 114 | toneManager.startTone( 115 | Random.nextInt(0, 24), 116 | 100 117 | ) 118 | } 119 | } 120 | 121 | val currentPos = Offset(x, y) 122 | 123 | previousPos?.let { previousPos -> 124 | drawLine( 125 | Color.White, 126 | start = previousPos, 127 | end = currentPos, 128 | strokeWidth = 1f, 129 | pathEffect = PathEffect.dashPathEffect( 130 | floatArrayOf(0f, 15f, 5f, 10f), 131 | 10f 132 | ) 133 | ) 134 | } 135 | 136 | drawCircle( 137 | Color.hsl( 138 | (360f / maxIterations) * index, 139 | 0.3f, 140 | 0.6f 141 | ), 142 | center = currentPos, 143 | radius = 20f 144 | ) 145 | 146 | previousPos = currentPos 147 | } 148 | 149 | 150 | drawLine( 151 | Color.White, 152 | start = Offset.Zero, 153 | end = leftBound 154 | ) 155 | 156 | drawLine( 157 | Color.White, 158 | start = Offset.Zero, 159 | end = rightBound 160 | ) 161 | } 162 | } 163 | } 164 | } 165 | 166 | private fun getXCoord(angle: Float, index: Int): Float { 167 | return cos(angle) * 100f * index 168 | } 169 | 170 | private fun getYCoord(angle: Float, index: Int): Float { 171 | return sin(angle) * 150f * index 172 | } -------------------------------------------------------------------------------- /compose_common/src/androidMain/kotlin/dev/thelumiereguy/creative_coding_compose/examples/circle_pattern/CirclePatternComposable.kt: -------------------------------------------------------------------------------- 1 | package dev.thelumiereguy.creative_coding_compose.examples.circle_pattern 2 | 3 | import android.view.MotionEvent 4 | import androidx.compose.animation.AnimatedVisibility 5 | import androidx.compose.animation.core.tween 6 | import androidx.compose.animation.fadeIn 7 | import androidx.compose.animation.fadeOut 8 | import androidx.compose.foundation.layout.fillMaxSize 9 | import androidx.compose.material.MaterialTheme 10 | import androidx.compose.material.Surface 11 | import androidx.compose.runtime.* 12 | import androidx.compose.ui.ExperimentalComposeUiApi 13 | import androidx.compose.ui.Modifier 14 | import androidx.compose.ui.geometry.Offset 15 | import androidx.compose.ui.input.pointer.pointerInteropFilter 16 | 17 | @OptIn(ExperimentalComposeUiApi::class, androidx.compose.animation.ExperimentalAnimationApi::class) 18 | @Composable 19 | fun CirclePatternComposable(modifier: Modifier = Modifier.fillMaxSize()) { 20 | 21 | var touchCoordinates by remember { 22 | mutableStateOf(null) 23 | } 24 | 25 | var shouldStartTracking by remember { 26 | mutableStateOf(false) 27 | } 28 | 29 | Surface( 30 | modifier = modifier 31 | .pointerInteropFilter { event -> 32 | when (event.action) { 33 | MotionEvent.ACTION_DOWN -> { 34 | touchCoordinates = Offset( 35 | event.x, 36 | event.y 37 | ) 38 | shouldStartTracking = true 39 | } 40 | MotionEvent.ACTION_MOVE -> { 41 | touchCoordinates = Offset( 42 | event.x, 43 | event.y 44 | ) 45 | } 46 | MotionEvent.ACTION_UP -> { 47 | shouldStartTracking = false 48 | } 49 | else -> false 50 | } 51 | true 52 | }, 53 | color = MaterialTheme.colors.background 54 | ) { 55 | AnimatedVisibility( 56 | visible = shouldStartTracking, 57 | modifier = Modifier.fillMaxSize(), 58 | enter = fadeIn(animationSpec = tween(3000, delayMillis = 90)), 59 | exit = fadeOut(animationSpec = tween(1000)) 60 | ) { 61 | touchCoordinates?.let { coords -> 62 | DrawCirclePatternComposable(coords.x, coords.y) 63 | } 64 | } 65 | } 66 | } -------------------------------------------------------------------------------- /compose_common/src/commonMain/kotlin/dev/thelumiereguy/creative_coding_compose/CreativeCodingExampleScreen.kt: -------------------------------------------------------------------------------- 1 | package dev.thelumiereguy.creative_coding_compose 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.ui.Modifier 5 | import dev.thelumiereguy.creative_coding_compose.examples.basic_flow_field.BasicFlowField 6 | import dev.thelumiereguy.creative_coding_compose.theme.CreativeCodingComposeTheme 7 | 8 | @Composable 9 | fun CreativeCodingExampleScreen( 10 | modifier: Modifier 11 | ) { 12 | CreativeCodingComposeTheme { 13 | BasicFlowField( 14 | modifier, 15 | ) 16 | } 17 | } -------------------------------------------------------------------------------- /compose_common/src/commonMain/kotlin/dev/thelumiereguy/creative_coding_compose/examples/animated_shapes/AnimatedShapes.kt: -------------------------------------------------------------------------------- 1 | package dev.thelumiereguy.creative_coding_compose.examples.animated_shapes 2 | 3 | import androidx.compose.animation.core.RepeatMode 4 | import androidx.compose.animation.core.animateFloat 5 | import androidx.compose.animation.core.infiniteRepeatable 6 | import androidx.compose.animation.core.rememberInfiniteTransition 7 | import androidx.compose.animation.core.tween 8 | import androidx.compose.foundation.Canvas 9 | import androidx.compose.runtime.Composable 10 | import androidx.compose.runtime.getValue 11 | import androidx.compose.ui.Modifier 12 | import androidx.compose.ui.geometry.Offset 13 | import androidx.compose.ui.graphics.Color 14 | import androidx.compose.ui.graphics.drawscope.rotate 15 | import androidx.compose.ui.graphics.drawscope.translate 16 | import dev.romainguy.kotlin.math.radians 17 | import kotlin.math.cos 18 | import kotlin.math.roundToInt 19 | import kotlin.math.sin 20 | 21 | @Composable 22 | fun AnimatedShapes(modifier: Modifier) { 23 | 24 | val sideCount by rememberInfiniteTransition().animateFloat( 25 | 1f, 26 | 20f, 27 | infiniteRepeatable( 28 | tween( 29 | 3000 30 | ), 31 | repeatMode = RepeatMode.Reverse 32 | ) 33 | ) 34 | 35 | Canvas( 36 | modifier = Modifier.then( 37 | modifier 38 | ), 39 | ) { 40 | 41 | translate( 42 | drawContext.size.width / 2, 43 | drawContext.size.height / 2, 44 | ) { 45 | 46 | repeat(sideCount.roundToInt()) { 47 | 48 | rotate( 49 | (360 / sideCount) * it, 50 | pivot = Offset.Zero 51 | ) { 52 | 53 | drawLine( 54 | Color.White, 55 | start = Offset( 56 | cos(0f) * 150f, 57 | sin(0f) * 150f 58 | ), 59 | end = Offset( 60 | cos(radians((360 / sideCount))) * 250f, 61 | sin(radians((360 / sideCount))) * 150f 62 | ) 63 | ) 64 | 65 | } 66 | } 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /compose_common/src/commonMain/kotlin/dev/thelumiereguy/creative_coding_compose/examples/basic_flow_field/BasicFlowField.kt: -------------------------------------------------------------------------------- 1 | package dev.thelumiereguy.creative_coding_compose.examples.basic_flow_field 2 | 3 | import androidx.compose.foundation.layout.BoxWithConstraints 4 | import androidx.compose.runtime.Composable 5 | import androidx.compose.runtime.LaunchedEffect 6 | import androidx.compose.runtime.getValue 7 | import androidx.compose.runtime.mutableStateOf 8 | import androidx.compose.runtime.remember 9 | import androidx.compose.runtime.setValue 10 | import androidx.compose.runtime.withFrameMillis 11 | import androidx.compose.ui.Modifier 12 | import androidx.compose.ui.geometry.Offset 13 | import androidx.compose.ui.geometry.Size 14 | import androidx.compose.ui.graphics.BlendMode 15 | import androidx.compose.ui.graphics.Color 16 | import androidx.compose.ui.graphics.drawscope.translate 17 | import androidx.compose.ui.platform.LocalDensity 18 | import dev.romainguy.kotlin.math.Float2 19 | import dev.romainguy.kotlin.math.PI 20 | import dev.romainguy.kotlin.math.TWO_PI 21 | import dev.romainguy.kotlin.math.length 22 | import dev.romainguy.kotlin.math.radians 23 | import dev.thelumiereguy.creative_coding_compose.helper_composables.persistable_canvas.PersistableCanvas 24 | import dev.thelumiereguy.creative_coding_compose.utils.map 25 | import dev.thelumiereguy.creative_coding_compose.utils.noise2D 26 | import kotlinx.coroutines.isActive 27 | import kotlin.math.cos 28 | import kotlin.math.roundToInt 29 | import kotlin.math.sin 30 | import kotlin.random.Random 31 | 32 | private const val densityX = 15f 33 | private const val densityY = 30f 34 | 35 | @Composable 36 | fun BasicFlowField(modifier: Modifier = Modifier) { 37 | BoxWithConstraints { 38 | var frame by remember { 39 | mutableStateOf(0.0) 40 | } 41 | 42 | var time by remember { 43 | mutableStateOf(0f) 44 | } 45 | 46 | var points by remember { 47 | mutableStateOf( 48 | listOf() 49 | ) 50 | } 51 | 52 | val density = LocalDensity.current 53 | 54 | val width = remember { 55 | maxWidth.value * density.density 56 | } 57 | 58 | val height = remember { 59 | maxHeight.value * density.density 60 | } 61 | 62 | val halfWidth = remember { 63 | width / 2f 64 | } 65 | 66 | val halfHeight = remember { 67 | height / 2f 68 | } 69 | 70 | LaunchedEffect(key1 = Unit) { 71 | val widthOffset = (width / densityX) 72 | val heightOffset = (height / densityY) 73 | 74 | val pointsList = mutableListOf() 75 | 76 | repeat( 77 | (width / widthOffset).roundToInt() 78 | ) { 79 | repeat( 80 | (height / heightOffset).roundToInt() 81 | ) { 82 | pointsList.add( 83 | Float2( 84 | (Random.nextFloat() - 0.5f) * width, 85 | (Random.nextFloat() - 0.5f) * height 86 | ) 87 | ) 88 | } 89 | } 90 | 91 | points = pointsList 92 | 93 | val startTime = withFrameMillis { it } 94 | while (isActive) { 95 | withFrameMillis { frameTime -> 96 | frame = (frameTime - startTime) * 0.0000001 97 | time = (frameTime - startTime) * 0.001f 98 | } 99 | } 100 | } 101 | 102 | PersistableCanvas( 103 | modifier = modifier, 104 | canvasSize = Size( 105 | width, 106 | height 107 | ) 108 | ) { 109 | translate( 110 | top = halfHeight, 111 | left = halfWidth 112 | ) { 113 | points = points.map { point -> 114 | 115 | val angle = map( 116 | noise2D( 117 | frame * point.x.toDouble(), 118 | frame * point.y.toDouble() 119 | ), 120 | 0, 121 | 1, 122 | 0f, 123 | 112f 124 | ) 125 | 126 | val saturation = ( 127 | sin( 128 | radians(length(point)) 129 | ) + PI 130 | ) / TWO_PI 131 | 132 | val luminosity = minOf(1 / length(point) + 0.01f, 1f) 133 | 134 | drawCircle( 135 | color = Color.hsl( 136 | 200f + (time % 50f), 137 | maxOf(saturation, 0f), 138 | luminosity 139 | ), 140 | radius = 1f, 141 | center = Offset(point.x, point.y), 142 | blendMode = BlendMode.Screen 143 | ) 144 | 145 | point + Float2( 146 | cos(angle), 147 | sin(angle) 148 | ) 149 | } 150 | } 151 | } 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /compose_common/src/commonMain/kotlin/dev/thelumiereguy/creative_coding_compose/examples/cantor_circles/CantorCirclePattern.kt: -------------------------------------------------------------------------------- 1 | package dev.thelumiereguy.creative_coding_compose.examples.cantor_circles 2 | 3 | import androidx.compose.animation.core.FastOutSlowInEasing 4 | import androidx.compose.animation.core.RepeatMode 5 | import androidx.compose.animation.core.animateFloat 6 | import androidx.compose.animation.core.infiniteRepeatable 7 | import androidx.compose.animation.core.rememberInfiniteTransition 8 | import androidx.compose.animation.core.tween 9 | import androidx.compose.foundation.Canvas 10 | import androidx.compose.foundation.background 11 | import androidx.compose.foundation.layout.BoxWithConstraints 12 | import androidx.compose.runtime.Composable 13 | import androidx.compose.runtime.getValue 14 | import androidx.compose.runtime.remember 15 | import androidx.compose.ui.Modifier 16 | import androidx.compose.ui.geometry.Offset 17 | import androidx.compose.ui.graphics.BlendMode 18 | import androidx.compose.ui.graphics.Color 19 | import androidx.compose.ui.graphics.drawscope.DrawScope 20 | import androidx.compose.ui.graphics.drawscope.rotate 21 | import androidx.compose.ui.graphics.drawscope.translate 22 | import kotlin.math.abs 23 | 24 | const val MAX_ITERATIONS = 7f 25 | 26 | @Composable 27 | fun CantorCirclePattern(modifier: Modifier) { 28 | 29 | BoxWithConstraints(modifier = Modifier) { 30 | 31 | val frame by rememberInfiniteTransition().animateFloat( 32 | initialValue = 1f, 33 | targetValue = 3f, 34 | animationSpec = infiniteRepeatable( 35 | tween( 36 | 10000, 37 | easing = FastOutSlowInEasing 38 | ), 39 | RepeatMode.Reverse 40 | ), 41 | ) 42 | 43 | 44 | val halfWidth = remember { 45 | constraints.maxWidth / 2 46 | } 47 | 48 | val halfHeight = remember { 49 | constraints.maxHeight / 2 50 | } 51 | 52 | 53 | fun DrawScope.drawCircle( 54 | center: Offset, 55 | radius: Float, 56 | iteration: Int = 1, 57 | ) { 58 | if (iteration > MAX_ITERATIONS) { 59 | return 60 | } 61 | 62 | val color = iteration / MAX_ITERATIONS 63 | 64 | rotate( 65 | degrees = (iteration % 2) * 180f * frame, 66 | pivot = center 67 | ) { 68 | 69 | // draw original circle 70 | drawCircle( 71 | color = Color( 72 | abs(color - 0.1f), 73 | color, 74 | abs(color - 0.01f), 75 | ), 76 | radius = radius, 77 | center = center, 78 | blendMode = BlendMode.Xor 79 | ) 80 | 81 | val halfRadius = radius / 2f 82 | 83 | // draw circle inside to the left 84 | drawCircle( 85 | center - Offset(halfRadius * frame, 0f), 86 | radius = halfRadius, 87 | iteration + 1 88 | ) 89 | 90 | // draw circle inside to the top 91 | drawCircle( 92 | center - Offset(0f, halfRadius * frame), 93 | radius = halfRadius, 94 | iteration + 1 95 | ) 96 | 97 | 98 | // draw circle inside to the right 99 | drawCircle( 100 | center + Offset(halfRadius * frame, 0f), 101 | radius = halfRadius, 102 | iteration + 1 103 | ) 104 | 105 | 106 | // draw circle inside to the bottom 107 | drawCircle( 108 | center + Offset(0f, halfRadius * frame), 109 | radius = halfRadius, 110 | iteration + 1 111 | ) 112 | } 113 | } 114 | 115 | Canvas(modifier = modifier.background(Color.Black)) { 116 | 117 | // Move origin to center 118 | translate( 119 | left = halfWidth.toFloat(), 120 | top = halfHeight.toFloat() 121 | ) { 122 | 123 | drawCircle( 124 | center = Offset(0f, 0f), 125 | radius = minOf(halfWidth, halfHeight).toFloat(), 126 | ) 127 | 128 | } 129 | } 130 | } 131 | } -------------------------------------------------------------------------------- /compose_common/src/commonMain/kotlin/dev/thelumiereguy/creative_coding_compose/examples/circle_pattern/DrawCirclePatternComposable.kt: -------------------------------------------------------------------------------- 1 | package dev.thelumiereguy.creative_coding_compose.examples.circle_pattern 2 | 3 | import androidx.compose.animation.core.CubicBezierEasing 4 | import androidx.compose.animation.core.RepeatMode 5 | import androidx.compose.animation.core.animateFloat 6 | import androidx.compose.animation.core.infiniteRepeatable 7 | import androidx.compose.animation.core.rememberInfiniteTransition 8 | import androidx.compose.animation.core.tween 9 | import androidx.compose.foundation.Canvas 10 | import androidx.compose.foundation.layout.fillMaxSize 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.ui.Modifier 13 | import androidx.compose.ui.geometry.Offset 14 | import androidx.compose.ui.graphics.Color 15 | import androidx.compose.ui.graphics.drawscope.DrawScope 16 | import androidx.compose.ui.graphics.drawscope.rotate 17 | import androidx.compose.ui.graphics.drawscope.translate 18 | import dev.romainguy.kotlin.math.PI 19 | import kotlin.math.floor 20 | import kotlin.math.roundToInt 21 | import kotlin.random.Random 22 | 23 | 24 | @Composable 25 | fun DrawCirclePatternComposable( 26 | xCoord: Float, 27 | yCoord: Float, 28 | modifier: Modifier = Modifier 29 | ) { 30 | 31 | val frame = rememberInfiniteTransition().animateFloat( 32 | initialValue = 1f, 33 | targetValue = 5f, 34 | animationSpec = infiniteRepeatable( 35 | tween( 36 | 5000, easing = CubicBezierEasing( 37 | 0f, 0f, 1f, 0.2f 38 | ) 39 | ), 40 | RepeatMode.Reverse 41 | ), 42 | ) 43 | 44 | Canvas( 45 | modifier = modifier.fillMaxSize() 46 | ) { 47 | drawCirclePattern(xCoord, yCoord, frame.value) 48 | } 49 | } 50 | 51 | 52 | @Composable 53 | fun DrawCirclePatternComposable( 54 | modifier: Modifier = Modifier 55 | ) { 56 | 57 | val frame = rememberInfiniteTransition().animateFloat( 58 | initialValue = 1f, 59 | targetValue = 5f, 60 | animationSpec = infiniteRepeatable( 61 | tween( 62 | 5000, easing = CubicBezierEasing( 63 | 0f, 0f, 1f, 0.2f 64 | ) 65 | ), 66 | RepeatMode.Reverse 67 | ), 68 | ) 69 | 70 | Canvas( 71 | modifier = modifier 72 | ) { 73 | drawCirclePattern( 74 | drawContext.size.width / 2, 75 | drawContext.size.height / 2, 76 | frame.value 77 | ) 78 | } 79 | } 80 | 81 | private fun DrawScope.drawCirclePattern( 82 | xCoord: Float, 83 | yCoord: Float, 84 | frame: Float, 85 | 86 | ) { 87 | 88 | val canvasWidth = size.width 89 | val canvasHeight = size.height 90 | 91 | val rings = maxOf(canvasWidth, canvasHeight).roundToInt() / 3 92 | 93 | translate( 94 | xCoord, 95 | yCoord 96 | ) { 97 | (1..rings step 50).forEach { offset -> 98 | val circumference = 2 * PI * offset 99 | val circles = circumference / 50 100 | val angleSteps = (360 / floor(circles)) 101 | var angle = 0f 102 | 103 | while (angle <= 360) { 104 | val circleRadius = ( 105 | (kotlin.math.sin( 106 | (offset * frame * 0.01f) 107 | )) * 5f) + 4f 108 | 109 | rotate( 110 | angle, 111 | Offset( 112 | 0f, 113 | 0f 114 | ) 115 | ) { 116 | val color = Color.hsl( 117 | angle % 40, 118 | Random((angle + frame * 0.01f).roundToInt()).nextFloat(), 119 | 0.5f, 120 | ) 121 | 122 | drawCircle( 123 | color, 124 | circleRadius, 125 | Offset( 126 | offset.toFloat(), 127 | 0f 128 | ) 129 | ) 130 | } 131 | angle += angleSteps 132 | } 133 | } 134 | } 135 | } -------------------------------------------------------------------------------- /compose_common/src/commonMain/kotlin/dev/thelumiereguy/creative_coding_compose/examples/cubic_sphere/CubicSphere.kt: -------------------------------------------------------------------------------- 1 | @file:OptIn(ExperimentalGraphicsApi::class) 2 | 3 | package dev.thelumiereguy.creative_coding_compose.examples.cubic_sphere 4 | 5 | import androidx.compose.animation.core.LinearEasing 6 | import androidx.compose.animation.core.RepeatMode 7 | import androidx.compose.animation.core.animateFloat 8 | import androidx.compose.animation.core.infiniteRepeatable 9 | import androidx.compose.animation.core.rememberInfiniteTransition 10 | import androidx.compose.animation.core.tween 11 | import androidx.compose.foundation.Canvas 12 | import androidx.compose.runtime.Composable 13 | import androidx.compose.runtime.getValue 14 | import androidx.compose.ui.Modifier 15 | import androidx.compose.ui.geometry.Offset 16 | import androidx.compose.ui.geometry.Size 17 | import androidx.compose.ui.graphics.BlendMode 18 | import androidx.compose.ui.graphics.Color 19 | import androidx.compose.ui.graphics.ExperimentalGraphicsApi 20 | import androidx.compose.ui.graphics.drawscope.Stroke 21 | import androidx.compose.ui.graphics.drawscope.rotate 22 | import androidx.compose.ui.graphics.drawscope.translate 23 | import kotlin.math.abs 24 | import kotlin.math.roundToInt 25 | 26 | private const val repetitionCount = 10 27 | 28 | @Composable 29 | fun CubicSphere(modifier: Modifier) { 30 | 31 | 32 | val infiniteTransition = rememberInfiniteTransition() 33 | 34 | val squareCount by infiniteTransition.animateFloat( 35 | 359f, 36 | 0f, 37 | infiniteRepeatable( 38 | tween( 39 | 5000, 40 | easing = LinearEasing 41 | ), 42 | repeatMode = RepeatMode.Reverse 43 | ) 44 | ) 45 | 46 | 47 | val offset = (0..repetitionCount).map { index -> 48 | infiniteTransition.animateFloat( 49 | 30f * (repetitionCount - index), 50 | 30f * repetitionCount, 51 | infiniteRepeatable( 52 | tween( 53 | 10000, 54 | easing = LinearEasing 55 | ), 56 | repeatMode = RepeatMode.Reverse 57 | ) 58 | ) 59 | } 60 | 61 | 62 | Canvas( 63 | modifier = modifier, 64 | ) { 65 | 66 | repeat(repetitionCount) { index -> 67 | 68 | translate( 69 | drawContext.size.width / 2f, 70 | drawContext.size.height / 2f, 71 | ) { 72 | 73 | (0..squareCount.roundToInt() step 5).forEach { angle -> 74 | 75 | val size = 5f * (index + 1) 76 | 77 | rotate( 78 | angle.toFloat(), 79 | pivot = Offset(0f, 0f) 80 | ) { 81 | drawRect( 82 | Color.hsl( 83 | 20f, 84 | abs(index / repetitionCount.toFloat() - 0.2f), 85 | 0.5f 86 | ), 87 | topLeft = Offset( 88 | offset[index].value, 89 | offset[index].value 90 | ), 91 | size = Size(size, size), 92 | style = Stroke(index.toFloat()), 93 | alpha = 1f / (index + 1), 94 | blendMode = BlendMode.Plus 95 | ) 96 | } 97 | } 98 | } 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /compose_common/src/commonMain/kotlin/dev/thelumiereguy/creative_coding_compose/examples/flow_field_patterns/FlowFieldPattern1.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Created by Piyush Pradeepkumar on 12/10/22, 10:51 pm 3 | */ 4 | 5 | package dev.thelumiereguy.creative_coding_compose.examples.flow_field_patterns 6 | 7 | import androidx.compose.foundation.layout.BoxWithConstraints 8 | import androidx.compose.runtime.Composable 9 | import androidx.compose.runtime.LaunchedEffect 10 | import androidx.compose.runtime.getValue 11 | import androidx.compose.runtime.mutableStateOf 12 | import androidx.compose.runtime.remember 13 | import androidx.compose.runtime.setValue 14 | import androidx.compose.runtime.withFrameMillis 15 | import androidx.compose.ui.Modifier 16 | import androidx.compose.ui.geometry.Offset 17 | import androidx.compose.ui.geometry.Size 18 | import androidx.compose.ui.graphics.BlendMode 19 | import androidx.compose.ui.graphics.Color 20 | import androidx.compose.ui.graphics.ExperimentalGraphicsApi 21 | import androidx.compose.ui.graphics.drawscope.rotate 22 | import androidx.compose.ui.graphics.drawscope.translate 23 | import androidx.compose.ui.platform.LocalDensity 24 | import dev.romainguy.kotlin.math.Float2 25 | import dev.romainguy.kotlin.math.length 26 | import dev.thelumiereguy.creative_coding_compose.helper_composables.persistable_canvas.PersistableCanvas 27 | import dev.thelumiereguy.creative_coding_compose.utils.map 28 | import dev.thelumiereguy.creative_coding_compose.utils.noise3D 29 | import kotlinx.coroutines.isActive 30 | import kotlin.math.cos 31 | import kotlin.math.hypot 32 | import kotlin.math.sin 33 | 34 | private const val trailCount = 150 35 | private const val repetitionCount = 5 36 | 37 | private val colorHues = listOf(0f, 60f, 200f, 260f, 330f) 38 | 39 | @Composable 40 | fun FlowFieldPattern1(modifier: Modifier = Modifier) { 41 | 42 | BoxWithConstraints { 43 | 44 | var frame by remember { 45 | mutableStateOf(0f) 46 | } 47 | 48 | var points by remember { 49 | mutableStateOf( 50 | List(trailCount) { 51 | Float2(0f, 0f) 52 | } 53 | ) 54 | } 55 | 56 | val density = LocalDensity.current 57 | 58 | val width = remember { 59 | maxWidth.value * density.density 60 | } 61 | 62 | val height = remember { 63 | maxHeight.value * density.density 64 | } 65 | 66 | val halfWidth = remember { 67 | width / 2f 68 | } 69 | 70 | val halfHeight = remember { 71 | height / 2f 72 | } 73 | 74 | val maxLength = remember { 75 | hypot(halfWidth, halfHeight) 76 | } 77 | 78 | LaunchedEffect(key1 = Unit) { 79 | val startTime = withFrameMillis { it } 80 | while (isActive) { 81 | withFrameMillis { frameTime -> 82 | frame = (frameTime - startTime) * 0.0000001f 83 | } 84 | } 85 | } 86 | 87 | PersistableCanvas( 88 | modifier = modifier, 89 | canvasSize = Size( 90 | width, 91 | height 92 | ) 93 | ) { 94 | 95 | translate( 96 | top = halfHeight - 50, 97 | left = halfWidth 98 | ) { 99 | 100 | points = points.mapIndexed { index, point -> 101 | 102 | val mag = length(point) 103 | 104 | val angle = map( 105 | noise3D( 106 | point.x.toDouble() * frame, 107 | point.y.toDouble() * index * frame, 108 | (index + 1).toDouble() 109 | ), 110 | 0f, 111 | 1f, 112 | 1f, 113 | 12f 114 | ) 115 | 116 | 117 | val luminosity = maxOf( 118 | map( 119 | mag, 120 | 0f, 121 | (maxLength / 2), 122 | 0.1f, 123 | 0f, 124 | ), 125 | 0f 126 | ) 127 | 128 | val saturation = maxOf( 129 | map( 130 | mag, 131 | 0f, 132 | maxLength, 133 | 1f, 134 | 0f, 135 | ), 136 | 0f 137 | ) 138 | 139 | repeat(repetitionCount) { repetitionIndex -> 140 | 141 | rotate( 142 | 45 + (360f / repetitionCount) * (repetitionIndex + 1), 143 | pivot = Offset.Zero 144 | ) { 145 | drawCircle( 146 | color = Color.hsl( 147 | colorHues[repetitionIndex], 148 | saturation, 149 | luminosity 150 | ), 151 | radius = 0.5f, 152 | center = Offset(point.x, point.y), 153 | blendMode = BlendMode.Screen 154 | ) 155 | } 156 | } 157 | 158 | point + Float2( 159 | cos(angle), 160 | sin(angle) 161 | ) 162 | } 163 | } 164 | } 165 | } 166 | } -------------------------------------------------------------------------------- /compose_common/src/commonMain/kotlin/dev/thelumiereguy/creative_coding_compose/examples/flow_field_patterns/FlowFieldPattern2.kt: -------------------------------------------------------------------------------- 1 | package dev.thelumiereguy.creative_coding_compose.examples.flow_field_patterns 2 | 3 | import androidx.compose.foundation.layout.BoxWithConstraints 4 | import androidx.compose.runtime.Composable 5 | import androidx.compose.runtime.LaunchedEffect 6 | import androidx.compose.runtime.getValue 7 | import androidx.compose.runtime.mutableStateOf 8 | import androidx.compose.runtime.remember 9 | import androidx.compose.runtime.setValue 10 | import androidx.compose.runtime.withFrameMillis 11 | import androidx.compose.ui.Modifier 12 | import androidx.compose.ui.geometry.Offset 13 | import androidx.compose.ui.geometry.Size 14 | import androidx.compose.ui.graphics.BlendMode 15 | import androidx.compose.ui.graphics.Color 16 | import androidx.compose.ui.graphics.ExperimentalGraphicsApi 17 | import androidx.compose.ui.graphics.drawscope.rotate 18 | import androidx.compose.ui.graphics.drawscope.translate 19 | import androidx.compose.ui.platform.LocalDensity 20 | import dev.romainguy.kotlin.math.Float2 21 | import dev.romainguy.kotlin.math.PI 22 | import dev.romainguy.kotlin.math.length 23 | import dev.thelumiereguy.creative_coding_compose.helper_composables.persistable_canvas.PersistableCanvas 24 | import dev.thelumiereguy.creative_coding_compose.utils.map 25 | import dev.thelumiereguy.creative_coding_compose.utils.noise3D 26 | import kotlinx.coroutines.delay 27 | import kotlinx.coroutines.isActive 28 | import kotlin.math.atan2 29 | import kotlin.math.cos 30 | import kotlin.math.hypot 31 | import kotlin.math.sin 32 | import kotlin.random.Random 33 | 34 | private const val trailCount = 250 35 | private const val repetitionCount = 6 36 | 37 | @OptIn(ExperimentalGraphicsApi::class) 38 | @Composable 39 | fun FlowFieldPattern2(modifier: Modifier = Modifier) { 40 | 41 | BoxWithConstraints { 42 | 43 | var frame by remember { 44 | mutableStateOf(0f) 45 | } 46 | 47 | var points by remember { 48 | mutableStateOf( 49 | List(trailCount) { 50 | Float2(0f, 0f) 51 | } 52 | ) 53 | } 54 | 55 | val density = LocalDensity.current 56 | 57 | val width = remember { 58 | maxWidth.value * density.density 59 | } 60 | 61 | val height = remember { 62 | maxHeight.value * density.density 63 | } 64 | 65 | val halfWidth = remember { 66 | width / 2f 67 | } 68 | 69 | val halfHeight = remember { 70 | height / 2f 71 | } 72 | 73 | val maxLength = remember { 74 | hypot(halfWidth, halfHeight) 75 | } 76 | 77 | LaunchedEffect(key1 = Unit) { 78 | val startTime = withFrameMillis { it } 79 | while (isActive) { 80 | withFrameMillis { frameTime -> 81 | frame = (frameTime - startTime) * 0.00000008f 82 | } 83 | } 84 | } 85 | 86 | PersistableCanvas( 87 | modifier = modifier, 88 | canvasSize = Size( 89 | width, 90 | height 91 | ) 92 | ) { 93 | 94 | translate( 95 | top = halfHeight, 96 | left = halfWidth 97 | ) { 98 | 99 | 100 | points = points.mapIndexed { index, point -> 101 | 102 | val mag = length(point) 103 | 104 | val angle = map( 105 | noise3D( 106 | point.x.toDouble() * frame, 107 | (index + 1).toDouble() * frame * point.y.toDouble(), 108 | ( 109 | (atan2( 110 | point.y, 111 | point.x 112 | ) / PI) * frame 113 | ).toDouble() 114 | ), 115 | 0f, 116 | 1f, 117 | 30f, 118 | 245f 119 | ) 120 | 121 | 122 | val luminosity = maxOf( 123 | map( 124 | mag, 125 | 0f, 126 | maxLength / 3, 127 | 0.1f, 128 | 0f, 129 | ), 130 | 0f 131 | ) 132 | 133 | val saturation = maxOf( 134 | map( 135 | mag, 136 | 0f, 137 | maxLength / 2, 138 | 1f, 139 | 0f, 140 | ), 141 | 0f 142 | ) 143 | 144 | repeat(repetitionCount) { 145 | 146 | rotate( 147 | (360f / repetitionCount) * (it + 1), 148 | pivot = Offset.Zero 149 | ) { 150 | drawCircle( 151 | color = Color.hsl( 152 | 200f, 153 | saturation, 154 | luminosity 155 | ), 156 | radius = 1f, 157 | center = Offset(point.x, point.y), 158 | blendMode = BlendMode.Screen 159 | ) 160 | } 161 | } 162 | 163 | point + Float2( 164 | cos(angle), 165 | sin(angle) 166 | ) 167 | } 168 | } 169 | } 170 | } 171 | } -------------------------------------------------------------------------------- /compose_common/src/commonMain/kotlin/dev/thelumiereguy/creative_coding_compose/examples/mandela_patterns/MandelaPattern1.kt: -------------------------------------------------------------------------------- 1 | package dev.thelumiereguy.creative_coding_compose.examples.mandela_patterns 2 | 3 | import androidx.compose.animation.core.RepeatMode 4 | import androidx.compose.animation.core.animateFloat 5 | import androidx.compose.animation.core.infiniteRepeatable 6 | import androidx.compose.animation.core.rememberInfiniteTransition 7 | import androidx.compose.animation.core.tween 8 | import androidx.compose.foundation.layout.BoxWithConstraints 9 | import androidx.compose.runtime.Composable 10 | import androidx.compose.runtime.LaunchedEffect 11 | import androidx.compose.runtime.getValue 12 | import androidx.compose.runtime.mutableStateOf 13 | import androidx.compose.runtime.remember 14 | import androidx.compose.runtime.setValue 15 | import androidx.compose.runtime.withFrameMillis 16 | import androidx.compose.ui.Modifier 17 | import androidx.compose.ui.geometry.Size 18 | import androidx.compose.ui.geometry.center 19 | import androidx.compose.ui.graphics.BlendMode 20 | import androidx.compose.ui.graphics.Color 21 | import androidx.compose.ui.graphics.Path 22 | import androidx.compose.ui.graphics.drawscope.Stroke 23 | import androidx.compose.ui.graphics.drawscope.rotate 24 | import androidx.compose.ui.graphics.drawscope.scale 25 | import androidx.compose.ui.platform.LocalDensity 26 | import dev.thelumiereguy.creative_coding_compose.helper_composables.persistable_canvas.PersistableCanvas 27 | import dev.thelumiereguy.creative_coding_compose.utils.noise1D 28 | import kotlin.math.roundToInt 29 | import kotlinx.coroutines.isActive 30 | 31 | @Composable 32 | fun MandelaPattern1(modifier: Modifier) { 33 | 34 | BoxWithConstraints { 35 | 36 | val frequencyX by rememberInfiniteTransition().animateFloat( 37 | 40f, 38 | 100f, 39 | infiniteRepeatable( 40 | tween( 41 | 500, 42 | ), 43 | repeatMode = RepeatMode.Reverse 44 | ) 45 | ) 46 | 47 | val repetitions by rememberInfiniteTransition().animateFloat( 48 | 2f, 49 | 10f, 50 | infiniteRepeatable( 51 | tween( 52 | 3000, 53 | ), 54 | repeatMode = RepeatMode.Reverse 55 | ) 56 | ) 57 | 58 | 59 | var time by remember { 60 | mutableStateOf(0f) 61 | } 62 | 63 | val density = LocalDensity.current 64 | 65 | val width = remember { 66 | maxWidth.value * density.density 67 | } 68 | 69 | val height = remember { 70 | maxHeight.value * density.density 71 | } 72 | 73 | LaunchedEffect(key1 = Unit) { 74 | val startTime = withFrameMillis { it } 75 | 76 | while (isActive) { 77 | withFrameMillis { frameTime -> 78 | time = (frameTime - startTime) * 0.01f 79 | } 80 | } 81 | } 82 | 83 | PersistableCanvas( 84 | modifier = Modifier.then( 85 | modifier 86 | ), 87 | canvasSize = Size( 88 | width, 89 | height 90 | ) 91 | ) { 92 | 93 | 94 | rotate( 95 | time, 96 | pivot = drawContext.size.center 97 | ) { 98 | 99 | val startX = 0f 100 | val startY = drawContext.size.height / 2 101 | 102 | val path = Path().apply { 103 | 104 | moveTo( 105 | startX, 106 | startY 107 | ) 108 | 109 | 110 | repeat(repetitions.roundToInt()) { index -> 111 | relativeQuadraticBezierTo( 112 | 20f, 113 | 50f, 114 | frequencyX, 115 | 0f 116 | ) 117 | 118 | relativeQuadraticBezierTo( 119 | 20f, 120 | -50f, 121 | frequencyX, 122 | 0f 123 | ) 124 | } 125 | } 126 | 127 | drawPath( 128 | path, 129 | color = Color.White.copy( 130 | alpha = noise1D(time * 0.001).toFloat() 131 | ), 132 | style = Stroke( 133 | width = 1f 134 | ), 135 | blendMode = BlendMode.Screen 136 | ) 137 | } 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /compose_common/src/commonMain/kotlin/dev/thelumiereguy/creative_coding_compose/examples/rippled_cubes/RippledCubes.kt: -------------------------------------------------------------------------------- 1 | package dev.thelumiereguy.creative_coding_compose.examples.rippled_cubes 2 | 3 | import androidx.compose.animation.core.LinearEasing 4 | import androidx.compose.animation.core.RepeatMode 5 | import androidx.compose.animation.core.animateFloat 6 | import androidx.compose.animation.core.infiniteRepeatable 7 | import androidx.compose.animation.core.rememberInfiniteTransition 8 | import androidx.compose.animation.core.tween 9 | import androidx.compose.foundation.Canvas 10 | import androidx.compose.foundation.layout.BoxWithConstraints 11 | import androidx.compose.foundation.layout.fillMaxSize 12 | import androidx.compose.runtime.Composable 13 | import androidx.compose.runtime.getValue 14 | import androidx.compose.runtime.remember 15 | import androidx.compose.ui.Modifier 16 | import androidx.compose.ui.geometry.Offset 17 | import androidx.compose.ui.geometry.Size 18 | import androidx.compose.ui.graphics.BlendMode 19 | import androidx.compose.ui.graphics.Color 20 | import androidx.compose.ui.graphics.drawscope.DrawScope 21 | import androidx.compose.ui.graphics.drawscope.Stroke 22 | import androidx.compose.ui.graphics.drawscope.translate 23 | import androidx.compose.ui.platform.LocalDensity 24 | import dev.romainguy.kotlin.math.Float2 25 | import dev.romainguy.kotlin.math.PI 26 | import dev.romainguy.kotlin.math.degrees 27 | import dev.romainguy.kotlin.math.fract 28 | import dev.romainguy.kotlin.math.length 29 | import kotlin.math.abs 30 | import kotlin.math.atan2 31 | import kotlin.math.cos 32 | import kotlin.math.roundToInt 33 | import kotlin.math.sin 34 | 35 | 36 | @Composable 37 | fun RippledCubes( 38 | modifier: Modifier = Modifier 39 | ) { 40 | 41 | BoxWithConstraints( 42 | modifier 43 | ) { 44 | 45 | val infiniteTransition = rememberInfiniteTransition() 46 | 47 | val frame by infiniteTransition.animateFloat( 48 | initialValue = 1f, 49 | targetValue = 100f, 50 | animationSpec = infiniteRepeatable( 51 | tween( 52 | 10000, 53 | easing = LinearEasing 54 | ), 55 | RepeatMode.Reverse 56 | ), 57 | ) 58 | 59 | 60 | val density = LocalDensity.current 61 | 62 | val halfWidth = remember { 63 | (maxWidth.value * density.density) / 2f 64 | } 65 | 66 | val halfHeight = remember { 67 | (maxHeight.value * density.density) / 2f 68 | } 69 | 70 | Canvas( 71 | modifier = Modifier.fillMaxSize(), 72 | ) { 73 | 74 | translate( 75 | left = halfWidth, 76 | top = halfHeight 77 | ) { 78 | 79 | (-halfWidth.roundToInt()..halfWidth.roundToInt() step 71).forEach { x -> 80 | (-halfHeight.roundToInt()..halfHeight.roundToInt() step 71).forEach { y -> 81 | drawCube(x.toFloat(), y.toFloat(), frame) 82 | } 83 | } 84 | } 85 | } 86 | } 87 | } 88 | 89 | private fun DrawScope.drawCube(x: Float, y: Float, frame: Float) { 90 | 91 | val topLeftCoordinate = Float2( 92 | x, 93 | y 94 | ) 95 | 96 | val lengthByFrame = length( 97 | topLeftCoordinate * frame 98 | ) 99 | 100 | val sideSize = abs( 101 | sin( 102 | lengthByFrame * 0.0001f 103 | ) 104 | ) * 40f 105 | 106 | 107 | val size = Size( 108 | sideSize, 109 | sideSize, 110 | ) 111 | 112 | val lateralSideLength = sideSize / 1.5f 113 | 114 | val angleRadians = atan2(topLeftCoordinate.y, topLeftCoordinate.x) - PI 115 | 116 | val color = Color.hsl( 117 | minOf(abs(degrees(angleRadians)), 360f), 118 | 1f, 119 | fract( 120 | lengthByFrame * 0.0001f 121 | ) 122 | ) 123 | 124 | val otherSideX = x + cos(angleRadians) * lateralSideLength 125 | val otherSideY = y + sin(angleRadians) * lateralSideLength 126 | 127 | 128 | drawLateralLines( 129 | x, 130 | y, 131 | angleRadians, 132 | sideSize, 133 | lateralSideLength, 134 | color 135 | ) 136 | 137 | drawRect( 138 | color, 139 | topLeft = Offset( 140 | otherSideX, 141 | otherSideY 142 | ), 143 | style = Stroke(1f), 144 | size = size, 145 | blendMode = BlendMode.Plus 146 | ) 147 | 148 | drawRect( 149 | color, 150 | topLeft = Offset( 151 | x, 152 | y 153 | ), 154 | style = Stroke(1f), 155 | size = size, 156 | blendMode = BlendMode.Plus 157 | ) 158 | } 159 | 160 | 161 | fun DrawScope.drawLateralLines( 162 | centerX: Float, 163 | centerY: Float, 164 | angleRadians: Float, 165 | sideSize: Float, 166 | lateralSideLength: Float, 167 | color: Color 168 | ) { 169 | 170 | listOf(0f, sideSize).forEach { xOffset -> 171 | listOf(0f, sideSize).forEach { yOffset -> 172 | 173 | val startX = centerX + xOffset 174 | val startY = centerY + yOffset 175 | 176 | val endX = startX + (cos(angleRadians) * lateralSideLength) 177 | val endY = startY + (sin(angleRadians) * lateralSideLength) 178 | 179 | drawLine( 180 | color, 181 | start = Offset( 182 | startX, 183 | startY 184 | ), 185 | end = Offset( 186 | endX, 187 | endY 188 | ), 189 | blendMode = BlendMode.Plus 190 | ) 191 | } 192 | } 193 | } -------------------------------------------------------------------------------- /compose_common/src/commonMain/kotlin/dev/thelumiereguy/creative_coding_compose/examples/stroked_ripples/StrokedRipples.kt: -------------------------------------------------------------------------------- 1 | package dev.thelumiereguy.creative_coding_compose.examples.stroked_ripples 2 | 3 | import androidx.compose.animation.core.LinearEasing 4 | import androidx.compose.animation.core.RepeatMode 5 | import androidx.compose.animation.core.animateFloat 6 | import androidx.compose.animation.core.infiniteRepeatable 7 | import androidx.compose.animation.core.rememberInfiniteTransition 8 | import androidx.compose.animation.core.tween 9 | import androidx.compose.foundation.Canvas 10 | import androidx.compose.foundation.background 11 | import androidx.compose.foundation.layout.BoxWithConstraints 12 | import androidx.compose.runtime.Composable 13 | import androidx.compose.runtime.getValue 14 | import androidx.compose.runtime.remember 15 | import androidx.compose.ui.Modifier 16 | import androidx.compose.ui.geometry.Offset 17 | import androidx.compose.ui.graphics.Color 18 | import androidx.compose.ui.graphics.drawscope.rotate 19 | import androidx.compose.ui.graphics.drawscope.translate 20 | import dev.romainguy.kotlin.math.Float2 21 | import dev.romainguy.kotlin.math.length 22 | 23 | @Composable 24 | fun StrokedRipples(modifier: Modifier) { 25 | 26 | BoxWithConstraints(modifier = Modifier) { 27 | 28 | val frame by rememberInfiniteTransition().animateFloat( 29 | initialValue = 1f, 30 | targetValue = 200f, 31 | animationSpec = infiniteRepeatable( 32 | tween( 33 | 5000, 34 | easing = LinearEasing 35 | ), 36 | RepeatMode.Reverse 37 | ), 38 | ) 39 | 40 | 41 | val halfWidth = remember { 42 | constraints.maxWidth / 2 43 | } 44 | 45 | val halfHeight = remember { 46 | constraints.maxHeight / 2 47 | } 48 | 49 | Canvas(modifier = modifier.background(Color.Black)) { 50 | 51 | 52 | // Move origin to center 53 | translate( 54 | halfWidth.toFloat(), 55 | halfHeight.toFloat() 56 | ) { 57 | 58 | (-halfWidth..halfWidth step 50).forEach { x -> 59 | 60 | (-halfHeight..halfHeight step 30).forEach { y -> 61 | 62 | val coord = Float2(x.toFloat(), y.toFloat()) 63 | 64 | // Rotate by length of vector, pivot at line start 65 | rotate( 66 | degrees = frame + length(coord), 67 | pivot = Offset(coord.x, coord.y) 68 | ) { 69 | drawLine( 70 | Color.White, 71 | start = Offset(coord.x, coord.y), 72 | end = Offset(coord.x + 30, coord.y), 73 | strokeWidth = 1.5f 74 | ) 75 | } 76 | } 77 | } 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /compose_common/src/commonMain/kotlin/dev/thelumiereguy/creative_coding_compose/examples/torus/Torus.kt: -------------------------------------------------------------------------------- 1 | package dev.thelumiereguy.creative_coding_compose.examples.torus 2 | 3 | import androidx.compose.animation.core.LinearEasing 4 | import androidx.compose.animation.core.RepeatMode 5 | import androidx.compose.animation.core.animateFloat 6 | import androidx.compose.animation.core.infiniteRepeatable 7 | import androidx.compose.animation.core.rememberInfiniteTransition 8 | import androidx.compose.animation.core.tween 9 | import androidx.compose.foundation.Canvas 10 | import androidx.compose.foundation.layout.BoxWithConstraints 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.runtime.getValue 13 | import androidx.compose.runtime.remember 14 | import androidx.compose.ui.Modifier 15 | import androidx.compose.ui.geometry.Offset 16 | import androidx.compose.ui.graphics.BlendMode 17 | import androidx.compose.ui.graphics.Color 18 | import androidx.compose.ui.graphics.PathEffect 19 | import androidx.compose.ui.graphics.drawscope.Stroke 20 | import androidx.compose.ui.graphics.drawscope.translate 21 | import androidx.compose.ui.platform.LocalDensity 22 | import dev.romainguy.kotlin.math.Float2 23 | import dev.romainguy.kotlin.math.Mat2 24 | import dev.romainguy.kotlin.math.radians 25 | import kotlin.math.abs 26 | import kotlin.math.cos 27 | import kotlin.math.sin 28 | 29 | @Composable 30 | fun Torus(modifier: Modifier) { 31 | 32 | BoxWithConstraints(modifier = modifier) { 33 | 34 | val localDensity = LocalDensity.current 35 | 36 | val screenHeight = remember { maxHeight.value * localDensity.density } 37 | val screenWidth = remember { maxWidth.value * localDensity.density } 38 | 39 | /** 40 | * Infinite animation that linearly interpolates from 10 to 0f 41 | * within 15 seconds and reverse 42 | */ 43 | val animationFrame by rememberInfiniteTransition().animateFloat( 44 | initialValue = 10f, 45 | targetValue = 0f, 46 | animationSpec = infiniteRepeatable( 47 | tween( 48 | 15000, 49 | easing = LinearEasing 50 | ), 51 | RepeatMode.Reverse 52 | ), 53 | ) 54 | 55 | // Minor Radius 56 | val innerRadius = remember { 57 | (screenWidth / screenHeight) * screenWidth * 0.3f 58 | } 59 | 60 | // Major radius 61 | val outerRadius = 15 * animationFrame 62 | 63 | val rotationMatrix = Mat2( 64 | Float2(sin(animationFrame), 0f), 65 | Float2(0f, cos(animationFrame)) 66 | ) 67 | 68 | Canvas(modifier = modifier) { 69 | 70 | 71 | // Translate to center of screen 72 | translate( 73 | screenWidth / 2, 74 | screenHeight / 2 75 | ) { 76 | 77 | // Loop for drawing 360 circles 78 | (0 until 360).forEach { angle -> 79 | 80 | val angleF = angle.toFloat() 81 | 82 | val angleRadians = radians(angleF) 83 | 84 | val rotatedCenter = rotationMatrix * Float2( 85 | cos(angleRadians) * outerRadius, 86 | sin(angleRadians) * outerRadius 87 | ) 88 | 89 | 90 | drawCircle( 91 | Color.hsl( 92 | angleF, 93 | 0.6f, 94 | 0.5f, 95 | minOf(1f, (abs(rotatedCenter.x) / screenWidth) + 0.1f) 96 | ), 97 | radius = innerRadius, 98 | center = Offset( 99 | rotatedCenter.x, 100 | rotatedCenter.y 101 | ), 102 | style = Stroke( 103 | width = 10f, 104 | pathEffect = PathEffect.dashPathEffect( 105 | floatArrayOf(10f, 10f), 106 | ) 107 | ), 108 | blendMode = BlendMode.Lighten 109 | ) 110 | } 111 | } 112 | } 113 | } 114 | } -------------------------------------------------------------------------------- /compose_common/src/commonMain/kotlin/dev/thelumiereguy/creative_coding_compose/helper_composables/persistable_canvas/PersistableCanvas.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Created by Piyush Pradeepkumar on 13/10/22, 9:12 pm 3 | */ 4 | 5 | package dev.thelumiereguy.creative_coding_compose.helper_composables.persistable_canvas 6 | 7 | 8 | import androidx.compose.foundation.layout.Box 9 | import androidx.compose.runtime.Composable 10 | import androidx.compose.runtime.remember 11 | import androidx.compose.ui.Modifier 12 | import androidx.compose.ui.draw.drawWithCache 13 | import androidx.compose.ui.geometry.Size 14 | import androidx.compose.ui.graphics.ImageBitmap 15 | import androidx.compose.ui.graphics.ImageBitmapConfig 16 | import androidx.compose.ui.graphics.drawscope.CanvasDrawScope 17 | import androidx.compose.ui.graphics.drawscope.DrawScope 18 | import kotlin.math.roundToInt 19 | 20 | /** 21 | * A normal canvas would clear the canvas's content on each onDraw call. This is 22 | * a special canvas composable which persists the previous drawn calls, by drawing on a separate bitmap, 23 | * which is reused across onDraw calls. 24 | */ 25 | @Composable 26 | fun PersistableCanvas( 27 | modifier: Modifier = Modifier, 28 | canvasSize: Size, 29 | bitmapConfig: ImageBitmapConfig = ImageBitmapConfig.Argb8888, 30 | onDraw: DrawScope.() -> Unit 31 | ) { 32 | 33 | val bitmap = remember(key1 = canvasSize, key2 = bitmapConfig) { 34 | ImageBitmap( 35 | canvasSize.width.roundToInt(), 36 | canvasSize.height.roundToInt(), 37 | config = bitmapConfig 38 | ) 39 | } 40 | 41 | val canvas = remember(key1 = bitmap) { 42 | androidx.compose.ui.graphics.Canvas(bitmap) 43 | } 44 | 45 | val drawScope = remember { 46 | CanvasDrawScope() 47 | } 48 | 49 | /** 50 | * This implementation is from 51 | * https://github.com/drinkthestars/compose-sketch/blob/main/app/src/main/kotlin/com/drinkstars/composesketch/sketch/Sketch.kt 52 | * which is better than the original 'Separate Canvas' approach. 53 | */ 54 | Box( 55 | modifier = modifier 56 | .drawWithCache { 57 | drawScope.draw( 58 | density = this, 59 | layoutDirection = layoutDirection, 60 | canvas = canvas, 61 | size = size, 62 | onDraw 63 | ) 64 | 65 | bitmap.prepareToDraw() 66 | 67 | onDrawBehind { 68 | // After the bitmap is updated, draw the bitmap once here 69 | drawImage(image = bitmap) 70 | } 71 | } 72 | ) 73 | } 74 | 75 | 76 | /** 77 | * A normal canvas would clear the canvas's content on each onDraw call. This is 78 | * a special canvas composable which persists the previous drawn calls, by drawing on a separate bitmap, 79 | * which is reused across onDraw calls. 80 | * 81 | * Additional bitmap parameter to reuse/share existing bitmap objects. 82 | */ 83 | @Composable 84 | fun PersistableCanvas( 85 | modifier: Modifier = Modifier, 86 | canvasSize: Size, 87 | bitmapConfig: ImageBitmapConfig = ImageBitmapConfig.Argb8888, 88 | bitmap: ImageBitmap = ImageBitmap( 89 | canvasSize.width.roundToInt(), 90 | canvasSize.height.roundToInt(), 91 | bitmapConfig 92 | ), 93 | onDraw: DrawScope.() -> Unit 94 | ) { 95 | 96 | val canvas = remember(key1 = bitmap) { 97 | androidx.compose.ui.graphics.Canvas(bitmap) 98 | } 99 | 100 | val drawScope = remember { 101 | CanvasDrawScope() 102 | } 103 | 104 | /** 105 | * This implementation is from 106 | * https://github.com/drinkthestars/compose-sketch/blob/main/app/src/main/kotlin/com/drinkstars/composesketch/sketch/Sketch.kt 107 | * which is better than the original 'Separate Canvas' approach. 108 | */ 109 | Box( 110 | modifier = modifier 111 | .drawWithCache { 112 | drawScope.draw( 113 | density = this, 114 | layoutDirection = layoutDirection, 115 | canvas = canvas, 116 | size = size, 117 | onDraw 118 | ) 119 | 120 | bitmap.prepareToDraw() 121 | 122 | onDrawBehind { 123 | // After the bitmap is updated, draw the bitmap once here 124 | drawImage(image = bitmap) 125 | } 126 | } 127 | ) 128 | } -------------------------------------------------------------------------------- /compose_common/src/commonMain/kotlin/dev/thelumiereguy/creative_coding_compose/helper_composables/stacker/Stacker.kt: -------------------------------------------------------------------------------- 1 | package dev.thelumiereguy.creative_coding_compose.helper_composables.stacker 2 | 3 | import androidx.compose.runtime.Composable 4 | import androidx.compose.ui.Modifier 5 | import androidx.compose.ui.draw.alpha 6 | import androidx.compose.ui.layout.Layout 7 | import androidx.compose.ui.unit.IntOffset 8 | 9 | 10 | private val defaultAlphaProvider = { index: Int -> 11 | 1f / (index + 1) 12 | } 13 | 14 | private val defaultOffsetProvider = { index: Int -> 15 | IntOffset(index, index) 16 | } 17 | 18 | /** 19 | * A composable that draws the same content over and over again for [repetitions] times. 20 | * By default the content drawn each time is shifted by an offset [offsetProvider] and even the 21 | * alpha keeps reducing, given by [alphaProvider] 22 | */ 23 | @Composable 24 | fun Stacker( 25 | modifier: Modifier, 26 | repetitions: Int, 27 | offsetProvider: (index: Int) -> IntOffset = defaultOffsetProvider, 28 | alphaProvider: (index: Int) -> Float = defaultAlphaProvider, 29 | content: @Composable (index: Int, modifier: Modifier) -> Unit 30 | ) { 31 | 32 | Layout( 33 | modifier = modifier, 34 | content = { 35 | repeat(repetitions) { index -> 36 | content( 37 | index, 38 | Modifier.alpha( 39 | alphaProvider(index) 40 | ) 41 | ) 42 | } 43 | }, 44 | measurePolicy = { measurables, constraints -> 45 | 46 | val placeables = measurables.mapIndexed { index, measurable -> 47 | measurable.measure(constraints) 48 | } 49 | 50 | layout( 51 | constraints.maxWidth, 52 | constraints.maxHeight 53 | ) { 54 | 55 | placeables.forEachIndexed { index, placeable -> 56 | placeable.placeRelative( 57 | position = offsetProvider(index), 58 | zIndex = repetitions - index.toFloat() 59 | ) 60 | } 61 | } 62 | } 63 | ) 64 | } -------------------------------------------------------------------------------- /compose_common/src/commonMain/kotlin/dev/thelumiereguy/creative_coding_compose/theme/Color.kt: -------------------------------------------------------------------------------- 1 | package dev.thelumiereguy.creative_coding_compose.theme 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | val Purple200 = Color(0xFFBB86FC) 6 | val Purple500 = Color(0xFF6200EE) 7 | val Purple700 = Color(0xFF3700B3) 8 | val Teal200 = Color(0xFF03DAC5) -------------------------------------------------------------------------------- /compose_common/src/commonMain/kotlin/dev/thelumiereguy/creative_coding_compose/theme/Shape.kt: -------------------------------------------------------------------------------- 1 | package dev.thelumiereguy.creative_coding_compose.theme 2 | 3 | import androidx.compose.foundation.shape.RoundedCornerShape 4 | import androidx.compose.material.Shapes 5 | import androidx.compose.ui.unit.dp 6 | 7 | val Shapes = Shapes( 8 | small = RoundedCornerShape(4.dp), 9 | medium = RoundedCornerShape(4.dp), 10 | large = RoundedCornerShape(0.dp) 11 | ) -------------------------------------------------------------------------------- /compose_common/src/commonMain/kotlin/dev/thelumiereguy/creative_coding_compose/theme/Theme.kt: -------------------------------------------------------------------------------- 1 | package dev.thelumiereguy.creative_coding_compose.theme 2 | 3 | import androidx.compose.foundation.isSystemInDarkTheme 4 | import androidx.compose.material.MaterialTheme 5 | import androidx.compose.material.darkColors 6 | import androidx.compose.material.lightColors 7 | import androidx.compose.runtime.Composable 8 | 9 | private val DarkColorPalette = darkColors( 10 | primary = Purple200, 11 | primaryVariant = Purple700, 12 | secondary = Teal200 13 | ) 14 | 15 | private val LightColorPalette = lightColors( 16 | primary = Purple500, 17 | primaryVariant = Purple700, 18 | secondary = Teal200 19 | 20 | /* Other default colors to override 21 | background = Color.White, 22 | surface = Color.White, 23 | onPrimary = Color.White, 24 | onSecondary = Color.Black, 25 | onBackground = Color.Black, 26 | onSurface = Color.Black, 27 | */ 28 | ) 29 | 30 | @Composable 31 | fun CreativeCodingComposeTheme( 32 | darkTheme: Boolean = isSystemInDarkTheme(), 33 | content: @Composable () -> Unit 34 | ) { 35 | val colors = if (darkTheme) { 36 | DarkColorPalette 37 | } else { 38 | LightColorPalette 39 | } 40 | 41 | MaterialTheme( 42 | colors = colors, 43 | typography = Typography, 44 | shapes = Shapes, 45 | content = content 46 | ) 47 | } -------------------------------------------------------------------------------- /compose_common/src/commonMain/kotlin/dev/thelumiereguy/creative_coding_compose/theme/Type.kt: -------------------------------------------------------------------------------- 1 | package dev.thelumiereguy.creative_coding_compose.theme 2 | 3 | import androidx.compose.material.Typography 4 | import androidx.compose.ui.text.TextStyle 5 | import androidx.compose.ui.text.font.FontFamily 6 | import androidx.compose.ui.text.font.FontWeight 7 | import androidx.compose.ui.unit.sp 8 | 9 | // Set of Material typography styles to start with 10 | val Typography = Typography( 11 | body1 = TextStyle( 12 | fontFamily = FontFamily.Default, 13 | fontWeight = FontWeight.Normal, 14 | fontSize = 16.sp 15 | ) 16 | /* Other default text styles to override 17 | button = TextStyle( 18 | fontFamily = FontFamily.Default, 19 | fontWeight = FontWeight.W500, 20 | fontSize = 14.sp 21 | ), 22 | caption = TextStyle( 23 | fontFamily = FontFamily.Default, 24 | fontWeight = FontWeight.Normal, 25 | fontSize = 12.sp 26 | ) 27 | */ 28 | ) -------------------------------------------------------------------------------- /compose_common/src/commonMain/kotlin/dev/thelumiereguy/creative_coding_compose/utils/noise.kt: -------------------------------------------------------------------------------- 1 | package dev.thelumiereguy.creative_coding_compose.utils 2 | 3 | /** 4 | * Perlin noise implementation from 5 | * https://github.com/CuriousNikhil/k5-compose/blob/main/k5-compose/src/main/kotlin/math/Noise.kt 6 | */ 7 | 8 | import kotlin.math.absoluteValue 9 | import kotlin.math.floor 10 | 11 | /** 12 | * Generates perlin noise value in 1D for given x offset. 13 | * Linear sequence of noise values in a one-dimensional space, and we can ask for a value at a specific x-location whenever we want. 14 | * 15 | * @param [x] x offset on plane. 16 | * @return The value of noise at specific x location 17 | */ 18 | fun noise1D(x: Double): Double { 19 | return Perlin.noise(x, 0.0, 0.0).absoluteValue 20 | } 21 | 22 | /** 23 | * Generates perlin noise values for 2D for given x and y offset 24 | * 25 | * @param [x] [y] x and y offset 26 | * @return The perlin noise value at x and y location at given point it time 27 | */ 28 | fun noise2D(x: Double, y: Double): Double { 29 | return Perlin.noise(x, y, 0.0).absoluteValue 30 | } 31 | 32 | /** 33 | * Generates perlin noise values for 3D 34 | * 35 | * @param [x] [y] [z] x, y & z offset 36 | * @return The perlin noise value at (x, y, z) location at given point it time 37 | */ 38 | fun noise3D(x: Double, y: Double, z: Double): Double { 39 | return Perlin.noise(x, y, z).absoluteValue 40 | } 41 | 42 | /** 43 | * Represents a simple Perlon noise generator 44 | * This generates smooth noise which gives a normal distribution of random values. 45 | * Perlin noise https://en.wikipedia.org/wiki/Perlin_noise 46 | * from: https://rosettacode.org/wiki/Perlin_noise#Kotlin 47 | */ 48 | object Perlin { 49 | private val permutation = intArrayOf( 50 | 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 51 | 140, 36, 103, 30, 69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 52 | 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203, 117, 35, 11, 32, 53 | 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 54 | 74, 165, 71, 134, 139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 55 | 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245, 40, 244, 102, 143, 54, 56 | 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 57 | 200, 196, 135, 130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 58 | 52, 217, 226, 250, 124, 123, 5, 202, 38, 147, 118, 126, 255, 82, 85, 212, 59 | 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 60 | 119, 248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 61 | 129, 22, 39, 253, 19, 98, 108, 110, 79, 113, 224, 232, 178, 185, 112, 104, 62 | 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162, 241, 63 | 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 64 | 184, 84, 204, 176, 115, 121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 65 | 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66, 215, 61, 156, 180 66 | ) 67 | 68 | private val p = IntArray(512) { 69 | if (it < 256) permutation[it] else permutation[it - 256] 70 | } 71 | 72 | fun noise(x: Double, y: Double, z: Double): Double { 73 | // Find unit cube that contains point 74 | val xi = floor(x).toInt() and 255 75 | val yi = floor(y).toInt() and 255 76 | val zi = floor(z).toInt() and 255 77 | 78 | // Find relative x, y, z of point in cube 79 | val xx = x - floor(x) 80 | val yy = y - floor(y) 81 | val zz = z - floor(z) 82 | 83 | // Compute fade curves for each of xx, yy, zz 84 | val u = fade(xx) 85 | val v = fade(yy) 86 | val w = fade(zz) 87 | 88 | // Hash co-ordinates of the 8 cube corners 89 | // and add blended results from 8 corners of cube 90 | 91 | val a = p[xi] + yi 92 | val aa = p[a] + zi 93 | val ab = p[a + 1] + zi 94 | val b = p[xi + 1] + yi 95 | val ba = p[b] + zi 96 | val bb = p[b + 1] + zi 97 | 98 | return lerp( 99 | w, 100 | lerp( 101 | v, 102 | lerp( 103 | u, grad(p[aa], xx, yy, zz), 104 | grad(p[ba], xx - 1, yy, zz) 105 | ), 106 | lerp( 107 | u, grad(p[ab], xx, yy - 1, zz), 108 | grad(p[bb], xx - 1, yy - 1, zz) 109 | ) 110 | ), 111 | lerp( 112 | v, 113 | lerp( 114 | u, grad(p[aa + 1], xx, yy, zz - 1), 115 | grad(p[ba + 1], xx - 1, yy, zz - 1) 116 | ), 117 | lerp( 118 | u, grad(p[ab + 1], xx, yy - 1, zz - 1), 119 | grad(p[bb + 1], xx - 1, yy - 1, zz - 1) 120 | ) 121 | ) 122 | ) 123 | } 124 | 125 | private fun fade(t: Double) = t * t * t * (t * (t * 6 - 15) + 10) 126 | 127 | private fun lerp(t: Double, a: Double, b: Double) = a + t * (b - a) 128 | 129 | private fun grad(hash: Int, x: Double, y: Double, z: Double): Double { 130 | // Convert low 4 bits of hash code into 12 gradient directions 131 | val h = hash and 15 132 | val u = if (h < 8) x else y 133 | val v = if (h < 4) y else if (h == 12 || h == 14) x else z 134 | return (if ((h and 1) == 0) u else -u) + 135 | (if ((h and 2) == 0) v else -v) 136 | } 137 | } 138 | 139 | fun map( 140 | value: T, 141 | in_min: T, 142 | in_max: T, 143 | out_min: T, 144 | out_max: T, 145 | ): Float { 146 | return (value.toFloat() - in_min.toFloat()) * (out_max.toFloat() - out_min.toFloat()) / (in_max.toFloat() - in_min.toFloat()) + out_min.toFloat() 147 | } 148 | -------------------------------------------------------------------------------- /compose_common/src/desktopMain/kotlin/Main.kt: -------------------------------------------------------------------------------- 1 | import androidx.compose.foundation.background 2 | import androidx.compose.foundation.layout.fillMaxSize 3 | import androidx.compose.ui.Modifier 4 | import androidx.compose.ui.graphics.Color 5 | import androidx.compose.ui.window.Window 6 | import androidx.compose.ui.window.application 7 | import androidx.compose.ui.window.rememberWindowState 8 | import dev.thelumiereguy.creative_coding_compose.CreativeCodingExampleScreen 9 | 10 | fun main() = application { 11 | 12 | val windowState = rememberWindowState() 13 | 14 | Window( 15 | onCloseRequest = ::exitApplication, 16 | state = windowState, 17 | title = "Creative Coding Examples", 18 | ) { 19 | CreativeCodingExampleScreen( 20 | modifier = Modifier 21 | .fillMaxSize() 22 | .background(Color.Black) 23 | ) 24 | } 25 | } -------------------------------------------------------------------------------- /compose_common/src/jsMain/kotlin/BrowserViewportWindow.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Courtesy of Oliver O from 3 | * https://kotlinlang.slack.com/archives/C01F2HV7868/p1660083429206369?thread_ts=1660083398.571449&cid=C01F2HV7868 4 | */ 5 | 6 | @file:Suppress( 7 | "INVISIBLE_MEMBER", 8 | "INVISIBLE_REFERENCE", 9 | "EXPOSED_PARAMETER_TYPE" 10 | ) // WORKAROUND: ComposeWindow and ComposeLayer are internal 11 | 12 | import androidx.compose.runtime.Composable 13 | import androidx.compose.ui.window.ComposeWindow 14 | import kotlinx.browser.document 15 | import kotlinx.browser.window 16 | import org.w3c.dom.HTMLCanvasElement 17 | import org.w3c.dom.HTMLStyleElement 18 | import org.w3c.dom.HTMLTitleElement 19 | 20 | private const val CanvasElementId = "ComposeTarget" // Hardwired into ComposeWindow 21 | 22 | /** 23 | * A Skiko/Canvas-based top-level window using the browser's entire viewport. Supports resizing. 24 | */ 25 | fun BrowserViewportWindow( 26 | title: String = "Untitled", 27 | content: @Composable ComposeWindow.() -> Unit 28 | ) { 29 | 30 | 31 | val htmlHeadElement = document.head!! 32 | htmlHeadElement.appendChild( 33 | (document.createElement("style") as HTMLStyleElement).apply { 34 | type = "text/css" 35 | appendChild( 36 | document.createTextNode( 37 | """ 38 | html, body { 39 | overflow: hidden; 40 | margin: 0 !important; 41 | padding: 0 !important; 42 | } 43 | 44 | #$CanvasElementId { 45 | outline: none; 46 | } 47 | """.trimIndent() 48 | ) 49 | ) 50 | } 51 | ) 52 | 53 | fun HTMLCanvasElement.fillViewportSize() { 54 | setAttribute("width", "${window.innerWidth}") 55 | setAttribute("height", "${window.innerHeight}") 56 | } 57 | 58 | val canvas = (document.getElementById(CanvasElementId) as HTMLCanvasElement).apply { 59 | fillViewportSize() 60 | } 61 | 62 | ComposeWindow().apply { 63 | window.addEventListener("resize", { 64 | val scale = layer.layer.contentScale 65 | canvas.fillViewportSize() 66 | layer.layer.attachTo(canvas) 67 | layer.layer.needRedraw() 68 | layer.setSize((canvas.width / scale).toInt(), (canvas.height / scale).toInt()) 69 | }) 70 | 71 | // WORKAROUND: ComposeWindow does not implement `setTitle(title)` 72 | val htmlTitleElement = ( 73 | htmlHeadElement.getElementsByTagName("title").item(0) 74 | ?: document.createElement("title").also { htmlHeadElement.appendChild(it) } 75 | ) as HTMLTitleElement 76 | htmlTitleElement.textContent = title 77 | 78 | setContent { 79 | content(this) 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /compose_common/src/jsMain/kotlin/Main.kt: -------------------------------------------------------------------------------- 1 | import androidx.compose.foundation.background 2 | import androidx.compose.foundation.layout.fillMaxSize 3 | import androidx.compose.ui.Modifier 4 | import androidx.compose.ui.graphics.Color 5 | import dev.thelumiereguy.creative_coding_compose.CreativeCodingExampleScreen 6 | import org.jetbrains.skiko.wasm.onWasmReady 7 | 8 | fun main() { 9 | onWasmReady { 10 | BrowserViewportWindow("Creative Coding Examples") { 11 | CreativeCodingExampleScreen( 12 | modifier = Modifier 13 | .fillMaxSize() 14 | .background(Color.Black), 15 | ) 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /compose_common/src/jsMain/resources/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Sample 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /compose_common/src/uikitMain/kotlin/main.uikit.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020-2022 JetBrains s.r.o. and respective authors and developers. 3 | * Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file. 4 | */ 5 | 6 | // Use `xcodegen` first, then `open ./ComposeMinesweeper.xcodeproj` and then Run button in XCode. 7 | import androidx.compose.foundation.background 8 | import androidx.compose.foundation.layout.fillMaxSize 9 | import androidx.compose.ui.Modifier 10 | import androidx.compose.ui.draw.scale 11 | import androidx.compose.ui.graphics.Color 12 | import androidx.compose.ui.window.Application 13 | import dev.thelumiereguy.creative_coding_compose.CreativeCodingExampleScreen 14 | import kotlinx.cinterop.ObjCObjectBase 15 | import kotlinx.cinterop.autoreleasepool 16 | import kotlinx.cinterop.cstr 17 | import kotlinx.cinterop.memScoped 18 | import kotlinx.cinterop.toCValues 19 | import platform.Foundation.NSStringFromClass 20 | import platform.UIKit.UIApplication 21 | import platform.UIKit.UIApplicationDelegateProtocol 22 | import platform.UIKit.UIApplicationDelegateProtocolMeta 23 | import platform.UIKit.UIApplicationMain 24 | import platform.UIKit.UIResponder 25 | import platform.UIKit.UIResponderMeta 26 | import platform.UIKit.UIScreen 27 | import platform.UIKit.UIWindow 28 | 29 | /** 30 | * Courtesy of latest examples from 31 | * https://github.com/JetBrains/compose-jb/tree/59d4e677b61902d7e465b1c6ce24b95379e55271/experimental/examples/falling-balls-mpp 32 | */ 33 | fun main() { 34 | val args = emptyArray() 35 | memScoped { 36 | val argc = args.size + 1 37 | val argv = (arrayOf("skikoApp") + args).map { it.cstr.ptr }.toCValues() 38 | autoreleasepool { 39 | UIApplicationMain(argc, argv, null, NSStringFromClass(SkikoAppDelegate)) 40 | } 41 | } 42 | } 43 | 44 | class SkikoAppDelegate : UIResponder, UIApplicationDelegateProtocol { 45 | companion object : UIResponderMeta(), UIApplicationDelegateProtocolMeta 46 | 47 | @ObjCObjectBase.OverrideInit 48 | constructor() : super() 49 | 50 | private var _window: UIWindow? = null 51 | override fun window() = _window 52 | override fun setWindow(window: UIWindow?) { 53 | _window = window 54 | } 55 | 56 | override fun application( 57 | application: UIApplication, 58 | didFinishLaunchingWithOptions: Map? 59 | ): Boolean { 60 | window = UIWindow(frame = UIScreen.mainScreen.bounds) 61 | window!!.rootViewController = Application("CreativeCodingExamples") { 62 | CreativeCodingExampleScreen( 63 | Modifier 64 | .fillMaxSize() 65 | .background(Color.Black) 66 | ) 67 | } 68 | window!!.makeKeyAndVisible() 69 | return true 70 | } 71 | } 72 | 73 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | ########## 2 | # Jekyll # 3 | ####################################### 4 | _site 5 | .sass-cache 6 | .jekyll-metadata 7 | 8 | ######## 9 | # Ruby # 10 | ####################################### 11 | *.gem 12 | *.rbc 13 | /.config 14 | /coverage/ 15 | /InstalledFiles 16 | /pkg/ 17 | /spec/reports/ 18 | /spec/examples.txt 19 | /test/tmp/ 20 | /test/version_tmp/ 21 | /tmp/ 22 | 23 | # Used by dotenv library to load environment variables. 24 | # .env 25 | 26 | # Ignore Byebug command history file. 27 | .byebug_history 28 | 29 | ## Documentation cache and generated files: 30 | /.yardoc/ 31 | /_yardoc/ 32 | /doc/ 33 | /rdoc/ 34 | 35 | ## Environment normalization: 36 | /.bundle/ 37 | /vendor/bundle 38 | /lib/bundler/man/ 39 | 40 | # for a library or gem, you might want to ignore these files since the code is 41 | # intended to run in multiple environments; otherwise, check them in: 42 | Gemfile.lock 43 | .ruby-version 44 | .ruby-gemset 45 | 46 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: 47 | .rvmrc 48 | -------------------------------------------------------------------------------- /docs/.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | sudo: false 3 | 4 | rvm: 5 | - 2.5.5 6 | 7 | addons: 8 | apt: 9 | packages: 10 | - libcurl4-openssl-dev 11 | 12 | env: 13 | global: 14 | - NOKOGIRI_USE_SYSTEM_LIBRARIES=true # speeds up installation of html-proofer 15 | 16 | 17 | cache: bundler 18 | 19 | script: 20 | - bundle exec jekyll build 21 | - bundle exec htmlproofer ./_site --disable_external 22 | -------------------------------------------------------------------------------- /docs/404.html: -------------------------------------------------------------------------------- 1 | 14 | 15 |
16 |

404

17 | 18 |

Page not found :(

19 |

The requested page could not be found.

20 |
21 | -------------------------------------------------------------------------------- /docs/Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | # Hello! This is where you manage which Jekyll version is used to run. 4 | # When you want to use a different version, change it below, save the 5 | # file and run `bundle install`. Run Jekyll with `bundle exec`, like so: 6 | # 7 | # bundle exec jekyll serve 8 | # 9 | # This will help ensure the proper Jekyll version is running. 10 | # Happy Jekylling! 11 | # gem "jekyll", "~> 3.8.3" 12 | 13 | # This is the default theme for new Jekyll sites. You may change this to anything you like. 14 | #gem "minima", "~> 2.0" 15 | 16 | # If you want to use GitHub Pages, remove the "gem "jekyll"" above and 17 | # uncomment the line below. To upgrade, run `bundle update github-pages`. 18 | gem "github-pages", group: :jekyll_plugins 19 | 20 | # If you have any plugins, put them here! 21 | group :jekyll_plugins do 22 | gem "jekyll-feed", "~> 0.9" 23 | gem "jekyll-seo-tag", "~> 2.1" 24 | end 25 | 26 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem 27 | gem "tzinfo-data", platforms: [:mingw, :mswin, :x64_mingw, :jruby] 28 | 29 | # Performance-booster for watching directories on Windows 30 | gem "wdm", "~> 0.1.0" if Gem.win_platform? 31 | 32 | # Validate HTML output 33 | gem 'html-proofer' 34 | 35 | gem "webrick", "~> 1.7" 36 | -------------------------------------------------------------------------------- /docs/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 João Neto 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/_artworks/01.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Circle Pattern 3 | caption: Created with Processing and Jetbrains Compose 4 | image_thumb: https://user-images.githubusercontent.com/46375353/198363401-dbf40704-94b1-4e8d-813b-3ee9b806e137.gif 5 | image: https://user-images.githubusercontent.com/46375353/198087187-afb6c634-06c6-4138-9346-46a12e9ab711.png 6 | link_processing: https://github.com/thelumiereguy/CreativeCodingExamples/blob/master/processing/src/main/kotlin/dev/thelumiereguy/examples/circle_pattern/CirclePattern.kt 7 | link_compose: https://github.com/thelumiereguy/CreativeCodingExamples/blob/master/compose_common/src/commonMain/kotlin/dev/thelumiereguy/creative_coding_compose/examples/circle_pattern/DrawCirclePatternComposable.kt 8 | --- 9 | -------------------------------------------------------------------------------- /docs/_artworks/02.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Circleception 3 | caption: Created with Processing 4 | image_thumb: https://user-images.githubusercontent.com/46375353/198364447-ad6d860c-a776-49f6-a215-757417702d49.gif 5 | image: https://user-images.githubusercontent.com/46375353/198087197-dfc579b0-2208-4498-a6b1-12b28b9e7161.png 6 | link_processing: https://github.com/thelumiereguy/CreativeCodingExamples/blob/master/processing/src/main/kotlin/dev/thelumiereguy/examples/circleception/Circleception.kt 7 | 8 | --- 9 | -------------------------------------------------------------------------------- /docs/_artworks/03.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Divine Intervention 3 | caption: Created with Processing 4 | image_thumb: https://drive.google.com/uc?export=download&id=1M9B8kuKjEy1veIsqZxa6m8BV2_mVOGzP 5 | image: https://drive.google.com/uc?export=download&id=1M9B8kuKjEy1veIsqZxa6m8BV2_mVOGzP 6 | link_processing: https://github.com/thelumiereguy/CreativeCodingExamples/blob/master/processing/src/main/kotlin/dev/thelumiereguy/examples/divine_intervention/DivineIntervention.kt 7 | --- 8 | -------------------------------------------------------------------------------- /docs/_artworks/04.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Fractal Spirograph 3 | caption: Created with Processing 4 | image_thumb: https://user-images.githubusercontent.com/46375353/198364488-38df5bb5-05f5-4753-baaf-b1e194251d9e.gif 5 | image: https://user-images.githubusercontent.com/46375353/198087218-e460b2b8-dac5-4173-a298-e87fdfa11cad.png 6 | link_processing: https://github.com/thelumiereguy/CreativeCodingExamples/blob/master/processing/src/main/kotlin/dev/thelumiereguy/examples/fractal_spiralograph/FractalSpirograph.kt 7 | --- 8 | -------------------------------------------------------------------------------- /docs/_artworks/05.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Hexagonal Mandela 3 | caption: Created with Processing 4 | image_thumb: https://user-images.githubusercontent.com/46375353/198365354-fd2e3353-efef-4e27-8095-9d841c1b918d.gif 5 | image: https://user-images.githubusercontent.com/46375353/198087222-02b6cbed-0f48-4f88-b30d-2791d90b5240.png 6 | link_processing: https://github.com/thelumiereguy/CreativeCodingExamples/blob/master/processing/src/main/kotlin/dev/thelumiereguy/examples/hexagonal_mandela/HexagonalMandela.kt 7 | --- 8 | -------------------------------------------------------------------------------- /docs/_artworks/06.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Iris 3 | caption: Created with Processing 4 | image_thumb: https://user-images.githubusercontent.com/46375353/198365894-f4c40889-eba8-428f-b777-f476427ac9cd.gif 5 | image: https://user-images.githubusercontent.com/46375353/198087241-d9350f05-551f-495d-9a46-f36154d98638.png 6 | link_processing: https://github.com/thelumiereguy/CreativeCodingExamples/blob/master/processing/src/main/kotlin/dev/thelumiereguy/examples/iris/Iris.kt 7 | --- 8 | -------------------------------------------------------------------------------- /docs/_artworks/07.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Phyllotaxis 3 | caption: Created with Processing 4 | image_thumb: https://user-images.githubusercontent.com/46375353/198366227-3757faa9-2029-42ca-98c8-2caf64fa2ec2.gif 5 | image: https://user-images.githubusercontent.com/46375353/198087234-ff872a75-6e62-441b-80ba-32dd740f7816.png 6 | link_processing: https://github.com/thelumiereguy/CreativeCodingExamples/blob/master/processing/src/main/kotlin/dev/thelumiereguy/examples/phyllotaxis/Phyllotaxis.kt 7 | --- 8 | -------------------------------------------------------------------------------- /docs/_artworks/08.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Black Hole 3 | caption: Created with K5-Compose 4 | image_thumb: https://user-images.githubusercontent.com/46375353/197863760-f379073c-f6e1-452f-9130-cd6c8a76470d.png 5 | image: https://user-images.githubusercontent.com/46375353/197863760-f379073c-f6e1-452f-9130-cd6c8a76470d.png 6 | --- 7 | -------------------------------------------------------------------------------- /docs/_artworks/09.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Whirlpool 3 | caption: Created with Processing 4 | image_thumb: https://user-images.githubusercontent.com/46375353/198366799-d021c635-950e-420e-b9a6-5a016d6ada7e.gif 5 | image: https://user-images.githubusercontent.com/46375353/198087181-212d60c9-2bfb-4ce9-a0d9-361da8cfa7d4.png 6 | link_processing: https://github.com/thelumiereguy/CreativeCodingExamples/blob/master/processing/src/main/kotlin/dev/thelumiereguy/examples/whirlpool/Whirlpool.kt 7 | --- 8 | -------------------------------------------------------------------------------- /docs/_artworks/10.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Animated Shapes 3 | caption: Created with Jetbrains Compose 4 | image_thumb: https://user-images.githubusercontent.com/46375353/198367232-0881b84d-9653-4e4d-89ca-8eb6d3e261fb.gif 5 | image: https://user-images.githubusercontent.com/46375353/198090516-77dcebf0-71a7-41da-b131-8f2e109f7dac.png 6 | link_compose: https://github.com/thelumiereguy/CreativeCodingExamples/blob/master/compose_common/src/commonMain/kotlin/dev/thelumiereguy/creative_coding_compose/examples/animated_shapes/AnimatedShapes.kt 7 | --- 8 | -------------------------------------------------------------------------------- /docs/_artworks/11.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Bouncy Balls 3 | caption: Created with Jetbrains Compose 4 | image_thumb: https://user-images.githubusercontent.com/46375353/198367607-b1e26997-2c94-483a-94e3-bdaa4086f277.gif 5 | image: https://user-images.githubusercontent.com/46375353/198090492-10dbfa7d-cf9a-4d96-b3df-01917c4e1a3c.png 6 | link_compose: https://github.com/thelumiereguy/CreativeCodingExamples/blob/master/compose_common/src/androidMain/kotlin/dev/thelumiereguy/creative_coding_compose/examples/bouncy_balls_sound/BouncyBalls.kt 7 | --- 8 | -------------------------------------------------------------------------------- /docs/_artworks/12.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Cantor Circles 3 | caption: Created with Jetbrains Compose 4 | image_thumb: https://user-images.githubusercontent.com/46375353/198368110-a67da834-5674-4c4d-ae7f-443b2f789008.gif 5 | image: https://user-images.githubusercontent.com/46375353/198087124-23e5692c-2b45-42d5-9a76-009924a4ef0d.png 6 | link_compose: https://github.com/thelumiereguy/CreativeCodingExamples/blob/master/compose_common/src/commonMain/kotlin/dev/thelumiereguy/creative_coding_compose/examples/cantor_circles/CantorCirclePattern.kt 7 | --- 8 | -------------------------------------------------------------------------------- /docs/_artworks/13.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Dot Product Shading 3 | caption: Created with Jetbrains Compose 4 | image_thumb: https://user-images.githubusercontent.com/46375353/198368856-25bf8f30-995a-4889-8878-1861efacabc4.gif 5 | image: https://user-images.githubusercontent.com/46375353/198087202-3016675b-db31-4c24-9040-0edc8b2b3d45.png 6 | link_compose: https://github.com/thelumiereguy/CreativeCodingExamples/blob/master/compose_common/src/commonMain/kotlin/dev/thelumiereguy/creative_coding_compose/examples/dot_product_shading/DotProductShading.kt 7 | --- 8 | -------------------------------------------------------------------------------- /docs/_artworks/14.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Stroke Ripples 3 | caption: Created with Jetbrains Compose 4 | image_thumb: https://user-images.githubusercontent.com/46375353/198370069-8d3d4595-565c-493b-847c-2f0cb7a2d19e.gif 5 | image: https://user-images.githubusercontent.com/46375353/198087247-53abf143-f1ff-4f06-9d50-a0e9176ac4a8.png 6 | link_compose: https://github.com/thelumiereguy/CreativeCodingExamples/blob/master/compose_common/src/commonMain/kotlin/dev/thelumiereguy/creative_coding_compose/examples/stroked_ripples/StrokedRipples.kt 7 | --- 8 | -------------------------------------------------------------------------------- /docs/_artworks/15.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Torus 3 | caption: Created with Jetbrains Compose 4 | image_thumb: https://user-images.githubusercontent.com/46375353/198370455-ede339ad-8328-4a07-9ed1-ee07e456b023.gif 5 | image: https://user-images.githubusercontent.com/46375353/198087254-4c61e0ba-be47-4f35-982e-4a68c1a53766.png 6 | link_compose: https://github.com/thelumiereguy/CreativeCodingExamples/blob/master/compose_common/src/commonMain/kotlin/dev/thelumiereguy/creative_coding_compose/examples/torus/Torus.kt 7 | --- 8 | -------------------------------------------------------------------------------- /docs/_artworks/16.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Cubic Sphere 3 | caption: Created with Jetbrains Compose 4 | image_thumb: https://user-images.githubusercontent.com/46375353/198370824-02df2c2f-f9c4-4e1b-86c7-de93fd774dbe.gif 5 | image: https://user-images.githubusercontent.com/46375353/198087265-c6417e7f-e1f2-436d-aebf-ce765c475d0a.png 6 | link_compose: https://github.com/thelumiereguy/CreativeCodingExamples/blob/master/compose_common/src/commonMain/kotlin/dev/thelumiereguy/creative_coding_compose/examples/cubic_sphere/CubicSphere.kt 7 | --- 8 | -------------------------------------------------------------------------------- /docs/_artworks/17.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: A Basic Flow Field 3 | caption: Created with Jetbrains Compose 4 | image_thumb: https://user-images.githubusercontent.com/46375353/198372172-126e472b-2481-412d-b3b3-a833dd716499.gif 5 | image: https://user-images.githubusercontent.com/46375353/198090505-bdab963d-d5f6-4fba-8982-cc5a0578b8f3.png 6 | link_compose: https://github.com/thelumiereguy/CreativeCodingExamples/blob/master/compose_common/src/commonMain/kotlin/dev/thelumiereguy/creative_coding_compose/examples/basic_flow_field/BasicFlowField.kt 7 | --- 8 | -------------------------------------------------------------------------------- /docs/_artworks/18.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Flow Field Variation 3 | caption: Created with Jetbrains Compose 4 | image_thumb: https://user-images.githubusercontent.com/46375353/198372821-da79a393-3eca-4c72-b369-ff6a25fb8a75.gif 5 | image: https://user-images.githubusercontent.com/46375353/198087205-c9967f20-e761-4cf1-b1c0-8128fac3f89e.png 6 | link_compose: https://github.com/thelumiereguy/CreativeCodingExamples/blob/master/compose_common/src/commonMain/kotlin/dev/thelumiereguy/creative_coding_compose/examples/flow_field_patterns/FlowFieldPattern1.kt 7 | --- 8 | -------------------------------------------------------------------------------- /docs/_artworks/19.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Flow Field Variation 2 3 | caption: Created with Jetbrains Compose 4 | image_thumb: https://user-images.githubusercontent.com/46375353/198373310-245ae136-c2ea-4602-a247-38ab4b6a4aa5.gif 5 | image: https://user-images.githubusercontent.com/46375353/198087209-b3230738-7290-42bd-9b7a-5e51c078b316.png 6 | link_compose: https://github.com/thelumiereguy/CreativeCodingExamples/blob/master/compose_common/src/commonMain/kotlin/dev/thelumiereguy/creative_coding_compose/examples/flow_field_patterns/FlowFieldPattern2.kt 7 | --- 8 | -------------------------------------------------------------------------------- /docs/_artworks/20.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Rippled Cubes 3 | caption: Created with Jetbrains Compose 4 | image_thumb: https://user-images.githubusercontent.com/46375353/198373849-647662d3-987a-4546-a86d-bd182fa50363.gif 5 | image: https://user-images.githubusercontent.com/46375353/198087239-b690d5ff-21c6-409d-9754-2b4451d1e94c.png 6 | link_compose: https://github.com/thelumiereguy/CreativeCodingExamples/blob/master/compose_common/src/commonMain/kotlin/dev/thelumiereguy/creative_coding_compose/examples/rippled_cubes/RippledCubes.kt 7 | --- 8 | -------------------------------------------------------------------------------- /docs/_artworks/21.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Mandela pattern 1 3 | caption: Created with Jetbrains Compose 4 | image_thumb: https://user-images.githubusercontent.com/46375353/198374696-5102071c-4d3f-4be3-af69-6dc977c6204f.gif 5 | image: https://user-images.githubusercontent.com/46375353/198087228-ffda909e-1aa0-4dde-8284-c845ab9263bf.png 6 | link_compose: https://github.com/thelumiereguy/CreativeCodingExamples/blob/master/compose_common/src/commonMain/kotlin/dev/thelumiereguy/creative_coding_compose/examples/mandela_patterns/MandelaPattern1.kt 7 | --- 8 | -------------------------------------------------------------------------------- /docs/_artworks/22.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Particles 3 | caption: Created with Jetbrains Compose 4 | image_thumb: https://user-images.githubusercontent.com/46375353/198375110-ec8a822d-be6d-4e3b-bc24-c81857ee614a.gif 5 | image: https://user-images.githubusercontent.com/46375353/198087230-00ca1a11-c700-428e-adcb-ad341356f3c8.png 6 | link_compose: https://github.com/thelumiereguy/CreativeCodingExamples/blob/master/compose_common/src/commonMain/kotlin/dev/thelumiereguy/creative_coding_compose/examples/particles/ParticleSet.kt 7 | --- 8 | -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | title: Creative Coding Examples 2 | author: Piyush Pradeepkumar & Nikhil Chaudhari 3 | email: 4 | description: >- 5 | A collection of Creative Coding examples by me & other artists. 6 | We've used Jetbrains Compose and Processing (Java Edition) to create these. 7 | 8 | social: 9 | twitterP: thelumiereguy 10 | githubP: thelumiereguy 11 | twitterN: curiousnikhyl 12 | githubN: curiousnikhil 13 | 14 | # Build settings 15 | markdown: kramdown 16 | plugins: 17 | - jekyll-feed 18 | - jekyll-seo-tag 19 | 20 | collections: 21 | artworks: 22 | output: true 23 | -------------------------------------------------------------------------------- /docs/_includes/footer.html: -------------------------------------------------------------------------------- 1 | 19 | -------------------------------------------------------------------------------- /docs/_includes/google-analytics.html: -------------------------------------------------------------------------------- 1 | 2 | 10 | -------------------------------------------------------------------------------- /docs/_includes/head.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {%- seo -%} 6 | 7 | {%- feed_meta -%} 8 | 9 | {%- if jekyll.environment == 'production' and site.google_analytics -%} 10 | {%- include google-analytics.html -%} 11 | {%- endif -%} 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /docs/_includes/header.html: -------------------------------------------------------------------------------- 1 | 2 | 10 | -------------------------------------------------------------------------------- /docs/_layouts/creativecanvas.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {%- include head.html -%} 5 | 6 | 7 | 8 |
9 | 10 | {%- include header.html -%} 11 | 12 |
13 | {% for art in site.artworks %} 14 | 31 | {% endfor %} 32 | 33 | 34 | 35 |
36 | 37 | {%- include footer.html -%} 38 | 39 |
40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /docs/assets/css/images/arrow.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /docs/assets/css/images/close.svg: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /docs/assets/css/images/spinner.svg: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /docs/assets/css/noscript.css: -------------------------------------------------------------------------------- 1 | /* 2 | Multiverse by HTML5 UP 3 | html5up.net | @ajlkn 4 | Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | */ 6 | 7 | /* Wrapper */ 8 | 9 | body.is-preload #wrapper:before { 10 | display: none; 11 | } 12 | 13 | /* Main */ 14 | 15 | body.is-preload #main .thumb { 16 | pointer-events: auto; 17 | opacity: 1; 18 | } 19 | 20 | /* Header */ 21 | 22 | body.is-preload #header { 23 | -moz-transform: none; 24 | -webkit-transform: none; 25 | -ms-transform: none; 26 | transform: none; 27 | } -------------------------------------------------------------------------------- /docs/assets/js/breakpoints.min.js: -------------------------------------------------------------------------------- 1 | /* breakpoints.js v1.0 | @ajlkn | MIT licensed */ 2 | var breakpoints=function(){"use strict";function e(e){t.init(e)}var t={list:null,media:{},events:[],init:function(e){t.list=e,window.addEventListener("resize",t.poll),window.addEventListener("orientationchange",t.poll),window.addEventListener("load",t.poll),window.addEventListener("fullscreenchange",t.poll)},active:function(e){var n,a,s,i,r,d,c;if(!(e in t.media)){if(">="==e.substr(0,2)?(a="gte",n=e.substr(2)):"<="==e.substr(0,2)?(a="lte",n=e.substr(2)):">"==e.substr(0,1)?(a="gt",n=e.substr(1)):"<"==e.substr(0,1)?(a="lt",n=e.substr(1)):"!"==e.substr(0,1)?(a="not",n=e.substr(1)):(a="eq",n=e),n&&n in t.list)if(i=t.list[n],Array.isArray(i)){if(r=parseInt(i[0]),d=parseInt(i[1]),isNaN(r)){if(isNaN(d))return;c=i[1].substr(String(d).length)}else c=i[0].substr(String(r).length);if(isNaN(r))switch(a){case"gte":s="screen";break;case"lte":s="screen and (max-width: "+d+c+")";break;case"gt":s="screen and (min-width: "+(d+1)+c+")";break;case"lt":s="screen and (max-width: -1px)";break;case"not":s="screen and (min-width: "+(d+1)+c+")";break;default:s="screen and (max-width: "+d+c+")"}else if(isNaN(d))switch(a){case"gte":s="screen and (min-width: "+r+c+")";break;case"lte":s="screen";break;case"gt":s="screen and (max-width: -1px)";break;case"lt":s="screen and (max-width: "+(r-1)+c+")";break;case"not":s="screen and (max-width: "+(r-1)+c+")";break;default:s="screen and (min-width: "+r+c+")"}else switch(a){case"gte":s="screen and (min-width: "+r+c+")";break;case"lte":s="screen and (max-width: "+d+c+")";break;case"gt":s="screen and (min-width: "+(d+1)+c+")";break;case"lt":s="screen and (max-width: "+(r-1)+c+")";break;case"not":s="screen and (max-width: "+(r-1)+c+"), screen and (min-width: "+(d+1)+c+")";break;default:s="screen and (min-width: "+r+c+") and (max-width: "+d+c+")"}}else s="("==i.charAt(0)?"screen and "+i:i;t.media[e]=!!s&&s}return t.media[e]!==!1&&window.matchMedia(t.media[e]).matches},on:function(e,n){t.events.push({query:e,handler:n,state:!1}),t.active(e)&&n()},poll:function(){var e,n;for(e=0;e0:!!("ontouchstart"in window),e.mobile="wp"==e.os||"android"==e.os||"ios"==e.os||"bb"==e.os}};return e.init(),e}();!function(e,n){"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?module.exports=n():e.browser=n()}(this,function(){return browser}); 3 | -------------------------------------------------------------------------------- /docs/assets/sass/base/_page.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Multiverse by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Basic */ 8 | 9 | // MSIE: Required for IEMobile. 10 | @-ms-viewport { 11 | width: device-width; 12 | } 13 | 14 | // MSIE: Prevents scrollbar from overlapping content. 15 | body { 16 | -ms-overflow-style: scrollbar; 17 | } 18 | 19 | // Ensures page width is always >=320px. 20 | @include breakpoint('<=xsmall') { 21 | html, body { 22 | min-width: 320px; 23 | } 24 | } 25 | 26 | // Set box model to border-box. 27 | // Based on css-tricks.com/inheriting-box-sizing-probably-slightly-better-best-practice 28 | html { 29 | box-sizing: border-box; 30 | } 31 | 32 | *, *:before, *:after { 33 | box-sizing: inherit; 34 | } 35 | 36 | body { 37 | background: _palette(bg); 38 | 39 | // Stops initial animations until page loads. 40 | &.is-preload { 41 | *, *:before, *:after { 42 | @include vendor('animation', 'none !important'); 43 | @include vendor('transition', 'none !important'); 44 | } 45 | } 46 | 47 | // Prevents animations while resizing. 48 | &.is-resizing { 49 | *, *:before, *:after { 50 | @include vendor('animation', 'none !important'); 51 | @include vendor('transition', 'none !important'); 52 | } 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /docs/assets/sass/base/_reset.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Multiverse by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | // Reset. 8 | // Based on meyerweb.com/eric/tools/css/reset (v2.0 | 20110126 | License: public domain) 9 | 10 | html, body, div, span, applet, object, 11 | iframe, h1, h2, h3, h4, h5, h6, p, blockquote, 12 | pre, a, abbr, acronym, address, big, cite, 13 | code, del, dfn, em, img, ins, kbd, q, s, samp, 14 | small, strike, strong, sub, sup, tt, var, b, 15 | u, i, center, dl, dt, dd, ol, ul, li, fieldset, 16 | form, label, legend, table, caption, tbody, 17 | tfoot, thead, tr, th, td, article, aside, 18 | canvas, details, embed, figure, figcaption, 19 | footer, header, hgroup, menu, nav, output, ruby, 20 | section, summary, time, mark, audio, video { 21 | margin: 0; 22 | padding: 0; 23 | border: 0; 24 | font-size: 100%; 25 | font: inherit; 26 | vertical-align: baseline; 27 | } 28 | 29 | article, aside, details, figcaption, figure, 30 | footer, header, hgroup, menu, nav, section { 31 | display: block; 32 | } 33 | 34 | body { 35 | line-height: 1; 36 | } 37 | 38 | ol, ul { 39 | list-style:none; 40 | } 41 | 42 | blockquote, q { 43 | quotes: none; 44 | 45 | &:before, 46 | &:after { 47 | content: ''; 48 | content: none; 49 | } 50 | } 51 | 52 | table { 53 | border-collapse: collapse; 54 | border-spacing: 0; 55 | } 56 | 57 | body { 58 | -webkit-text-size-adjust: none; 59 | } 60 | 61 | mark { 62 | background-color: transparent; 63 | color: inherit; 64 | } 65 | 66 | input::-moz-focus-inner { 67 | border: 0; 68 | padding: 0; 69 | } 70 | 71 | input, select, textarea { 72 | -moz-appearance: none; 73 | -webkit-appearance: none; 74 | -ms-appearance: none; 75 | appearance: none; 76 | } -------------------------------------------------------------------------------- /docs/assets/sass/base/_typography.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Multiverse by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Type */ 8 | 9 | body, input, select, textarea { 10 | color: _palette(fg); 11 | font-family: _font(family); 12 | font-size: 15pt; 13 | font-weight: _font(weight); 14 | letter-spacing: _font(kerning); 15 | line-height: 1.65; 16 | 17 | @include breakpoint('<=xlarge') { 18 | font-size: 11pt; 19 | } 20 | } 21 | 22 | a { 23 | @include vendor('transition', ( 24 | 'color #{_duration(transition)} ease-in-out', 25 | 'border-bottom-color #{_duration(transition)} ease-in-out' 26 | )); 27 | border-bottom: dotted 1px; 28 | color: _palette(accent1); 29 | text-decoration: none; 30 | 31 | &:hover { 32 | border-bottom-color: transparent; 33 | color: _palette(accent1) !important; 34 | } 35 | } 36 | 37 | strong, b { 38 | color: _palette(fg-bold); 39 | font-weight: _font(weight-bold); 40 | } 41 | 42 | em, i { 43 | font-style: italic; 44 | } 45 | 46 | p { 47 | margin: 0 0 _size(element-margin) 0; 48 | } 49 | 50 | h1, h2, h3, h4, h5, h6 { 51 | color: _palette(fg-bold); 52 | font-weight: _font(weight-bold); 53 | letter-spacing: _font(kerning-alt); 54 | line-height: 1.5; 55 | margin: 0 0 (_size(element-margin) * 0.5) 0; 56 | text-transform: uppercase; 57 | 58 | a { 59 | color: inherit; 60 | text-decoration: none; 61 | } 62 | } 63 | 64 | h1 { 65 | font-size: 2em; 66 | } 67 | 68 | h2 { 69 | font-size: 1.25em; 70 | } 71 | 72 | h3 { 73 | font-size: 1.1em; 74 | } 75 | 76 | h4 { 77 | font-size: 1em; 78 | } 79 | 80 | h5 { 81 | font-size: 0.9em; 82 | } 83 | 84 | h6 { 85 | font-size: 0.7em; 86 | } 87 | 88 | @include breakpoint('<=small') { 89 | h2 { 90 | font-size: 1em; 91 | } 92 | 93 | h3 { 94 | font-size: 0.9em; 95 | } 96 | 97 | h4 { 98 | font-size: 0.8em; 99 | } 100 | 101 | h5 { 102 | font-size: 0.7em; 103 | } 104 | 105 | h6 { 106 | font-size: 0.7em; 107 | } 108 | } 109 | 110 | sub { 111 | font-size: 0.8em; 112 | position: relative; 113 | top: 0.5em; 114 | } 115 | 116 | sup { 117 | font-size: 0.8em; 118 | position: relative; 119 | top: -0.5em; 120 | } 121 | 122 | blockquote { 123 | border-left: 4px _palette(border); 124 | font-style: italic; 125 | margin: 0 0 _size(element-margin) 0; 126 | padding: (_size(element-margin) / 4) 0 (_size(element-margin) / 4) _size(element-margin); 127 | } 128 | 129 | code { 130 | background: _palette(border-bg); 131 | border: solid 1px _palette(border); 132 | font-family: _font(family-fixed); 133 | font-size: 0.9em; 134 | margin: 0 0.25em; 135 | padding: 0.25em 0.65em; 136 | } 137 | 138 | pre { 139 | -webkit-overflow-scrolling: touch; 140 | font-family: _font(family-fixed); 141 | font-size: 0.9em; 142 | margin: 0 0 _size(element-margin) 0; 143 | 144 | code { 145 | display: block; 146 | line-height: 1.75; 147 | padding: 1em 1.5em; 148 | overflow-x: auto; 149 | } 150 | } 151 | 152 | hr { 153 | border: 0; 154 | border-bottom: solid 1px _palette(border); 155 | margin: _size(element-margin) 0; 156 | 157 | &.major { 158 | margin: (_size(element-margin) * 1.5) 0; 159 | } 160 | } 161 | 162 | .align-left { 163 | text-align: left; 164 | } 165 | 166 | .align-center { 167 | text-align: center; 168 | } 169 | 170 | .align-right { 171 | text-align: right; 172 | } -------------------------------------------------------------------------------- /docs/assets/sass/components/_actions.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Multiverse by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Actions */ 8 | 9 | ul.actions { 10 | @include vendor('display', 'flex'); 11 | cursor: default; 12 | list-style: none; 13 | margin-left: (_size(element-margin) * -0.5); 14 | padding-left: 0; 15 | 16 | li { 17 | padding: 0 0 0 (_size(element-margin) * 0.5); 18 | vertical-align: middle; 19 | } 20 | 21 | &.special { 22 | @include vendor('justify-content', 'center'); 23 | width: 100%; 24 | margin-left: 0; 25 | 26 | li { 27 | &:first-child { 28 | padding-left: 0; 29 | } 30 | } 31 | } 32 | 33 | &.stacked { 34 | @include vendor('flex-direction', 'column'); 35 | margin-left: 0; 36 | 37 | li { 38 | padding: (_size(element-margin) * 0.65) 0 0 0; 39 | 40 | &:first-child { 41 | padding-top: 0; 42 | } 43 | } 44 | } 45 | 46 | &.fit { 47 | width: calc(100% + #{_size(element-margin) * 0.5}); 48 | 49 | li { 50 | @include vendor('flex-grow', '1'); 51 | @include vendor('flex-shrink', '1'); 52 | width: 100%; 53 | 54 | > * { 55 | width: 100%; 56 | } 57 | } 58 | 59 | &.stacked { 60 | width: 100%; 61 | } 62 | } 63 | 64 | @include breakpoint('<=xsmall') { 65 | &:not(.fixed) { 66 | @include vendor('flex-direction', 'column'); 67 | margin-left: 0; 68 | width: 100% !important; 69 | 70 | li { 71 | @include vendor('flex-grow', '1'); 72 | @include vendor('flex-shrink', '1'); 73 | padding: (_size(element-margin) * 0.5) 0 0 0; 74 | text-align: center; 75 | width: 100%; 76 | 77 | > * { 78 | width: 100%; 79 | } 80 | 81 | &:first-child { 82 | padding-top: 0; 83 | } 84 | 85 | input[type="submit"], 86 | input[type="reset"], 87 | input[type="button"], 88 | button, 89 | .button { 90 | width: 100%; 91 | 92 | &.icon { 93 | &:before { 94 | margin-left: -0.5rem; 95 | } 96 | } 97 | } 98 | } 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /docs/assets/sass/components/_button.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Multiverse by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Button */ 8 | 9 | input[type="submit"], 10 | input[type="reset"], 11 | input[type="button"], 12 | button, 13 | .button { 14 | @include vendor('appearance', 'none'); 15 | @include vendor('transition', ( 16 | 'background-color #{_duration(transition)} ease-in-out', 17 | 'box-shadow #{_duration(transition)} ease-in-out', 18 | 'color #{_duration(transition)} ease-in-out' 19 | )); 20 | background-color: transparent; 21 | border: 0; 22 | border-radius: 0; 23 | box-shadow: inset 0 0 0 2px _palette(border); 24 | color: _palette(fg-bold) !important; 25 | cursor: pointer; 26 | display: inline-block; 27 | font-size: 0.9em; 28 | font-weight: _font(weight-bold); 29 | height: _size(element-height) * (1 / 0.9); 30 | letter-spacing: _font(kerning-alt); 31 | line-height: _size(element-height) * (1 / 0.9); 32 | padding: 0 2.5em; 33 | text-align: center; 34 | text-decoration: none; 35 | text-transform: uppercase; 36 | white-space: nowrap; 37 | 38 | &:hover { 39 | box-shadow: inset 0 0 0 2px _palette(accent1); 40 | color: _palette(accent1) !important; 41 | 42 | &:active { 43 | background-color: transparentize(_palette(accent1), 0.85); 44 | color: _palette(accent1) !important; 45 | } 46 | } 47 | 48 | &.icon { 49 | padding-left: 1.35em; 50 | 51 | &:before { 52 | margin-right: 0.5em; 53 | } 54 | } 55 | 56 | &.fit { 57 | width: 100%; 58 | } 59 | 60 | &.small { 61 | font-size: 0.8em; 62 | } 63 | 64 | &.large { 65 | font-size: 1.35em; 66 | } 67 | 68 | &.primary { 69 | background-color: _palette(accent1); 70 | box-shadow: none; 71 | 72 | &:hover { 73 | background-color: lighten(_palette(accent1), 10); 74 | color: _palette(fg-bold) !important; 75 | 76 | &:active { 77 | background-color: darken(_palette(accent1), 10); 78 | } 79 | } 80 | } 81 | 82 | &.disabled, 83 | &:disabled { 84 | @include vendor('pointer-events', 'none'); 85 | opacity: 0.35; 86 | } 87 | } -------------------------------------------------------------------------------- /docs/assets/sass/components/_icon.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Multiverse by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Icon */ 8 | 9 | .icon { 10 | @include icon; 11 | border-bottom: none; 12 | position: relative; 13 | 14 | > .label { 15 | display: none; 16 | } 17 | 18 | &:before { 19 | line-height: inherit; 20 | } 21 | 22 | &.solid { 23 | &:before { 24 | font-weight: 900; 25 | } 26 | } 27 | 28 | &.brands { 29 | &:before { 30 | font-family: 'Font Awesome 5 Brands'; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /docs/assets/sass/components/_icons.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Multiverse by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Icons */ 8 | 9 | ul.icons { 10 | cursor: default; 11 | list-style: none; 12 | padding-left: 0; 13 | 14 | li { 15 | display: inline-block; 16 | padding: 0 1em 0 0; 17 | 18 | &:last-child { 19 | padding-right: 0; 20 | } 21 | 22 | .icon { 23 | color: _palette(fg-light); 24 | 25 | &:before { 26 | font-size: 1.5em; 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /docs/assets/sass/components/_list.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Multiverse by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* List */ 8 | 9 | ol { 10 | list-style: decimal; 11 | margin: 0 0 _size(element-margin) 0; 12 | padding-left: 1.25em; 13 | 14 | li { 15 | padding-left: 0.25em; 16 | } 17 | } 18 | 19 | ul { 20 | list-style: disc; 21 | margin: 0 0 _size(element-margin) 0; 22 | padding-left: 1em; 23 | 24 | li { 25 | padding-left: 0.5em; 26 | } 27 | 28 | &.alt { 29 | list-style: none; 30 | padding-left: 0; 31 | 32 | li { 33 | border-top: solid 1px _palette(border); 34 | padding: 0.5em 0; 35 | 36 | &:first-child { 37 | border-top: 0; 38 | padding-top: 0; 39 | } 40 | } 41 | } 42 | } 43 | 44 | dl { 45 | margin: 0 0 _size(element-margin) 0; 46 | 47 | dt { 48 | display: block; 49 | font-weight: _font(weight-bold); 50 | margin: 0 0 (_size(element-margin) * 0.5) 0; 51 | } 52 | 53 | dd { 54 | margin-left: _size(element-margin); 55 | } 56 | } -------------------------------------------------------------------------------- /docs/assets/sass/components/_panel.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Multiverse by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Panel */ 8 | 9 | .panel { 10 | @include padding(4em, 4em); 11 | @include vendor('transform', 'translateY(100vh)'); 12 | @include vendor('transition', 'transform #{_duration(panel)} ease'); 13 | -webkit-overflow-scrolling: touch; 14 | background: transparentize(_palette(bg), 0.025); 15 | bottom: _size(header); 16 | left: 0; 17 | max-height: calc(80vh - #{_size(header)}); 18 | overflow-y: auto; 19 | position: fixed; 20 | width: 100%; 21 | z-index: _misc(z-index-base) + 1; 22 | 23 | &.active { 24 | @include vendor('transform', 'translateY(1px)'); 25 | } 26 | 27 | > .inner { 28 | margin: 0 auto; 29 | max-width: 100%; 30 | width: 75em; 31 | 32 | &.split { 33 | @include vendor('display', 'flex'); 34 | 35 | > div { 36 | margin-left: 4em; 37 | width: 50%; 38 | } 39 | 40 | > :first-child { 41 | margin-left: 0; 42 | } 43 | } 44 | } 45 | 46 | > .closer { 47 | @include vendor('transition', 'opacity #{_duration(transition)} ease-in-out'); 48 | background-image: url('images/close.svg'); 49 | background-position: center; 50 | background-repeat: no-repeat; 51 | background-size: 3em; 52 | cursor: pointer; 53 | height: 5em; 54 | opacity: 0.25; 55 | position: absolute; 56 | right: 0; 57 | top: 0; 58 | width: 5em; 59 | z-index: 2; 60 | 61 | &:hover { 62 | opacity: 1.0; 63 | } 64 | } 65 | 66 | @include breakpoint('<=large') { 67 | @include padding(3em, 3em); 68 | 69 | > .inner { 70 | &.split { 71 | > div { 72 | margin-left: 3em; 73 | } 74 | } 75 | } 76 | 77 | > .closer { 78 | background-size: 2.5em; 79 | background-position: 75% 25%; 80 | } 81 | } 82 | 83 | @include breakpoint('<=medium') { 84 | > .inner { 85 | &.split { 86 | @include vendor('flex-direction', 'column'); 87 | 88 | > div { 89 | margin-left: 0; 90 | width: 100%; 91 | } 92 | } 93 | } 94 | } 95 | 96 | @include breakpoint('<=small') { 97 | @include vendor('transform', 'translateY(-100vh)'); 98 | @include padding(4em, 2em); 99 | bottom: auto; 100 | top: calc(#{_size(header)} - 1px); 101 | 102 | &.active { 103 | @include vendor('transform', 'translateY(0)'); 104 | } 105 | } 106 | } -------------------------------------------------------------------------------- /docs/assets/sass/components/_poptrox-popup.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Multiverse by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Poptrox Popup */ 8 | 9 | .poptrox-overlay { 10 | -webkit-tap-highlight-color: rgba(255,255,255,0); 11 | } 12 | 13 | .poptrox-popup { 14 | background: transparentize(_palette(bg-alt), 0.075); 15 | box-shadow: 0 1em 3em 0.5em rgba(0,0,0,0.25); 16 | cursor: default; 17 | 18 | &:before { 19 | @include vendor('transition', 'opacity #{_duration(transition)} ease-in-out'); 20 | @include vendor('background-image', ( 21 | 'linear-gradient(to left, rgba(31,34,36,0.35), rgba(31,34,36,0) 10em, rgba(31,34,36,0))', 22 | 'linear-gradient(to right, rgba(31,34,36,0.35), rgba(31,34,36,0) 10em, rgba(31,34,36,0))' 23 | )); 24 | content: ''; 25 | display: block; 26 | height: 100%; 27 | left: 0; 28 | position: absolute; 29 | top: 0; 30 | width: 100%; 31 | z-index: 1; 32 | opacity: 1; 33 | } 34 | 35 | .closer { 36 | @include vendor('transition', 'opacity #{_duration(transition)} ease-in-out'); 37 | background-image: url('images/close.svg'); 38 | background-position: center; 39 | background-repeat: no-repeat; 40 | background-size: 3em; 41 | height: 5em; 42 | opacity: 0; 43 | position: absolute; 44 | right: 0; 45 | top: 0; 46 | width: 5em; 47 | z-index: 2; 48 | } 49 | 50 | .nav-previous, 51 | .nav-next { 52 | @include vendor('transition', 'opacity #{_duration(transition)} ease-in-out'); 53 | background-image: url('images/arrow.svg'); 54 | background-position: center; 55 | background-repeat: no-repeat; 56 | background-size: 5em; 57 | cursor: pointer; 58 | height: 8em; 59 | margin-top: -4em; 60 | opacity: 0; 61 | position: absolute; 62 | top: 50%; 63 | width: 6em; 64 | z-index: 2; 65 | } 66 | 67 | .nav-previous { 68 | @include vendor('transform', 'scaleX(-1)'); 69 | left: 0; 70 | } 71 | 72 | .nav-next { 73 | right: 0; 74 | } 75 | 76 | .caption { 77 | @include padding(2em, 2em); 78 | @include vendor('background-image', 'linear-gradient(to top, rgba(16,16,16,0.45) 25%, rgba(16,16,16,0) 100%)'); 79 | bottom: 0; 80 | cursor: default; 81 | left: 0; 82 | position: absolute; 83 | text-align: left; 84 | width: 100%; 85 | z-index: 2; 86 | 87 | h2, h3, h4, h5, h6 { 88 | margin: 0 0 (_size(element-margin) * 0.25) 0; 89 | } 90 | 91 | p { 92 | color: _palette(fg-bold); 93 | } 94 | } 95 | 96 | .loader { 97 | @include vendor('animation', 'spinner 1s infinite linear !important'); 98 | background-image: url('images/spinner.svg'); 99 | background-position: center; 100 | background-repeat: no-repeat; 101 | background-size: contain; 102 | display: block; 103 | font-size: 2em; 104 | height: 2em; 105 | left: 50%; 106 | line-height: 2em; 107 | margin: -1em 0 0 -1em; 108 | opacity: 0.25; 109 | position: absolute; 110 | text-align: center; 111 | top: 50%; 112 | width: 2em; 113 | } 114 | 115 | &:hover { 116 | .closer, 117 | .nav-previous, 118 | .nav-next { 119 | opacity: 0.5; 120 | 121 | &:hover { 122 | opacity: 1.0; 123 | } 124 | } 125 | } 126 | 127 | &.loading { 128 | &:before { 129 | opacity: 0; 130 | } 131 | } 132 | 133 | body.touch & { 134 | .closer, 135 | .nav-previous, 136 | .nav-next { 137 | opacity: 1.0 !important; 138 | } 139 | } 140 | 141 | @include breakpoint('<=medium') { 142 | .closer { 143 | background-size: 3em; 144 | } 145 | 146 | .nav-previous, 147 | .nav-next { 148 | background-size: 4em; 149 | } 150 | } 151 | 152 | @include breakpoint('<=small') { 153 | &:before { 154 | display: none; 155 | } 156 | 157 | .caption { 158 | display: none !important; 159 | } 160 | 161 | .closer, 162 | .nav-previous, 163 | .nav-next { 164 | display: none !important; 165 | } 166 | } 167 | } -------------------------------------------------------------------------------- /docs/assets/sass/components/_table.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Multiverse by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Table */ 8 | 9 | .table-wrapper { 10 | -webkit-overflow-scrolling: touch; 11 | overflow-x: auto; 12 | } 13 | 14 | table { 15 | margin: 0 0 _size(element-margin) 0; 16 | width: 100%; 17 | 18 | tbody { 19 | tr { 20 | border: solid 1px _palette(border); 21 | border-left: 0; 22 | border-right: 0; 23 | 24 | &:nth-child(2n + 1) { 25 | background-color: _palette(border-bg); 26 | } 27 | } 28 | } 29 | 30 | td { 31 | padding: 0.75em 0.75em; 32 | } 33 | 34 | th { 35 | color: _palette(fg-bold); 36 | font-size: 0.9em; 37 | font-weight: _font(weight-bold); 38 | padding: 0 0.75em 0.75em 0.75em; 39 | text-align: left; 40 | } 41 | 42 | thead { 43 | border-bottom: solid 2px _palette(border); 44 | } 45 | 46 | tfoot { 47 | border-top: solid 2px _palette(border); 48 | } 49 | 50 | &.alt { 51 | border-collapse: separate; 52 | 53 | tbody { 54 | tr { 55 | td { 56 | border: solid 1px _palette(border); 57 | border-left-width: 0; 58 | border-top-width: 0; 59 | 60 | &:first-child { 61 | border-left-width: 1px; 62 | } 63 | } 64 | 65 | &:first-child { 66 | td { 67 | border-top-width: 1px; 68 | } 69 | } 70 | } 71 | } 72 | 73 | thead { 74 | border-bottom: 0; 75 | } 76 | 77 | tfoot { 78 | border-top: 0; 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /docs/assets/sass/layout/_footer.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Multiverse by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Footer */ 8 | 9 | #footer { 10 | .copyright { 11 | color: _palette(fg-light); 12 | font-size: 0.9em; 13 | 14 | a { 15 | color: inherit; 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /docs/assets/sass/layout/_header.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Multiverse by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Header */ 8 | 9 | body { 10 | padding: 0 0 _size(header) 0; 11 | } 12 | 13 | #header { 14 | @include vendor('transform', 'translateY(0)'); 15 | @include vendor('transition', 'transform #{_duration(header)} ease'); 16 | -moz-user-select: none; 17 | -ms-user-select: none; 18 | -webkit-user-select: none; 19 | background: _palette(bg-alt); 20 | bottom: -1em; 21 | height: _size(header) + 1em; 22 | left: 0; 23 | line-height: _size(header); 24 | padding: 0 1.5em; 25 | position: fixed; 26 | user-select: none; 27 | width: 100%; 28 | z-index: _misc(z-index-base) + 2; 29 | 30 | body.is-preload & { 31 | @include vendor('transform', 'translateY(#{_size(header)})'); 32 | } 33 | 34 | h1 { 35 | color: _palette(fg); 36 | display: inline-block; 37 | font-size: 1em; 38 | line-height: 1; 39 | margin: 0; 40 | vertical-align: middle; 41 | 42 | a { 43 | border: 0; 44 | color: inherit; 45 | 46 | &:hover { 47 | color: inherit !important; 48 | } 49 | } 50 | } 51 | 52 | nav { 53 | position: absolute; 54 | right: 0; 55 | top: 0; 56 | 57 | > ul { 58 | list-style: none; 59 | margin: 0; 60 | padding: 0; 61 | 62 | > li { 63 | display: inline-block; 64 | padding: 0; 65 | 66 | a { 67 | @include vendor('transition', 'background-color #{_duration(panel)} ease'); 68 | border: 0; 69 | color: _palette(fg-bold); 70 | display: inline-block; 71 | letter-spacing: _font(kerning-alt); 72 | padding: 0 1.65em; 73 | text-transform: uppercase; 74 | 75 | &.icon { 76 | &:before { 77 | color: _palette(fg-light); 78 | float: right; 79 | margin-left: 0.75em; 80 | } 81 | } 82 | 83 | &:hover { 84 | color: _palette(fg-bold) !important; 85 | } 86 | 87 | &.active { 88 | background-color: _palette(bg); 89 | } 90 | } 91 | } 92 | } 93 | } 94 | } 95 | 96 | @include breakpoint('<=small') { 97 | body { 98 | padding: _size(header) 0 0 0; 99 | } 100 | 101 | #header { 102 | @include vendor('transform', 'translateY(0)'); 103 | bottom: auto; 104 | height: _size(header); 105 | padding: 0 1em; 106 | top: 0; 107 | 108 | body.is-preload & { 109 | @include vendor('transform', 'translateY(#{_size(header) * -0.85})'); 110 | } 111 | 112 | h1 { 113 | font-size: 0.9em; 114 | } 115 | 116 | nav { 117 | > ul { 118 | > li { 119 | a { 120 | font-size: 0.9em; 121 | padding: 0 1.15em; 122 | } 123 | } 124 | } 125 | } 126 | } 127 | } -------------------------------------------------------------------------------- /docs/assets/sass/layout/_main.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Multiverse by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Main */ 8 | 9 | #main { 10 | @include vendor('transition', ( 11 | '-moz-filter #{_duration(panel)} ease', 12 | '-webkit-filter #{_duration(panel)} ease', 13 | '-ms-filter #{_duration(panel)} ease', 14 | 'filter #{_duration(panel)} ease' 15 | )); 16 | @include vendor('display', 'flex'); 17 | @include vendor('flex-wrap', 'wrap'); 18 | -webkit-tap-highlight-color: rgba(255,255,255,0); 19 | 20 | .thumb { 21 | @include vendor('transition', ( 22 | 'opacity 1.25s ease-in-out' 23 | )); 24 | @include vendor('pointer-events', 'auto'); 25 | -webkit-tap-highlight-color: rgba(255,255,255,0); 26 | opacity: 1; 27 | overflow: hidden; 28 | position: relative; 29 | 30 | &:after { 31 | @include vendor('background-image', 'linear-gradient(to top, rgba(10,17,25,0.35) 5%, rgba(10,17,25,0) 35%)'); 32 | @include vendor('pointer-events', 'none'); 33 | background-size: cover; 34 | content: ''; 35 | display: block; 36 | height: 100%; 37 | left: 0; 38 | position: absolute; 39 | top: 0; 40 | width: 100%; 41 | } 42 | 43 | > .image { 44 | -webkit-tap-highlight-color: rgba(255,255,255,0); 45 | background-position: center; 46 | background-repeat: no-repeat; 47 | background-size: cover; 48 | border: 0; 49 | height: 100%; 50 | left: 0; 51 | position: absolute; 52 | top: 0; 53 | width: 100%; 54 | } 55 | 56 | > h2 { 57 | @include vendor('pointer-events', 'none'); 58 | bottom: (1.5em / 0.8); 59 | font-size: 0.8em; 60 | left: (1.75em / 0.8); 61 | margin: 0; 62 | position: absolute; 63 | z-index: 1; 64 | } 65 | 66 | > p { 67 | display: none; 68 | } 69 | } 70 | 71 | &:after { 72 | @include vendor('pointer-events', 'none'); 73 | @include vendor('transition', ( 74 | 'opacity #{_duration(panel)} ease', 75 | 'visibility #{_duration(panel)}', 76 | )); 77 | background: _palette(bg-overlay); 78 | content: ''; 79 | display: block; 80 | height: 100%; 81 | left: 0; 82 | opacity: 0; 83 | position: absolute; 84 | top: 0; 85 | visibility: hidden; 86 | width: 100%; 87 | z-index: 1; 88 | 89 | body.ie & { 90 | background: _palette(bg-ie-overlay); 91 | } 92 | } 93 | 94 | body.content-active & { 95 | @include vendor('filter', 'blur(6px)'); 96 | 97 | &:after { 98 | @include vendor('pointer-events', 'auto'); 99 | opacity: 1; 100 | visibility: visible; 101 | } 102 | } 103 | 104 | body.is-preload & { 105 | .thumb { 106 | @include vendor('pointer-events', 'none'); 107 | opacity: 0; 108 | } 109 | } 110 | 111 | @mixin thumb($rows, $columns, $pad, $minHeight) { 112 | $baseDelay: _duration(header) - 0.5; 113 | $defaultDelay: $baseDelay + (((($rows * $columns) + 1) * 1.5) * _duration(thumb)); 114 | 115 | .thumb { 116 | @include vendor('transition-delay', '#{$defaultDelay}'); 117 | height: calc(#{100vh / ($rows + $pad)} - #{_size(header) / $rows}); 118 | min-height: $minHeight; 119 | width: (100% / $columns); 120 | 121 | @for $i from 1 through (($rows * $columns) * 1.5) { 122 | &:nth-child(#{$i}) { 123 | @include vendor('transition-delay', '#{$baseDelay + ($i * _duration(thumb))}'); 124 | } 125 | } 126 | } 127 | } 128 | 129 | // Default. 130 | @include thumb( 131 | _misc(main-layout, default, rows), 132 | _misc(main-layout, default, columns), 133 | _misc(main-layout, default, pad), 134 | _misc(main-layout, default, minHeight) 135 | ); 136 | 137 | // XLarge. 138 | @include breakpoint('<=xlarge') { 139 | @include thumb( 140 | _misc(main-layout, xlarge, rows), 141 | _misc(main-layout, xlarge, columns), 142 | _misc(main-layout, xlarge, pad), 143 | _misc(main-layout, xlarge, minHeight) 144 | ); 145 | } 146 | 147 | // Large. 148 | @include breakpoint('<=large') { 149 | @include thumb( 150 | _misc(main-layout, large, rows), 151 | _misc(main-layout, large, columns), 152 | _misc(main-layout, large, pad), 153 | _misc(main-layout, large, minHeight) 154 | ); 155 | } 156 | 157 | // Medium. 158 | @include breakpoint('<=medium') { 159 | @include thumb( 160 | _misc(main-layout, medium, rows), 161 | _misc(main-layout, medium, columns), 162 | _misc(main-layout, medium, pad), 163 | _misc(main-layout, medium, minHeight) 164 | ); 165 | } 166 | 167 | // XSmall. 168 | @include breakpoint('<=xsmall') { 169 | @include thumb( 170 | _misc(main-layout, xsmall, rows), 171 | _misc(main-layout, xsmall, columns), 172 | _misc(main-layout, xsmall, pad), 173 | _misc(main-layout, xsmall, minHeight) 174 | ); 175 | } 176 | 177 | } -------------------------------------------------------------------------------- /docs/assets/sass/layout/_wrapper.scss: -------------------------------------------------------------------------------- 1 | /// 2 | /// Multiverse by HTML5 UP 3 | /// html5up.net | @ajlkn 4 | /// Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 5 | /// 6 | 7 | /* Wrapper */ 8 | 9 | #wrapper { 10 | @include vendor('transition', ( 11 | '-moz-filter #{_duration(panel)} ease', 12 | '-webkit-filter #{_duration(panel)} ease', 13 | '-ms-filter #{_duration(panel)} ease', 14 | 'filter #{_duration(panel)} ease' 15 | )); 16 | position: relative; 17 | 18 | &:after { 19 | @include vendor('pointer-events', 'none'); 20 | @include vendor('transition', ( 21 | 'opacity #{_duration(modal)} ease', 22 | 'visibility #{_duration(modal)}', 23 | )); 24 | background: _palette(bg-overlay-alt); 25 | content: ''; 26 | display: block; 27 | height: 100%; 28 | left: 0; 29 | opacity: 0; 30 | position: absolute; 31 | top: 0; 32 | visibility: hidden; 33 | width: 100%; 34 | z-index: 1; 35 | 36 | body.ie & { 37 | background: _palette(bg-ie-overlay-alt); 38 | } 39 | } 40 | 41 | body.modal-active & { 42 | @include vendor('filter', 'blur(8px)'); 43 | 44 | &:after { 45 | @include vendor('pointer-events', 'auto'); 46 | opacity: 1; 47 | visibility: visible; 48 | z-index: _misc(z-index-base) + 3; 49 | } 50 | } 51 | 52 | &:before { 53 | @include vendor('animation', 'spinner 1s infinite linear !important'); 54 | @include vendor('pointer-events', 'none'); 55 | @include vendor('transition', ( 56 | 'top 0.75s ease-in-out', 57 | 'opacity 0.35s ease-out', 58 | 'visibility 0.35s' 59 | )); 60 | background-image: url('images/spinner.svg'); 61 | background-position: center; 62 | background-repeat: no-repeat; 63 | background-size: contain; 64 | content: ''; 65 | display: block; 66 | font-size: 2em; 67 | height: 2em; 68 | left: 50%; 69 | line-height: 2em; 70 | margin: -1em 0 0 -1em; 71 | opacity: 0; 72 | position: fixed; 73 | text-align: center; 74 | top: 75%; 75 | visibility: hidden; 76 | width: 2em; 77 | } 78 | 79 | body.is-preload & { 80 | &:before { 81 | @include vendor('transition', 'opacity 1s ease-out !important'); 82 | @include vendor('transition-delay', '0.5s !important'); 83 | opacity: 0.25; 84 | top: 50%; 85 | visibility: visible; 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /docs/assets/sass/libs/_breakpoints.scss: -------------------------------------------------------------------------------- 1 | // breakpoints.scss v1.0 | @ajlkn | MIT licensed */ 2 | 3 | // Vars. 4 | 5 | /// Breakpoints. 6 | /// @var {list} 7 | $breakpoints: () !global; 8 | 9 | // Mixins. 10 | 11 | /// Sets breakpoints. 12 | /// @param {map} $x Breakpoints. 13 | @mixin breakpoints($x: ()) { 14 | $breakpoints: $x !global; 15 | } 16 | 17 | /// Wraps @content in a @media block targeting a specific orientation. 18 | /// @param {string} $orientation Orientation. 19 | @mixin orientation($orientation) { 20 | @media screen and (orientation: #{$orientation}) { 21 | @content; 22 | } 23 | } 24 | 25 | /// Wraps @content in a @media block using a given query. 26 | /// @param {string} $query Query. 27 | @mixin breakpoint($query: null) { 28 | 29 | $breakpoint: null; 30 | $op: null; 31 | $media: null; 32 | 33 | // Determine operator, breakpoint. 34 | 35 | // Greater than or equal. 36 | @if (str-slice($query, 0, 2) == '>=') { 37 | 38 | $op: 'gte'; 39 | $breakpoint: str-slice($query, 3); 40 | 41 | } 42 | 43 | // Less than or equal. 44 | @elseif (str-slice($query, 0, 2) == '<=') { 45 | 46 | $op: 'lte'; 47 | $breakpoint: str-slice($query, 3); 48 | 49 | } 50 | 51 | // Greater than. 52 | @elseif (str-slice($query, 0, 1) == '>') { 53 | 54 | $op: 'gt'; 55 | $breakpoint: str-slice($query, 2); 56 | 57 | } 58 | 59 | // Less than. 60 | @elseif (str-slice($query, 0, 1) == '<') { 61 | 62 | $op: 'lt'; 63 | $breakpoint: str-slice($query, 2); 64 | 65 | } 66 | 67 | // Not. 68 | @elseif (str-slice($query, 0, 1) == '!') { 69 | 70 | $op: 'not'; 71 | $breakpoint: str-slice($query, 2); 72 | 73 | } 74 | 75 | // Equal. 76 | @else { 77 | 78 | $op: 'eq'; 79 | $breakpoint: $query; 80 | 81 | } 82 | 83 | // Build media. 84 | @if ($breakpoint and map-has-key($breakpoints, $breakpoint)) { 85 | 86 | $a: map-get($breakpoints, $breakpoint); 87 | 88 | // Range. 89 | @if (type-of($a) == 'list') { 90 | 91 | $x: nth($a, 1); 92 | $y: nth($a, 2); 93 | 94 | // Max only. 95 | @if ($x == null) { 96 | 97 | // Greater than or equal (>= 0 / anything) 98 | @if ($op == 'gte') { 99 | $media: 'screen'; 100 | } 101 | 102 | // Less than or equal (<= y) 103 | @elseif ($op == 'lte') { 104 | $media: 'screen and (max-width: ' + $y + ')'; 105 | } 106 | 107 | // Greater than (> y) 108 | @elseif ($op == 'gt') { 109 | $media: 'screen and (min-width: ' + ($y + 1) + ')'; 110 | } 111 | 112 | // Less than (< 0 / invalid) 113 | @elseif ($op == 'lt') { 114 | $media: 'screen and (max-width: -1px)'; 115 | } 116 | 117 | // Not (> y) 118 | @elseif ($op == 'not') { 119 | $media: 'screen and (min-width: ' + ($y + 1) + ')'; 120 | } 121 | 122 | // Equal (<= y) 123 | @else { 124 | $media: 'screen and (max-width: ' + $y + ')'; 125 | } 126 | 127 | } 128 | 129 | // Min only. 130 | @else if ($y == null) { 131 | 132 | // Greater than or equal (>= x) 133 | @if ($op == 'gte') { 134 | $media: 'screen and (min-width: ' + $x + ')'; 135 | } 136 | 137 | // Less than or equal (<= inf / anything) 138 | @elseif ($op == 'lte') { 139 | $media: 'screen'; 140 | } 141 | 142 | // Greater than (> inf / invalid) 143 | @elseif ($op == 'gt') { 144 | $media: 'screen and (max-width: -1px)'; 145 | } 146 | 147 | // Less than (< x) 148 | @elseif ($op == 'lt') { 149 | $media: 'screen and (max-width: ' + ($x - 1) + ')'; 150 | } 151 | 152 | // Not (< x) 153 | @elseif ($op == 'not') { 154 | $media: 'screen and (max-width: ' + ($x - 1) + ')'; 155 | } 156 | 157 | // Equal (>= x) 158 | @else { 159 | $media: 'screen and (min-width: ' + $x + ')'; 160 | } 161 | 162 | } 163 | 164 | // Min and max. 165 | @else { 166 | 167 | // Greater than or equal (>= x) 168 | @if ($op == 'gte') { 169 | $media: 'screen and (min-width: ' + $x + ')'; 170 | } 171 | 172 | // Less than or equal (<= y) 173 | @elseif ($op == 'lte') { 174 | $media: 'screen and (max-width: ' + $y + ')'; 175 | } 176 | 177 | // Greater than (> y) 178 | @elseif ($op == 'gt') { 179 | $media: 'screen and (min-width: ' + ($y + 1) + ')'; 180 | } 181 | 182 | // Less than (< x) 183 | @elseif ($op == 'lt') { 184 | $media: 'screen and (max-width: ' + ($x - 1) + ')'; 185 | } 186 | 187 | // Not (< x and > y) 188 | @elseif ($op == 'not') { 189 | $media: 'screen and (max-width: ' + ($x - 1) + '), screen and (min-width: ' + ($y + 1) + ')'; 190 | } 191 | 192 | // Equal (>= x and <= y) 193 | @else { 194 | $media: 'screen and (min-width: ' + $x + ') and (max-width: ' + $y + ')'; 195 | } 196 | 197 | } 198 | 199 | } 200 | 201 | // String. 202 | @else { 203 | 204 | // Missing a media type? Prefix with "screen". 205 | @if (str-slice($a, 0, 1) == '(') { 206 | $media: 'screen and ' + $a; 207 | } 208 | 209 | // Otherwise, use as-is. 210 | @else { 211 | $media: $a; 212 | } 213 | 214 | } 215 | 216 | } 217 | 218 | // Output. 219 | @media #{$media} { 220 | @content; 221 | } 222 | 223 | } -------------------------------------------------------------------------------- /docs/assets/sass/libs/_functions.scss: -------------------------------------------------------------------------------- 1 | /// Removes a specific item from a list. 2 | /// @author Hugo Giraudel 3 | /// @param {list} $list List. 4 | /// @param {integer} $index Index. 5 | /// @return {list} Updated list. 6 | @function remove-nth($list, $index) { 7 | 8 | $result: null; 9 | 10 | @if type-of($index) != number { 11 | @warn "$index: #{quote($index)} is not a number for `remove-nth`."; 12 | } 13 | @else if $index == 0 { 14 | @warn "List index 0 must be a non-zero integer for `remove-nth`."; 15 | } 16 | @else if abs($index) > length($list) { 17 | @warn "List index is #{$index} but list is only #{length($list)} item long for `remove-nth`."; 18 | } 19 | @else { 20 | 21 | $result: (); 22 | $index: if($index < 0, length($list) + $index + 1, $index); 23 | 24 | @for $i from 1 through length($list) { 25 | 26 | @if $i != $index { 27 | $result: append($result, nth($list, $i)); 28 | } 29 | 30 | } 31 | 32 | } 33 | 34 | @return $result; 35 | 36 | } 37 | 38 | /// Gets a value from a map. 39 | /// @author Hugo Giraudel 40 | /// @param {map} $map Map. 41 | /// @param {string} $keys Key(s). 42 | /// @return {string} Value. 43 | @function val($map, $keys...) { 44 | 45 | @if nth($keys, 1) == null { 46 | $keys: remove-nth($keys, 1); 47 | } 48 | 49 | @each $key in $keys { 50 | $map: map-get($map, $key); 51 | } 52 | 53 | @return $map; 54 | 55 | } 56 | 57 | /// Gets a duration value. 58 | /// @param {string} $keys Key(s). 59 | /// @return {string} Value. 60 | @function _duration($keys...) { 61 | @return val($duration, $keys...); 62 | } 63 | 64 | /// Gets a font value. 65 | /// @param {string} $keys Key(s). 66 | /// @return {string} Value. 67 | @function _font($keys...) { 68 | @return val($font, $keys...); 69 | } 70 | 71 | /// Gets a misc value. 72 | /// @param {string} $keys Key(s). 73 | /// @return {string} Value. 74 | @function _misc($keys...) { 75 | @return val($misc, $keys...); 76 | } 77 | 78 | /// Gets a palette value. 79 | /// @param {string} $keys Key(s). 80 | /// @return {string} Value. 81 | @function _palette($keys...) { 82 | @return val($palette, $keys...); 83 | } 84 | 85 | /// Gets a size value. 86 | /// @param {string} $keys Key(s). 87 | /// @return {string} Value. 88 | @function _size($keys...) { 89 | @return val($size, $keys...); 90 | } -------------------------------------------------------------------------------- /docs/assets/sass/libs/_mixins.scss: -------------------------------------------------------------------------------- 1 | /// Makes an element's :before pseudoelement a FontAwesome icon. 2 | /// @param {string} $content Optional content value to use. 3 | /// @param {string} $category Optional category to use. 4 | /// @param {string} $where Optional pseudoelement to target (before or after). 5 | @mixin icon($content: false, $category: regular, $where: before) { 6 | 7 | text-decoration: none; 8 | 9 | &:#{$where} { 10 | 11 | @if $content { 12 | content: $content; 13 | } 14 | 15 | -moz-osx-font-smoothing: grayscale; 16 | -webkit-font-smoothing: antialiased; 17 | display: inline-block; 18 | font-style: normal; 19 | font-variant: normal; 20 | text-rendering: auto; 21 | line-height: 1; 22 | text-transform: none !important; 23 | 24 | @if ($category == brands) { 25 | font-family: 'Font Awesome 5 Brands'; 26 | } 27 | @elseif ($category == solid) { 28 | font-family: 'Font Awesome 5 Free'; 29 | font-weight: 900; 30 | } 31 | @else { 32 | font-family: 'Font Awesome 5 Free'; 33 | font-weight: 400; 34 | } 35 | 36 | } 37 | 38 | } 39 | 40 | /// Applies padding to an element, taking the current element-margin value into account. 41 | /// @param {mixed} $tb Top/bottom padding. 42 | /// @param {mixed} $lr Left/right padding. 43 | /// @param {list} $pad Optional extra padding (in the following order top, right, bottom, left) 44 | /// @param {bool} $important If true, adds !important. 45 | @mixin padding($tb, $lr, $pad: (0,0,0,0), $important: null) { 46 | 47 | @if $important { 48 | $important: '!important'; 49 | } 50 | 51 | $x: 0.1em; 52 | 53 | @if unit(_size(element-margin)) == 'rem' { 54 | $x: 0.1rem; 55 | } 56 | 57 | padding: ($tb + nth($pad,1)) ($lr + nth($pad,2)) max($x, $tb - _size(element-margin) + nth($pad,3)) ($lr + nth($pad,4)) #{$important}; 58 | 59 | } 60 | 61 | /// Encodes a SVG data URL so IE doesn't choke (via codepen.io/jakob-e/pen/YXXBrp). 62 | /// @param {string} $svg SVG data URL. 63 | /// @return {string} Encoded SVG data URL. 64 | @function svg-url($svg) { 65 | 66 | $svg: str-replace($svg, '"', '\''); 67 | $svg: str-replace($svg, '%', '%25'); 68 | $svg: str-replace($svg, '<', '%3C'); 69 | $svg: str-replace($svg, '>', '%3E'); 70 | $svg: str-replace($svg, '&', '%26'); 71 | $svg: str-replace($svg, '#', '%23'); 72 | $svg: str-replace($svg, '{', '%7B'); 73 | $svg: str-replace($svg, '}', '%7D'); 74 | $svg: str-replace($svg, ';', '%3B'); 75 | 76 | @return url("data:image/svg+xml;charset=utf8,#{$svg}"); 77 | 78 | } -------------------------------------------------------------------------------- /docs/assets/sass/libs/_vars.scss: -------------------------------------------------------------------------------- 1 | // Misc. 2 | $misc: ( 3 | z-index-base: 10000, 4 | main-layout: ( 5 | default: ( 6 | rows: 2, 7 | columns: 4, 8 | pad: 0.5, 9 | minHeight: 20em 10 | ), 11 | xlarge: ( 12 | rows: 2, 13 | columns: 3, 14 | pad: 0.5, 15 | minHeight: 20em 16 | ), 17 | large: ( 18 | rows: 2, 19 | columns: 2, 20 | pad: 0.5, 21 | minHeight: 20em 22 | ), 23 | medium: ( 24 | rows: 3, 25 | columns: 2, 26 | pad: 0.5, 27 | minHeight: 18em 28 | ), 29 | xsmall: ( 30 | rows: 2, 31 | columns: 1, 32 | pad: 0.5, 33 | minHeight: 18em 34 | ) 35 | ) 36 | ); 37 | 38 | // Duration. 39 | $duration: ( 40 | transition: 0.2s, 41 | header: 1s, 42 | panel: 0.5s, 43 | modal: 0.5s, 44 | thumb: 0.15s 45 | ); 46 | 47 | // Size. 48 | $size: ( 49 | element-height: 2.75em, 50 | element-margin: 2em, 51 | header: 4em 52 | ); 53 | 54 | // Font. 55 | $font: ( 56 | family: ('Source Sans Pro', Helvetica, sans-serif), 57 | family-fixed: ('Courier New', monospace), 58 | weight: 300, 59 | weight-bold: 300, 60 | weight-extrabold: 400, 61 | kerning: 0.025em, 62 | kerning-alt: 0.1em 63 | ); 64 | 65 | // Palette. 66 | $palette: ( 67 | bg: #242629, 68 | bg-alt: #1f2224, 69 | bg-overlay: transparentize(#242629, 0.75), 70 | bg-overlay-alt: transparentize(#242629, 0.5), 71 | bg-ie-overlay: transparentize(#242629, 0.45), 72 | bg-ie-overlay-alt: transparentize(#242629, 0.2), 73 | fg: #a0a0a1, 74 | fg-bold: #ffffff, 75 | fg-medium: #707071, 76 | fg-light: #505051, 77 | border: #36383c, 78 | border-bg: #34363b, 79 | border-bg-alt: #44464b, 80 | accent1: #34a58e 81 | ); -------------------------------------------------------------------------------- /docs/assets/sass/main.scss: -------------------------------------------------------------------------------- 1 | @import 'libs/vars'; 2 | @import 'libs/functions'; 3 | @import 'libs/mixins'; 4 | @import 'libs/vendor'; 5 | @import 'libs/breakpoints'; 6 | @import 'fontawesome-all.min.css'; 7 | @import url('https://fonts.googleapis.com/css?family=Source+Sans+Pro:300,300italic,400,400italic'); 8 | 9 | /* 10 | Multiverse by HTML5 UP 11 | html5up.net | @ajlkn 12 | Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 13 | */ 14 | 15 | // Breakpoints. 16 | 17 | @include breakpoints(( 18 | xlarge: ( 1281px, 1680px ), 19 | large: ( 981px, 1280px ), 20 | medium: ( 737px, 980px ), 21 | small: ( 481px, 736px ), 22 | xsmall: ( null, 480px ) 23 | )); 24 | 25 | // Spinner. 26 | 27 | @include keyframes(spinner) { 28 | 0% { 29 | @include vendor('transform', 'rotate(0deg)'); 30 | } 31 | 32 | 100% { 33 | @include vendor('transform', 'rotate(359deg)'); 34 | } 35 | } 36 | 37 | // Base. 38 | 39 | @import 'base/reset'; 40 | @import 'base/page'; 41 | @import 'base/typography'; 42 | 43 | // Component. 44 | 45 | @import 'components/button'; 46 | @import 'components/form'; 47 | @import 'components/icon'; 48 | @import 'components/list'; 49 | @import 'components/actions'; 50 | @import 'components/icons'; 51 | @import 'components/table'; 52 | @import 'components/panel'; 53 | @import 'components/poptrox-popup'; 54 | 55 | // Layout. 56 | 57 | @import 'layout/wrapper'; 58 | @import 'layout/header'; 59 | @import 'layout/main'; 60 | @import 'layout/footer'; -------------------------------------------------------------------------------- /docs/assets/sass/noscript.scss: -------------------------------------------------------------------------------- 1 | @import 'libs/vars'; 2 | @import 'libs/functions'; 3 | @import 'libs/mixins'; 4 | @import 'libs/vendor'; 5 | @import 'libs/breakpoints'; 6 | 7 | /* 8 | Multiverse by HTML5 UP 9 | html5up.net | @ajlkn 10 | Free for personal and commercial use under the CCA 3.0 license (html5up.net/license) 11 | */ 12 | 13 | /* Wrapper */ 14 | 15 | #wrapper { 16 | body.is-preload & { 17 | &:before { 18 | display: none; 19 | } 20 | } 21 | } 22 | 23 | /* Main */ 24 | 25 | #main { 26 | body.is-preload & { 27 | .thumb { 28 | @include vendor('pointer-events', 'auto'); 29 | opacity: 1; 30 | } 31 | } 32 | } 33 | 34 | /* Header */ 35 | 36 | #header { 37 | body.is-preload & { 38 | @include vendor('transform', 'none'); 39 | } 40 | } -------------------------------------------------------------------------------- /docs/assets/webfonts/fa-brands-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thelumiereguy/CreativeCodingExamples/94372d747bcaae6a710753832e8d7fddc3d0ccc4/docs/assets/webfonts/fa-brands-400.eot -------------------------------------------------------------------------------- /docs/assets/webfonts/fa-brands-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thelumiereguy/CreativeCodingExamples/94372d747bcaae6a710753832e8d7fddc3d0ccc4/docs/assets/webfonts/fa-brands-400.ttf -------------------------------------------------------------------------------- /docs/assets/webfonts/fa-brands-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thelumiereguy/CreativeCodingExamples/94372d747bcaae6a710753832e8d7fddc3d0ccc4/docs/assets/webfonts/fa-brands-400.woff -------------------------------------------------------------------------------- /docs/assets/webfonts/fa-brands-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thelumiereguy/CreativeCodingExamples/94372d747bcaae6a710753832e8d7fddc3d0ccc4/docs/assets/webfonts/fa-brands-400.woff2 -------------------------------------------------------------------------------- /docs/assets/webfonts/fa-regular-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thelumiereguy/CreativeCodingExamples/94372d747bcaae6a710753832e8d7fddc3d0ccc4/docs/assets/webfonts/fa-regular-400.eot -------------------------------------------------------------------------------- /docs/assets/webfonts/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thelumiereguy/CreativeCodingExamples/94372d747bcaae6a710753832e8d7fddc3d0ccc4/docs/assets/webfonts/fa-regular-400.ttf -------------------------------------------------------------------------------- /docs/assets/webfonts/fa-regular-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thelumiereguy/CreativeCodingExamples/94372d747bcaae6a710753832e8d7fddc3d0ccc4/docs/assets/webfonts/fa-regular-400.woff -------------------------------------------------------------------------------- /docs/assets/webfonts/fa-regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thelumiereguy/CreativeCodingExamples/94372d747bcaae6a710753832e8d7fddc3d0ccc4/docs/assets/webfonts/fa-regular-400.woff2 -------------------------------------------------------------------------------- /docs/assets/webfonts/fa-solid-900.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thelumiereguy/CreativeCodingExamples/94372d747bcaae6a710753832e8d7fddc3d0ccc4/docs/assets/webfonts/fa-solid-900.eot -------------------------------------------------------------------------------- /docs/assets/webfonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thelumiereguy/CreativeCodingExamples/94372d747bcaae6a710753832e8d7fddc3d0ccc4/docs/assets/webfonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /docs/assets/webfonts/fa-solid-900.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thelumiereguy/CreativeCodingExamples/94372d747bcaae6a710753832e8d7fddc3d0ccc4/docs/assets/webfonts/fa-solid-900.woff -------------------------------------------------------------------------------- /docs/assets/webfonts/fa-solid-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thelumiereguy/CreativeCodingExamples/94372d747bcaae6a710753832e8d7fddc3d0ccc4/docs/assets/webfonts/fa-solid-900.woff2 -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: creativecanvas 3 | --- 4 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | #Gradle 2 | org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8 -XX:+UseParallelGC -Dkotlin.daemon.jvm.options\="-Xmx2048M" 3 | #Kotlin 4 | kotlin.code.style=official 5 | #Android 6 | android.useAndroidX=true 7 | android.nonTransitiveRClass=true 8 | #MPP 9 | kotlin.mpp.enableCInteropCommonization=true 10 | 11 | kotlin.version=1.7.10 12 | agp.version=7.3.1 13 | compose.version=1.3.0-alpha01-dev831 14 | 15 | kotlin.native.cacheKind=none 16 | kotlin.native.useEmbeddableCompilerJar=true 17 | kotlin.mpp.enableGranularSourceSetsMetadata=true 18 | # Enable kotlin/native experimental memory model 19 | kotlin.native.binary.memoryModel=experimental 20 | compose.desktop.verbose=true 21 | org.jetbrains.compose.experimental.jscanvas.enabled=true 22 | org.jetbrains.compose.experimental.macos.enabled=true 23 | org.jetbrains.compose.experimental.uikit.enabled=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thelumiereguy/CreativeCodingExamples/94372d747bcaae6a710753832e8d7fddc3d0ccc4/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Oct 16 21:57:01 IST 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /k5-compose/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /k5-compose/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | kotlin("jvm") 3 | id("org.jetbrains.compose") 4 | } 5 | 6 | group = "com.github.CreativeCodingExamples" 7 | version = "1.0.0" 8 | 9 | repositories { 10 | maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") 11 | google() 12 | mavenCentral() 13 | } 14 | 15 | dependencies { 16 | implementation(compose.desktop.macos_arm64) 17 | implementation(kotlin("stdlib")) 18 | implementation("me.nikhilchaudhari:k5-compose:1.0.1") 19 | implementation("dev.romainguy:kotlin-math:1.4.0") 20 | } 21 | 22 | tasks.withType() { 23 | kotlinOptions { 24 | jvmTarget = "11" 25 | } 26 | } 27 | 28 | compose.desktop { 29 | application { 30 | mainClass = "K5ComposeMainKt" 31 | nativeDistributions { 32 | packageName = "k5-compose" 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /k5-compose/src/main/kotlin/K5ComposeMain.kt: -------------------------------------------------------------------------------- 1 | import examples.testK5 2 | 3 | 4 | 5 | fun main() { 6 | testK5() 7 | } -------------------------------------------------------------------------------- /k5-compose/src/main/kotlin/examples/test_k5.kt: -------------------------------------------------------------------------------- 1 | package examples 2 | 3 | import androidx.compose.ui.geometry.Offset 4 | import androidx.compose.ui.graphics.Color 5 | import k5 6 | 7 | fun testK5() = k5 { 8 | 9 | show() { 10 | it.drawCircle( 11 | Color.Yellow, radius = dimensFloat.width / 2, center = Offset(dimensFloat.width / 2, dimensFloat.height / 2) 12 | ) 13 | } 14 | } -------------------------------------------------------------------------------- /processing/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /processing/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | kotlin("jvm") 3 | } 4 | 5 | group = "dev.thelumiereguy" 6 | version = "1.0.0" 7 | 8 | repositories { 9 | google() 10 | mavenCentral() 11 | } 12 | 13 | dependencies { 14 | implementation(kotlin("stdlib")) 15 | implementation(fileTree("src/main/libs") { include("*.jar") }) 16 | } -------------------------------------------------------------------------------- /processing/src/main/kotlin/dev/thelumiereguy/examples/arc_waves/ArcWaves.kt: -------------------------------------------------------------------------------- 1 | package dev.thelumiereguy.examples.arc_waves 2 | 3 | import processing.core.PApplet 4 | 5 | class ArcWaves : PApplet() { 6 | 7 | private var perlinOffset = 0f 8 | 9 | init { 10 | setSize(852, 852) 11 | runSketch() 12 | } 13 | 14 | 15 | override fun setup() { 16 | 17 | } 18 | 19 | 20 | override fun draw() { 21 | 22 | translate(width / 2f, height / 2f) 23 | 24 | background(37f, 74f, 108f) 25 | 26 | noFill() 27 | 28 | rotate(perlinOffset * 0.1f) 29 | 30 | perlinOffset += 0.05f 31 | 32 | val ringGapWidth = 35 33 | 34 | (0..width / 2 step ringGapWidth).mapIndexed { index, horizontalPixel -> 35 | 36 | stroke(135f, 164f, 196f, 255f) 37 | 38 | 39 | val gapsCount = fibonacci(index + 5).first() 40 | 41 | val angleGap = radians((360f / gapsCount)) 42 | 43 | strokeWeight(perlinOffset / gapsCount) 44 | 45 | repeat(gapsCount.toInt()) { 46 | 47 | rotate(angleGap) 48 | 49 | val angle = atan2( 50 | 0f, 51 | horizontalPixel.toFloat() 52 | ) 53 | 54 | arc( 55 | horizontalPixel.toFloat(), 56 | 0f, 57 | 25f, 58 | 25f, 59 | sin(horizontalPixel.toFloat()) * angle, 60 | angle + PI 61 | ) 62 | } 63 | } 64 | } 65 | 66 | private fun fibonacci(n: Int) = sequence { 67 | var previousResult: Long = 0 68 | var result: Long = 1 69 | 70 | repeat(n) { 71 | previousResult = result.also { 72 | result += previousResult 73 | } 74 | } 75 | 76 | yield(previousResult) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /processing/src/main/kotlin/dev/thelumiereguy/examples/circle_pattern/CirclePattern.kt: -------------------------------------------------------------------------------- 1 | package dev.thelumiereguy.examples.circle_pattern 2 | 3 | import processing.core.PApplet 4 | 5 | class CirclePattern : PApplet() { 6 | 7 | init { 8 | setSize(1024, 1024) 9 | runSketch() 10 | } 11 | 12 | 13 | override fun setup() { 14 | stroke(0xfff) 15 | colorMode(HSB, 360f, 100f, 100f) 16 | } 17 | 18 | 19 | override fun draw() { 20 | background(20f) 21 | 22 | translate(width / 2f, height / 2f) 23 | 24 | ((1..400) step 30).forEach { radius -> 25 | val circumference = (2 * PI * radius) 26 | val circles = floor(circumference / 30) 27 | val steps = 360 / max(circles.toFloat(), 0.1f) 28 | 29 | var angle = 0f 30 | 31 | while (angle <= 360) { 32 | pushMatrix() 33 | rotate(radians(angle)) 34 | 35 | val extent = (sin(radius + frameCount * 0.05f) + 1) * max(radius, 200) * 0.03f 36 | 37 | fill( 38 | angle % 40, 39 | (noise(angle + frameCount * 0.01f)) * 150f, 40 | 255f 41 | ) 42 | circle(radius.toFloat(), 0f, extent) 43 | popMatrix() 44 | 45 | angle += steps 46 | } 47 | } 48 | 49 | // saveFrame("/${this.javaClass.name}/#####.png") 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /processing/src/main/kotlin/dev/thelumiereguy/examples/circleception/Circleception.kt: -------------------------------------------------------------------------------- 1 | package dev.thelumiereguy.examples.circleception 2 | 3 | import dev.thelumiereguy.util.plotShape 4 | import processing.core.PApplet 5 | import processing.core.PVector 6 | 7 | class Circleception : PApplet() { 8 | 9 | init { 10 | setSize(800, 800) 11 | runSketch() 12 | } 13 | 14 | override fun setup() = Unit 15 | 16 | private var upTime = 0 17 | 18 | 19 | override fun draw() { 20 | 21 | colorMode(HSB, 360f, 1f, 1f) 22 | 23 | background(0) 24 | 25 | translate( 26 | width / 2f, 27 | height / 2f 28 | ) 29 | 30 | val animatedAngle = sin(upTime * 0.001f) * 360f 31 | val animatedAngleInnerShape = cos(upTime * 0.005f) * 360f 32 | val animatedAngleSecondInnerShape = cos(upTime * 0.0001f) * 360f 33 | 34 | noFill() 35 | 36 | blendMode(ADD) 37 | 38 | plotShape( 39 | PVector(0f, 0f), 40 | 6, 41 | 150f, 42 | animatedAngle 43 | ) { shapeVertex -> 44 | 45 | stroke(0f, 0.7f, 0.5f) 46 | strokeWeight(5f) 47 | 48 | circle( 49 | shapeVertex.startPair.first, 50 | shapeVertex.startPair.second, 51 | 300f 52 | ) 53 | 54 | noStroke() 55 | 56 | plotShape( 57 | PVector( 58 | shapeVertex.startPair.first, 59 | shapeVertex.startPair.second 60 | ), 61 | 3, 62 | 75f, 63 | (if (shapeVertex.sideIndex % 2 == 0) 0f else 180f) + animatedAngleInnerShape 64 | ) { innerShapeVertex -> 65 | 66 | stroke(20f, 0.5f, 0.5f) 67 | 68 | strokeWeight(3f) 69 | 70 | circle( 71 | innerShapeVertex.startPair.first, 72 | innerShapeVertex.startPair.second, 73 | 150f 74 | ) 75 | 76 | noStroke() 77 | 78 | plotShape( 79 | PVector( 80 | innerShapeVertex.startPair.first, 81 | innerShapeVertex.startPair.second, 82 | ), 83 | 3, 84 | 37f, 85 | (if (innerShapeVertex.sideIndex % 2 == 0) 00f else 90f) + animatedAngleSecondInnerShape 86 | ) { secondInnerShapeVertex -> 87 | 88 | stroke(20f, 0.3f, 0.3f) 89 | 90 | strokeWeight(3f) 91 | 92 | line( 93 | secondInnerShapeVertex.startPair.first, 94 | secondInnerShapeVertex.startPair.second, 95 | -secondInnerShapeVertex.startPair.first, 96 | -secondInnerShapeVertex.startPair.second, 97 | ) 98 | 99 | circle( 100 | secondInnerShapeVertex.startPair.first, 101 | secondInnerShapeVertex.startPair.second, 102 | 75f 103 | ) 104 | 105 | noStroke() 106 | 107 | } 108 | } 109 | } 110 | 111 | // saveFrame("../art/${this::class.simpleName}/#####.png") 112 | 113 | upTime++ 114 | } 115 | } -------------------------------------------------------------------------------- /processing/src/main/kotlin/dev/thelumiereguy/examples/color_pinwheel/ColorPinWheel.kt: -------------------------------------------------------------------------------- 1 | package dev.thelumiereguy.examples.color_pinwheel 2 | 3 | import processing.core.PApplet 4 | 5 | class ColorPinWheel : PApplet() { 6 | 7 | init { 8 | setSize(800, 800) 9 | runSketch() 10 | } 11 | 12 | 13 | override fun setup() { 14 | 15 | colorMode(HSB, 360f, 100f, 100f, 100f) 16 | 17 | 18 | 19 | background(0) 20 | 21 | val halfWidth = width / 2f 22 | val halfHeight = height / 2f 23 | 24 | noFill() 25 | 26 | translate(halfWidth, halfHeight) 27 | 28 | repeat(200) { index -> 29 | 30 | blendMode(ADD) 31 | 32 | strokeWeight(5f) 33 | 34 | stroke(2.2f * index, 50f, 50f) 35 | 36 | val angle = radians((360f / 200) * index) 37 | 38 | val circumX = cos(angle) * 256f 39 | val circumY = sin(angle) * 256f 40 | 41 | pushMatrix() 42 | 43 | line(0f, 0f, circumX, circumY) 44 | 45 | strokeWeight(0.5f) 46 | 47 | val endPoint = getMinimum(circumX, circumY, halfWidth, halfHeight) 48 | 49 | line(circumX, circumY, endPoint.first, endPoint.second) 50 | 51 | popMatrix() 52 | 53 | } 54 | 55 | // save("/snaps/magnitudeBasedCircle.png") 56 | } 57 | 58 | override fun draw() { 59 | 60 | } 61 | 62 | private fun getMinimum( 63 | valueX: Float, 64 | valueY: Float, 65 | boundX: Float, 66 | boundY: Float 67 | ): Pair { 68 | return when { 69 | valueX > 0 && valueY > 0 -> boundX to valueY 70 | valueX > 0 && valueY < 0 -> valueX to -boundY 71 | valueX < 0 && valueY < 0 -> -boundX to valueY 72 | valueX < 0 && valueY > 0 -> valueX to boundY 73 | else -> valueX to valueY 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /processing/src/main/kotlin/dev/thelumiereguy/examples/divine_intervention/DivineIntervention.kt: -------------------------------------------------------------------------------- 1 | package dev.thelumiereguy.examples.divine_intervention 2 | 3 | import processing.core.PApplet 4 | 5 | class DivineIntervention : PApplet() { 6 | 7 | init { 8 | setSize(800, 800) 9 | runSketch() 10 | } 11 | 12 | override fun setup() { 13 | colorMode(HSB, 360f, 100f, 100f) 14 | background(0) 15 | strokeCap(ROUND) 16 | repeat(200) { 17 | stroke(255) 18 | line(width / 2f, -100f, random(width.toFloat()), random(height.toFloat())) 19 | } 20 | } 21 | 22 | 23 | private var scale = 1f 24 | private var rotation = 1f 25 | 26 | override fun draw() { 27 | 28 | 29 | translate(width / 2f, height / 2f) 30 | scale(scale) 31 | stroke(0) 32 | strokeWeight(1.5f) 33 | (1..30).forEach { rings -> 34 | rotation += 0.000005f 35 | fill(5f, rings * 2F, 90f) 36 | (1..rings).forEach { 37 | val radius = 8f * rings 38 | val angle = radians(((360f / rings) * it)) + (rotation + (500 / rings) * scale) 39 | val x = radius * cos(angle) 40 | val y = radius * sin(angle) 41 | circle(x, y, max(rings * 0.3f, 2f)) 42 | } 43 | } 44 | 45 | scale += 0.0005f 46 | 47 | // saveFrame("/${this.javaClass.name}/#####.png") 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /processing/src/main/kotlin/dev/thelumiereguy/examples/fractal_spiralograph/FractalSpirograph.kt: -------------------------------------------------------------------------------- 1 | package dev.thelumiereguy.examples.fractal_spiralograph 2 | 3 | import processing.core.PApplet 4 | import processing.core.PVector 5 | 6 | class FractalSpirograph : PApplet() { 7 | 8 | init { 9 | setSize(800, 800) 10 | runSketch() 11 | } 12 | 13 | 14 | override fun setup() { 15 | 16 | } 17 | 18 | private val list = mutableListOf() 19 | 20 | 21 | private val orbit by lazy { Orbit(0f, 0f, 150f) } 22 | 23 | override fun draw() { 24 | background(0) 25 | stroke(255f) 26 | noFill() 27 | strokeWeight(2f) 28 | 29 | val x = width / 2f 30 | val y = height / 2f 31 | 32 | translate(x, y) 33 | 34 | orbit.run { 35 | update() 36 | drawCircle(this@FractalSpirograph) 37 | val newCoords = getCoords() 38 | list.add(PVector(newCoords.first, newCoords.second)) 39 | } 40 | 41 | beginShape() 42 | list.forEachIndexed { index, pVector -> 43 | stroke(max(index / 3, 150)) 44 | vertex(pVector.x, pVector.y) 45 | } 46 | endShape() 47 | 48 | if (list.size > 700) { 49 | list.removeFirstOrNull() 50 | } 51 | 52 | // noLoop() 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /processing/src/main/kotlin/dev/thelumiereguy/examples/fractal_spiralograph/Orbit.kt: -------------------------------------------------------------------------------- 1 | package dev.thelumiereguy.examples.fractal_spiralograph 2 | 3 | import processing.core.PApplet 4 | import processing.core.PApplet.* 5 | 6 | class Orbit( 7 | private var xCord: Float, 8 | private var yCord: Float, 9 | private val radius: Float, 10 | private val iteration: Int = 0, 11 | private val parent: Orbit? = null, 12 | private val speed: Float = radians(pow(-9f, iteration - 1f)), 13 | private var angle: Float = 0f, 14 | ) { 15 | 16 | private var child: Orbit? = null 17 | 18 | init { 19 | if (iteration < 10) { 20 | val newRadius = radius / 3f 21 | val newX = xCord + radius + newRadius 22 | val newY = yCord 23 | child = Orbit(newX, newY, newRadius, iteration + 1, this) 24 | } 25 | } 26 | 27 | fun getCoords(): Pair { 28 | return when { 29 | child != null -> child!!.getCoords() 30 | else -> xCord to yCord 31 | } 32 | } 33 | 34 | fun drawCircle(applet: PApplet) { 35 | if (parent != null) 36 | applet.circle(xCord, yCord, radius * 2f) 37 | child?.drawCircle(applet) 38 | } 39 | 40 | fun update() { 41 | if (parent != null) { 42 | angle += (speed) * 0.5f 43 | val radiusSum = radius + parent.radius 44 | xCord = parent.xCord + radiusSum * cos(angle) 45 | yCord = parent.yCord + radiusSum * sin(angle) 46 | } 47 | child?.update() 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /processing/src/main/kotlin/dev/thelumiereguy/examples/iris/Iris.kt: -------------------------------------------------------------------------------- 1 | package dev.thelumiereguy.examples.iris 2 | 3 | import processing.core.PApplet 4 | import kotlin.math.roundToInt 5 | 6 | class Iris : PApplet() { 7 | 8 | init { 9 | setSize(1024, 1024) 10 | runSketch() 11 | } 12 | 13 | override fun setup() { 14 | smooth() 15 | background(30f) 16 | colorMode(HSB, 360f, 100f, 100f, 100f) 17 | } 18 | 19 | override fun draw() { 20 | val frameCount = frameCount.toFloat() 21 | background(0) 22 | translate(width / 2f, height / 2f) 23 | scale(noise(frameCount * 0.0009f) + 0.6f) 24 | rotate(-frameCount * 0.006f) 25 | 26 | (1..5).forEach { 27 | fill(0f, 0f, 100f, 100f / it) 28 | circle(0f, 0f, sin(frameCount * 0.01f) + (2f * it)) 29 | } 30 | 31 | stroke(255) 32 | noFill() 33 | 34 | (1..(12)).forEach { ringIndex -> 35 | drawCircle(ringIndex) 36 | } 37 | 38 | // saveFrame("/curvedRings/#####.png") 39 | } 40 | 41 | private fun drawCircle(ringIndex: Int) { 42 | stroke(30f, min(frameCount * 0.5f, 60f), (10f / ringIndex) * 20) 43 | val maxSegments = min(frameCount * 0.6f, 150f).roundToInt() 44 | (0..maxSegments).forEach { 45 | if (it % 3 == 0) { 46 | val point = getPointCoords(maxSegments, it, ringIndex) 47 | beginShape() 48 | vertex(point.first, point.second) 49 | val nextPoint = getPointCoords(maxSegments, it + 1, ringIndex) 50 | val nextPoint2 = getPointCoords(maxSegments, it + 2, ringIndex) 51 | val nextPoint3 = getPointCoords(maxSegments, it + 3, ringIndex) 52 | bezierVertex( 53 | nextPoint.first, nextPoint.second, 54 | nextPoint2.first, nextPoint2.second, 55 | nextPoint3.first, nextPoint3.second, 56 | ) 57 | endShape() 58 | } 59 | } 60 | } 61 | 62 | private fun getPointCoords(maxSegments: Int, currentIndex: Int, ringIndex: Int): Pair { 63 | val angle = radians((360f / maxSegments) * currentIndex) 64 | val x = 65 | (cos(angle) * ((ringIndex * 50) + (cos(currentIndex + angle + frameCount * 0.02f) * 10))) 66 | val y = 67 | (sin(angle) * ((ringIndex * 50) + (sin(currentIndex + angle - frameCount * 0.02f) * 10))) 68 | 69 | return x to y 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /processing/src/main/kotlin/dev/thelumiereguy/examples/phyllotaxis/Phyllotaxis.kt: -------------------------------------------------------------------------------- 1 | package dev.thelumiereguy.examples.phyllotaxis 2 | 3 | import processing.core.PApplet 4 | import processing.event.KeyEvent 5 | 6 | class Phyllotaxis : PApplet() { 7 | 8 | init { 9 | setSize(800, 800) 10 | runSketch() 11 | } 12 | 13 | override fun setup() { 14 | colorMode(HSB, 360f, 100f, 100f, 100f) 15 | frameRate(120f) 16 | noStroke() 17 | background(0) 18 | 19 | } 20 | 21 | override fun keyPressed(event: KeyEvent?) { 22 | super.keyPressed(event) 23 | if (event?.key == 's') { 24 | save("/birdsEye/BirdsEye.png") 25 | } 26 | } 27 | 28 | private val constant = 4 29 | 30 | override fun draw() { 31 | 32 | translate(width / 2f, height / 2f) 33 | 34 | val current = frameCount 35 | 36 | val angle = current * 137.5f 37 | val radius = constant * sqrt(current.toFloat()) 38 | 39 | val x = radius * cos(angle) 40 | val y = radius * sin(angle) 41 | 42 | fill( 43 | map(radius % 100, 0f, 100f, 255f, 0f), 44 | radius % 75, 45 | radius % 100 46 | ) 47 | 48 | circle(x, y, radius % 5) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /processing/src/main/kotlin/dev/thelumiereguy/examples/squiggly_wiggly/SquigglyWiggly.kt: -------------------------------------------------------------------------------- 1 | package dev.thelumiereguy.examples.squiggly_wiggly 2 | 3 | import processing.core.PApplet 4 | 5 | class SquigglyWiggly : PApplet() { 6 | 7 | init { 8 | setSize(800, 600) 9 | runSketch() 10 | } 11 | 12 | 13 | override fun setup() { 14 | background(0) 15 | } 16 | 17 | private val maxTrailsSize = 300 18 | 19 | 20 | private val circleList = mutableListOf<() -> Unit>() 21 | 22 | private var noiseOffset = 0f 23 | 24 | private var radius = 0f 25 | 26 | override fun draw() { 27 | 28 | circleList.forEachIndexed { index, function -> 29 | if (index < maxTrailsSize) { 30 | fill(255f, minOf(index * 3f, 255f)) 31 | } 32 | 33 | radius = (index.toFloat() % 30) + 1 34 | 35 | function.invoke() 36 | 37 | noiseOffset += 0.0001f 38 | } 39 | 40 | val offset = noiseOffset 41 | 42 | circleList.add { 43 | circle( 44 | noise(offset) * width, 45 | noise(offset, -offset) * height, 46 | radius 47 | ) 48 | } 49 | 50 | if (circleList.size > maxTrailsSize) { 51 | circleList.removeFirstOrNull() 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /processing/src/main/kotlin/dev/thelumiereguy/examples/whirlpool/Whirlpool.kt: -------------------------------------------------------------------------------- 1 | package dev.thelumiereguy.examples.whirlpool 2 | 3 | import processing.core.PApplet 4 | 5 | class Whirlpool : PApplet() { 6 | 7 | init { 8 | setSize(800, 800) 9 | runSketch() 10 | } 11 | 12 | override fun setup() { 13 | colorMode(HSB, 360f, 100f, 100f) 14 | background(0) 15 | } 16 | 17 | 18 | private var scale = 1f 19 | private var rotation = 1f 20 | 21 | override fun draw() { 22 | 23 | translate(width / 2f, height / 2f) 24 | 25 | stroke(0) 26 | strokeWeight(1f) 27 | (1..30).forEach { rings -> 28 | rotation += 0.00005f 29 | fill(52f, rings * 2.5F, 100f) 30 | (1..rings).forEach { 31 | val radius = 10f * rings 32 | val angle = radians(((360f / rings) * it)) + (QUARTER_PI * (it * sin(rotation))) 33 | val x = radius * cos(angle) 34 | val y = radius * sin(angle) 35 | circle(x, y, max(rings * 0.3f, 2f)) 36 | } 37 | } 38 | 39 | scale += 0.01f 40 | 41 | // saveFrame("/circleArray/#####.png") 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /processing/src/main/kotlin/dev/thelumiereguy/main.kt: -------------------------------------------------------------------------------- 1 | package dev.thelumiereguy 2 | 3 | import dev.thelumiereguy.examples.divine_intervention.DivineIntervention 4 | 5 | fun main() { 6 | DivineIntervention() 7 | } -------------------------------------------------------------------------------- /processing/src/main/kotlin/dev/thelumiereguy/util/plotShape.kt: -------------------------------------------------------------------------------- 1 | package dev.thelumiereguy.util 2 | 3 | import processing.core.PApplet 4 | import processing.core.PVector 5 | 6 | /** 7 | * A makeshift algorithm to draw any shape with n [sides] and [radius] 8 | * You can rotate the shape by specifying the [rotationPhase] param 9 | * 10 | * [drawAtVertexBlock] block can be used to draw something at each vertex of the original shape 11 | */ 12 | internal fun PApplet.plotShape( 13 | center: PVector, 14 | sides: Int, 15 | radius: Float, 16 | rotationPhase: Float, 17 | drawAtVertexBlock: (VertexData) -> Unit = { _ -> } 18 | ) { 19 | 20 | pushMatrix() 21 | 22 | val rotationAngle = 360f / sides 23 | 24 | translate( 25 | center.x, 26 | center.y 27 | ) 28 | 29 | rotate(PApplet.radians(rotationPhase)) 30 | 31 | repeat( 32 | sides 33 | ) { side -> 34 | 35 | val startAngle = PApplet.radians(rotationAngle * side) 36 | val startX = PApplet.cos(startAngle) * radius 37 | val startY = PApplet.sin(startAngle) * radius 38 | 39 | val endAngle = PApplet.radians(rotationAngle * (side + 1)) 40 | 41 | val endX = PApplet.cos(endAngle) * radius 42 | val endY = PApplet.sin(endAngle) * radius 43 | 44 | drawAtVertexBlock( 45 | VertexData( 46 | startX to startY, 47 | endX to endY, 48 | side 49 | ) 50 | ) 51 | 52 | line( 53 | startX, 54 | startY, 55 | endX, 56 | endY 57 | ) 58 | } 59 | 60 | popMatrix() 61 | } 62 | 63 | 64 | data class VertexData( 65 | val startPair: Pair, 66 | val endPair: Pair, 67 | val sideIndex: Int 68 | ) 69 | -------------------------------------------------------------------------------- /processing/src/main/kotlin/dev/thelumiereguy/util/transformations.kt: -------------------------------------------------------------------------------- 1 | package dev.thelumiereguy.util 2 | 3 | import processing.core.PApplet 4 | 5 | inline fun PApplet.rotate(angle: Float, block: () -> Unit) { 6 | pushMatrix() 7 | rotate(angle) 8 | block() 9 | popMatrix() 10 | } -------------------------------------------------------------------------------- /processing/src/main/libs/processing-core.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/thelumiereguy/CreativeCodingExamples/94372d747bcaae6a710753832e8d7fddc3d0ccc4/processing/src/main/libs/processing-core.jar -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | rootProject.name = "CreativeCodingExamples" 2 | 3 | pluginManagement { 4 | repositories { 5 | maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") 6 | google() 7 | gradlePluginPortal() 8 | mavenCentral() 9 | } 10 | 11 | plugins { 12 | kotlin("multiplatform") version extra["kotlin.version"] as String 13 | kotlin("android") version extra["kotlin.version"] as String 14 | id("com.android.application") version extra["agp.version"] as String 15 | id("com.android.library") version extra["agp.version"] as String 16 | id("org.jetbrains.compose") version extra["compose.version"] as String 17 | id("org.jetbrains.kotlin.jvm") version extra["kotlin.version"] as String 18 | } 19 | 20 | } 21 | 22 | include("processing") 23 | include(":k5-compose") 24 | include(":androidApp") 25 | include(":compose_common") --------------------------------------------------------------------------------