Repository: sdsmdg/trianglify Branch: develop Commit: e3ba5f976cb8 Files: 77 Total size: 187.4 KB Directory structure: gitextract_a4up19b2/ ├── .gitignore ├── .project ├── .settings/ │ └── org.eclipse.buildship.core.prefs ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── DOCUMENTATION.md ├── LICENSE.md ├── README.md ├── app/ │ ├── .classpath │ ├── .gitignore │ ├── .project │ ├── .settings/ │ │ └── org.eclipse.buildship.core.prefs │ ├── build.gradle │ ├── proguard-rules.pro │ └── src/ │ ├── androidTest/ │ │ └── java/ │ │ └── com/ │ │ └── sdsmdg/ │ │ └── kd/ │ │ └── trianglifyexample/ │ │ └── ExampleInstrumentedTest.java │ ├── main/ │ │ ├── AndroidManifest.xml │ │ ├── java/ │ │ │ └── com/ │ │ │ └── sdsmdg/ │ │ │ └── kd/ │ │ │ └── trianglifyexample/ │ │ │ ├── AboutActivity.java │ │ │ ├── CustomPalettePickerActivity.java │ │ │ └── MainActivity.java │ │ └── res/ │ │ ├── drawable/ │ │ │ ├── color_picked_imageview_border.xml │ │ │ ├── ok_icon.xml │ │ │ └── palette_icon.xml │ │ ├── layout/ │ │ │ ├── activity_about.xml │ │ │ ├── activity_custom_palette_picker.xml │ │ │ └── activity_main.xml │ │ ├── menu/ │ │ │ ├── menu.xml │ │ │ └── menu_custom_palette_picker.xml │ │ ├── values/ │ │ │ ├── colors.xml │ │ │ ├── dimens.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ └── values-w820dp/ │ │ └── dimens.xml │ └── test/ │ └── java/ │ └── com/ │ └── sdsmdg/ │ └── kd/ │ └── trianglifyexample/ │ └── ExampleUnitTest.java ├── build.gradle ├── gradle/ │ └── wrapper/ │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradle.properties ├── gradlew ├── gradlew.bat ├── resources/ │ └── tables/ │ ├── palette-methods-table.tgn │ └── trianglify-view-attributes-table.tgn ├── settings.gradle └── trianglify/ ├── .classpath ├── .gitignore ├── .project ├── .settings/ │ └── org.eclipse.buildship.core.prefs ├── build.gradle ├── proguard-rules.pro └── src/ ├── androidTest/ │ └── java/ │ └── com/ │ └── sdsmdg/ │ └── kd/ │ └── trianglify/ │ └── ExampleInstrumentedTest.java ├── main/ │ ├── AndroidManifest.xml │ ├── java/ │ │ └── com/ │ │ └── sdsmdg/ │ │ └── kd/ │ │ └── trianglify/ │ │ ├── models/ │ │ │ ├── Grid.java │ │ │ ├── Palette.java │ │ │ └── Triangulation.java │ │ ├── presenters/ │ │ │ └── Presenter.java │ │ ├── utilities/ │ │ │ ├── ExtendedColor.java │ │ │ ├── Point.java │ │ │ ├── ThreadLocalRandom.java │ │ │ ├── Utilities.java │ │ │ ├── colorizers/ │ │ │ │ ├── Colorizer.java │ │ │ │ └── FixedPointsColorizer.java │ │ │ ├── patterns/ │ │ │ │ ├── Circle.java │ │ │ │ ├── Patterns.java │ │ │ │ └── Rectangle.java │ │ │ └── triangulator/ │ │ │ ├── DelaunayTriangulation.java │ │ │ ├── DelaunayTriangulator.java │ │ │ ├── Edge2D.java │ │ │ ├── EdgeDistancePack.java │ │ │ ├── NotEnoughPointsException.java │ │ │ ├── Triangle2D.java │ │ │ ├── TriangleSoup.java │ │ │ └── Vector2D.java │ │ └── views/ │ │ ├── TrianglifyView.java │ │ └── TrianglifyViewInterface.java │ └── res/ │ └── values/ │ ├── attrs.xml │ └── strings.xml └── test/ └── java/ └── com/ └── sdsmdg/ └── kd/ └── trianglify/ └── ExampleUnitTest.java ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Built application files *.apk *.ap_ # Files for the Dalvik VM *.dex # Java class files *.class # Generated files bin/ gen/ # Gradle files .gradle/ build/ # Local configuration file (sdk path, etc) local.properties # Proguard folder generated by Eclipse proguard/ # IntelliJ files .idea *.iml # Maven output folder target # Misc .DS_Store Thumbs.db *.swp *.bak ================================================ FILE: .project ================================================ trianglify Project trianglify created by Buildship. org.eclipse.buildship.core.gradleprojectbuilder org.eclipse.buildship.core.gradleprojectnature ================================================ FILE: .settings/org.eclipse.buildship.core.prefs ================================================ connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) connection.project.dir= eclipse.preferences.version=1 ================================================ FILE: .travis.yml ================================================ language: android jdk: oraclejdk8 sudo: required android: components: - tools - platform-tools - tools - build-tools-26.0.2 - android-27 - extra-google-m2repository - extra-android-m2repository script: - ./gradlew build - ./gradlew test before_install: - python <(curl -s https://raw.githubusercontent.com/TouK/sputnik-ci/master/sputnik-ci.py) env: global: - secure: PIbHGVr+Bv0HbK6qOE6C0FrVqcsm/Q6kzFCOa4y9ixT8+2i82DDP4kyyy2ymKevmps+7+SCeJiW8KQkZa7gztSG0h1BoHJo0C6f0xiMYG4kY/7Gq4ZX3mehOVFUymxqhVED/NMK4a7JJxqcSH7JIkBLkuEaueqxc3CmHjI7LCqTr9yMdu/8ulhOeEL/Drg0zsNK9424K6BtxnTh6nNmbNfRnvxCgFc3mLuZ4J6/YN7lBagNiBhpr3XsXdvUC/W89UCcQCIkl/xBKPTxoUGxD6YG+IhapEJrQvqsFkvwFcsUUa9iL7+23QDlK7VGXeKZxwcMYWjRSVai3np94cHZwqivL1okoMFnIjqcAAzX5NOgd0VvM1dUqIwkXQDhp9IPGqDe/H1BiuRb8rmbJyRsxx5YjwksuPugDcG/B24qhyz4NM+gupMx0UE1DXS20xtE1kItEMzCyOJGXDwD9VR13ED0kYkcZoovPFdGFUOpAyAE9UYe1wDpDbmxF8o24tM5ICTcaATpiowWvBmS3i2qsEg8akpxmOqwWK//aOg2aNRiiX5we1H8QK8QNT0MoRf7B9bh59BHL6aOJ/jCeiYXb1jjWGQCGO9Vh3lzJWw2V9f2CvnUaZfeJOCzLgVPqtrI9E1LoPLiVT0Izx9DZ+aINrBxhNfndz5UZ4VQPHF8DDe4= notifications: slack: sdsmdg:hvFSzqFNHm3t6NDR2H2Twiio ================================================ FILE: CHANGELOG.md ================================================ # Trianglify Releases ## Latest * [Version 1.0.0](https://bintray.com/suyashmahar/trianglify/trianglify/1.0.0) | [Google Play](http://bit.ly/trianglifyDemo) **First stable release of Trianglify** * Updated documents so that Trianglify can be seamlessly integrated anywhere * Fixed bugs in demo app ## Previous * [Version 0.9 beta](https://bintray.com/suyashmahar/trianglify/trianglify/0.9-beta) | [demo apk](https://drive.google.com/open?id=0Bz_2jvdEtUlrWEpxQ2Y2RnJGc1U) * Added custom palette demonstration to demo app * Added method `fillViewCompletely` to check if view is incompletely filled * Fixes many bugs - [#23](https://github.com/sdsmdg/trianglify/issues/23), [#33](https://github.com/sdsmdg/trianglify/issues/33), [#34](https://github.com/sdsmdg/trianglify/issues/34), [#35](https://github.com/sdsmdg/trianglify/issues/35) & [#36](https://github.com/sdsmdg/trianglify/issues/36) * [Version 0.8 beta](https://bintray.com/suyashmahar/trianglify/trianglify/0.8-beta) | [demo apk](https://www.dropbox.com/s/cn85g497nkwmx14/trianglify-release-0.8-beta.apk?dl=0) * Generation of triangulation on worker thread to prevent UI freezing [#20](https://github.com/sdsmdg/trianglify/issues/20) * Correct coloring of view, color now drawn matches its name [#18](https://github.com/sdsmdg/trianglify/issues/18) * Support for custom palette ([usage](#26-using-custom-palettes)) [#17](https://github.com/sdsmdg/trianglify/issues/17) * [Version 0.7 beta](https://bintray.com/suyashmahar/trianglify/trianglify/0.7-beta) [Google Play](https://suyashmahar.me/404) * Added library to jCenter | [Link](https://bintray.com/suyashmahar/trianglify/trianglify/) ================================================ FILE: CONTRIBUTING.md ================================================ Contributing to Trianglify ========================== :+1::tada: First off, thanks for taking the time to contribute! :tada::+1: If you'd like to report a bug or join in the development of Trianglify, then here are some notes on how to do that. ## Contents * [Reporting bugs and opening issues](#reporting-bugs-and-opening-issues) * [Coding Guidelines](#coding-guidelines) * [Pull Requests](#pull-requests) * [MVP architecture](#mvp-architecture) * [Style Check](#style-check) * [Git Commit Messages](#git-commit-messages) * [MDG Chat Room](#mdg-chat-room) * [Security](#security) ## Reporting bugs and opening issues If you'd like a report a bug or open an issue then please: **Check if there is an existing issue.** If there is then please add any more information that you have, or give it a 👍. When submitting an issue please describe the issue as clearly as possible, including how to reproduce the bug, which situations it appears in, what you expected to happen, and what actually happens. If you can include a screenshot for front end issues that is very helpful. ## Coding Guidelines ### Pull Requests We love pull requests, so be bold with them! Don't be afraid of going ahead and changing something, or adding a new feature. We're very happy to work with you to get your changes merged into Trianglify. If you've got an idea for a change then please discuss it in the open first, either by opening an issue, or by joining us in our [MDG public chat room](https://mdg.sdslabs.co/chat). If you're looking for something to work on, have a look at the open issues in the repository [here](https://github.com/sdsmdg/trianglify/issues). > We don't have a set format for Pull requests, but we expect you to list changes, bugs generated and other relevant things in PR message. ### MVP architecture Trianglify is built keeping [MVP (model-view-presenter)](https://en.wikipedia.org/wiki/Model–view–presenter) architecture in mind, so any changes that are proposed to Trianglfiy should follow MVP architecture. If you are confused regarding where a method should be, join us at [MDG public chat room](https://mdg.sdslabs.co/chat), we'll be happy to help. ### Style Check Trianglify uses Sputnik for performing style checks on the codebase, which helps us in maintaining the quality of the code. Sputnik checks for violation upon submission of Pull Requests. If Sputnik reports a violation and you believe that it is not applicable, just comment `N/A` on the sputnik review with the reason of why it is not applicable. **Pull Requests will only be merged once all the violations are resolved**. ### Git Commit Messages * Use the present tense ("Add feature" not "Added feature") * Use the imperative mood ("Move cursor to..." not "Moves cursor to...") * Limit the first line to 72 characters or less * Reference issues and pull requests liberally * When only changing documentation, include `[ci skip]` in the commit description * Consider starting the commit message with an applicable emoji: * :art: `:art:` when improving the format/structure of the code * :racehorse: `:racehorse:` when improving performance * :non-potable_water: `:non-potable_water:` when plugging memory leaks * :memo: `:memo:` when writing docs * :bug: `:bug:` when fixing a bug * :fire: `:fire:` when removing code or files * :green_heart: `:green_heart:` when fixing the CI build * :white_check_mark: `:white_check_mark:` when adding tests * :lock: `:lock:` when dealing with security * :arrow_up: `:arrow_up:` when upgrading dependencies * :arrow_down: `:arrow_down:` when downgrading dependencies * :shirt: `:shirt:` when removing linter warnings ## MDG Chat Room If you want to ask any questions in real-time, or get a feel for what's going on then please drop into our [MDG public chat room](https://mdg.iitr.ac.in/chat). If no one is online then you can still leave a message that will hopefully get a reply when we return. ## Security Please do not publish security vulnerabilities publicly until we've had a chance to address them. All security related issues/patches should be sent directly to [mdg@iitr.ac.in](mailto:mdg@iitr.ac.in) where we will attempt to address them quickly. If you're unsure whether something is a security issue or not, then please be cautious and contact us at [mdg@iitr.ac.in](mailto:mdg@iitr.ac.in) first. ================================================ FILE: DOCUMENTATION.md ================================================ Welcome to the Trianglify documentation! Here's the ultimate guide to using Trianglify in your app. # Documentation ## Contents 1. [Usages](#1-usages) 1. [Java](#11-java) 2. [XML](#12-xml) 2. [APIs](#2-api-documentation) 1. [Attributes and Methods](#21-attributes-and-methods) 1. [TrianglifyView](#211-trianglifyview) 2. [Palette](#212-palette) 2. [Details of Bleed and Grid Dimensions](#22-details-of-bleed-and-grid-dimensions) 4. [Note on Units of CellSize, Variance, Bleed & Grid Height](#24-note-on-units-of-cellsize-variance-bleed-and-grid-height) 5. [Setting Palette using setPalette](#25-setting-palette-using-setpalette-method) 6. [Using Custom Palettes](#26-using-custom-palettes) 7. [Updating the View](#27-updating-the-view) 8. [Exporting Bitmap](#28-exporting-bitmap) 3. [Performance Analysis](#3-performance-analysis) ## 1. Usages ### 1.1 Java #### Import Statements Include following lines along with other import statements at the beginning of your activity's java class: ```java import com.sdsmdg.kd.trianglify.views.TrianglifyView; import com.sdsmdg.kd.trianglify.models.Palette; ``` To use trianglify view include the following lines to get an instance of view and set its properties: ```java trianglifyView = (TrianglifyView) findViewById(R.id.trianglify_main_view); trianglifyView.setGridWidth(trianglifyView.getWidth()) .setGridHeight(trianglifyView.getHeight()) .setBleedX(50) .setBleedY(50) .setCellSize(20) .setVariance(10) .setTypeGrid(0) .setPalette(Palette.getPalette(26)) .setDrawStrokeEnabled(true); ``` ### 1.2 XML ```xml ``` ## 2. API Documentation ### 2.1 Attributes and Methods #### 2.1.1 TrianglifyView | Property | Default values | Java method | Attribute | Description | |-----------------------------|----------------|-------------------------|-----------------|-------------------------------------------------------------------------------------------------------------------------------| | Grid Height | NA | .setGridHeight(...) | grid_height | Height of the grid to generate | | Grid Width | NA | .setGridWidth(...) | grid_width | Width of the grid to generate | | X-axis Bleed | 0 px | .setBleedX(...) | bleed_x | TrianglifyView generates total area having width = gridWidth + 2*bleedX to avoid unfilled triangles at the edges of the view | | Y-Axis Bleed | 0 px | .setBleedY(...) | bleed_y | TrianglifyView generates total area having height = gridHeight + 2*bleedY to avoid unfilled triangles at the edges of the view | | Variance | 10 px | .setVariance(...) | variance | Displacement of points from original grid position to create triangles of different sizes | | Cell Size | 40 px | .setCellSize(...) | cell_size | Size of cells of rectangular grid used to generated vertices of the triangles | | Grid Type* | 0 | .setTypeGrid(...) | grid_type | Type of grid 0 for Rectangular | | Fill Triangles with color | true | .setFillTriangle(...) | fill_triangles | Fills the triangle generated with color chosen | | Draw strokes | false | .setDrawStrokes(...) | draw_strokes | Draws triangle's border with neighboring triangle's color | | Color Palette | YlGn | .setPalette(...) | palette | Set of existing colors to color triangles | | Random Coloring | false | .setRandomColoring(...) | random_coloring | If random coloring is on triangles will be colored randomly instead of linear interpolation | | Fill the View Completely | false | .setFillViewCompletely(...) | fillViewCompletely| If fillViewCompletely is true, then it will throw illegalArgumentsException whenever both `BleedX` and `BleedY` are not greater than `cellSize`. Refer to [Section 2.2](#22-details-of-bleed-and-grid-dimensions) *Current release contains only one GridType accessible with id `0` **Other methods** The following methods are getters for corresponding properties and are not covered in the table above: * isDrawStrokeEnabled * isRandomColoringEnabled * isFillTriangle * isFillViewCompletely * getVariance * getTypeGrid * getPalette * getGridWidth * getGridHeight * getCellSize * getBleedX * getBleedY The following are additional methods provided for the developer: **`getViewState`** This method returns the state of the view. For more information on states of view read **Smart update of view using smartUpdate** in [Section 2.7 Updating the View](#27-updating-the-view). **`clearView`** This method clears the triangulation of the view and sets it to `null`. #### 2.1.2 Palette | Method | Return Type | Type | Parameters | Description | |-------------|-------------|-------------|------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------| | Constructor | - | Constructor | int c0, int c1, int c2, int c3, int c4, int c5, int c6, int c7, int c8 | Takes nine parameters as colors to construct palette | | Constructor | - | Constructor | int[] colors | Takes nine parameters as an array of colors to construct palette | | getPalette | Palette | static | int paletteIndex | Returns palette object corresponding to passed value of paletteIndex, palette is constructed from a predefined set of colors. | | indexOf | int | static | Palette palette | Returns index of palette object in predefined palette, -1 if doesn't exists | | getColor | int | - | int index | Returns color corresponding to index passed from the set of colors for a palette |

### 2.2 Details of Bleed and Grid Dimensions * **Bleed:** Bleed defines the dimensions of extra size that TrianglifyView view generates so that triangles on the edge don't appear to be chopped off. In most of the cases `min{bleedX, bleedY} > cellSize` would ensure that the view is completely filled. * **Grid Dimensions:** `GridHeight` and `GridWidth` defines the dimensions of the visible area of the view. Total area generated by TrianglifyView is (`gridHeight` + 2 * `BleedY`) * (`gridWidth` + 2 * `BleedX`) while total area visible is (`gridHeight`) * (`gridWidth`) Following image demonstrates region covered by `gridHeight`, `gridWidth`, `bleedX` and `bleedY`

### 2.4 Note on Units of CellSize, Variance, Bleed and Grid Height Attributes of `CellSize`, `Variance`, `Bleed` and `GridHeight` supplied to TrianglifyView using Java are interpreted as `px`. To convert px to dp or vice-versa define and use following methods: ```java public int dpToPx(int dp) { DisplayMetrics displayMetrics = this.getResources().getDisplayMetrics(); return Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT)); } public int pxToDp(int px) { DisplayMetrics displayMetrics = this.getResources().getDisplayMetrics(); return Math.round(px / (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT)); } ``` ### 2.5 Setting Palette using setPalette method `TrianglifyView`'s `setPalette` method accepts `Palette` object. Palette can be set using one of the following methods: * To use one of the predefined palette, for example **Spectral** use `trianglifyView.setPalette(Palette.getPalette(Palette.Spectral))`. * To use palette referring to its index use `Palette.getPalette()`. * To use custom palette refer to section [2.6 Using custom Palettes](#26-using-custom-palettes). > **Note:** Index used for addressing palette should be less than `Palette.DEFAULT_PALETTE_COUNT`, if index is greater than `DEFAULT_PALETTE_COUNT` `IllegalArgumentException` is thrown. **Note: Palette enum defines total of 28 named palettes that can be used to generate views without specifying colors.** ### 2.6 Using Custom Palettes Custom Palettes can be used by creating new palette using one of the following constructor: ```java Palette customPalette = new Palette(int c0, int c1, int c2, int c3, int c4, int c5, int c6, int c7, int c8); ``` or ```java Palette customPalette = new Palette(int colors[]); ``` > Array based constructor will throw `IllegalArgumentException` if size of array is not exactly 9. ### 2.7 Updating the View `TrianglifyView` should be updated on change of any of the parameter for changes to take effect. `TrianglifyView` can be updated by the following two methods: **Smart update of view using `smartUpdate`** `smartUpdate` updates the view smartly by looking at the parameter changes in the view since the last update, and accordingly decides which parts of the view should be updated. More formal description is: * If only the `drawStroke` or `fillTriangle` values have been changed since the last update, the triangulation is only replotted in accordance to the new paint strokes. * If the `randomColoring` or `palette` have been changed without any changes in the grid parameters, only the new colors are assigned to the triangulation without regenerating the grid and delaunay triangulation, after which the view is plotted in accordance to the `fillTriangle` and `drawStrokes` parameters. * If there are changes in the grid parameters, the whole triangulation has to be generated from scratch. The method generates a new grid according to the parameters and fits a delaunay triangulation. This is followed by colorization and plotting of the triangulation onto the view. **Complete regeneration using `generateAndInvalidate`** This is used when the triangulation is to be generated from scratch. The method generates a grid according to the parameters and fits a delaunay triangulation. This is followed by colorization and plotting of the triangulation onto the view. **(Basic) Performance Comparision of the Two Methods** `smartUpdate` method regenerates the whole triangulation only when the grid parameters have been changed, thereby bypassing the unnecessary regeneration of grid and delaunay triangulation in situations when parameters other than grid parameters have been changed. This leads to a faster rendering of view. The `generateAndInvalidate` method regenerates the whole triangulation irrespective of which parameters have been changed, carrying out steps that might not be necessary, hence causing hindrance to performance. ### 2.8 Exporting Bitmap `TrianglifyView` can export Bitmap object of the triangulation plotted, to get a bitmap object use `getBitmap` method of trianglifyView. `getBitmap` returns a mutable bitmap object that is of `ARGB_8888` configuration. Quality of the bitmap obtained can be set using `setQuality` method of TrianglifyView which accepts one of the three possible parameter `TrianglifyView.DRAWING_CACHE_QUALITY_HIGH`, `TrianglifyView.DRAWING_CACHE_QUALITY_LOW` and `TrianglifyView.DRAWING_CACHE_QUALITY_AUTO`. ## 3. Performance Analysis Few notes on performance of Trianglify * Performance takes a serious hit with decrease in cell size. Time complexity of the algorithm to generate triangles from grid of points is Ω(n*log(n)). Decreasing cell size increases n (number of points on the grid). * Performance of coloring is faster on the use of random coloring rather than gradient. * On use of smartUpdate, performance of coloring depends on the parameters changed since last triangulation. Check [2.7 Updating the View](#27-updating-the-view) for more details. ================================================ FILE: LICENSE.md ================================================ Copyright 2016-17 SDSMDG Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================  # Trianglify [![platform](https://img.shields.io/badge/platform-Android-yellow.svg)](https://www.android.com) [![API](https://img.shields.io/badge/API-16%2B-brightgreen.svg?style=flat)](https://android-arsenal.com/api?level=16s) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![by-SDS-MDG](https://img.shields.io/badge/by-MDG--IITR-blue.svg)](http://mdg.iitr.ac.in) Get it on Google Play Trianglify is an Android library that helps creates views with beautiful patterns. Trianglify is based on MVP architecture and licensed under MIT license. # How to Apply Include following line in the gradle script of your application to include latest release of Trianglify: ```gradle compile 'com.sdsmdg.kd:trianglify:1.0.0' ``` # Features **Cell Size** ![alt text](resources/gifs/cell_size.gif) **Random Coloring** ![alt text](resources/gifs/random_coloring.gif) # Examples **Navigation Panes** ![alt text](resources/gifs/navigation_pane.gif) **Action Bars** ![alt text](resources/gifs/actionBar.gif) # Latest Release * [Version 1.0.0](https://bintray.com/suyashmahar/trianglify/trianglify/1.0.0) | [Google Play](http://bit.ly/trianglifyDemo) **First stable release of Trianglify** * Updated documents so that Trianglify can be seamlessly integrated anywhere * Fixed bugs in demo app For more check complete [change log](/CHANGELOG.md). # Usages and Documentation For details on usage and documentation of Trianglify, check documentation [here](DOCUMENTATION.md). # Credits Trianglify is inspired from qrohlf's work to generate triangle art, [Trianglify](https://github.com/qrohlf/trianglify) Development of Trianglify wouldn't have been possible without following libraries: * [Delaunay Triangulator by jdiemke](https://github.com/jdiemke/delaunay-triangulator) * [Color Picker by QuadFlask](https://github.com/QuadFlask/colorpicker) # Guidelines for Contributors If you want to contribute to improve this library, please read [our guidelines](CONTRIBUTING.md). # License Trianglify is licensed under `MIT license`. View license [here](LICENSE.md). ================================================ FILE: app/.classpath ================================================ ================================================ FILE: app/.gitignore ================================================ /build ================================================ FILE: app/.project ================================================ app Project app created by Buildship. org.eclipse.jdt.core.javabuilder org.eclipse.buildship.core.gradleprojectbuilder org.eclipse.jdt.core.javanature org.eclipse.buildship.core.gradleprojectnature ================================================ FILE: app/.settings/org.eclipse.buildship.core.prefs ================================================ connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) connection.project.dir=.. eclipse.preferences.version=1 ================================================ FILE: app/build.gradle ================================================ apply plugin: 'com.android.application' android { compileSdkVersion 27 buildToolsVersion '26.0.2' defaultConfig { applicationId "com.sdsmdg.kd.trianglifyexample" minSdkVersion 16 targetSdkVersion 27 versionCode 1 versionName "1.0.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } lintOptions { abortOnError false } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) //----Testing frameworks compile project(':trianglify') implementation 'com.android.support:appcompat-v7:27.0.2' implementation 'com.android.support.constraint:constraint-layout:1.0.2' implementation 'com.github.QuadFlask:colorpicker:0.0.13' testCompile 'junit:junit:4.12' testCompile 'org.mockito:mockito-core:2.11.0' } ================================================ FILE: app/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified # in /root/Android/Sdk/tools/proguard/proguard-android.txt # You can edit the include path and order by changing the proguardFiles # directive in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # Add any project specific keep options here: # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} ================================================ FILE: app/src/androidTest/java/com/sdsmdg/kd/trianglifyexample/ExampleInstrumentedTest.java ================================================ package com.sdsmdg.kd.trianglifyexample; import android.content.Context; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import static org.junit.Assert.*; /** * Instrumentation test, which will execute on an Android device. * * @see Testing documentation */ @RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest { @Test public void useAppContext() throws Exception { // Context of the app under test. Context appContext = InstrumentationRegistry.getTargetContext(); assertEquals("com.sdsmdg.kd.trianglifyexample", appContext.getPackageName()); } } ================================================ FILE: app/src/main/AndroidManifest.xml ================================================ ================================================ FILE: app/src/main/java/com/sdsmdg/kd/trianglifyexample/AboutActivity.java ================================================ package com.sdsmdg.kd.trianglifyexample; import android.content.ActivityNotFoundException; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.support.v7.app.ActionBar; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.text.SpannableString; import android.text.style.UnderlineSpan; import android.util.Log; import android.view.MenuItem; import android.view.View; import android.widget.ImageView; import android.widget.TextView; public class AboutActivity extends AppCompatActivity { private static final String TAG = "AboutActivity"; ImageView githubLinkBtn, reviewLinkBtn, shareLink, backBtn; TextView fragTitle, openSourceLicense, versiontTextView; View bottomMarginLayout; PackageInfo pInfo; String versionName; int versionCode; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_about); // To add underline effect in open source license textView SpannableString content = new SpannableString("view license"); content.setSpan(new UnderlineSpan(), 0, content.length(), 0); // Gets version and build number from package manager try { pInfo = this.getPackageManager().getPackageInfo(this.getPackageName(), 0); versionName = pInfo.versionName; versionCode = pInfo.versionCode; } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { actionBar.setTitle(getString(R.string.about_activity_title)); } try { if (actionBar != null) { actionBar.setDisplayHomeAsUpEnabled(true); } } catch (java.lang.NullPointerException e) { Log.e(TAG, "Null pointer exception in generating back action button"); } versiontTextView = this.findViewById(R.id.about_version_text); openSourceLicense = this.findViewById(R.id.about_license_text); openSourceLicense.setText(content); githubLinkBtn = this.findViewById(R.id.about_github_link); reviewLinkBtn = this.findViewById(R.id.about_rate_link); shareLink = this.findViewById(R.id.about_share_link); versiontTextView.setText(getString(R.string.about_activity_version) + versionName); openSourceLicense.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { displayOpenSourceLicenses(); } }); githubLinkBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Uri uri = Uri.parse("https://github.com/sdsmdg/trianglify"); Intent intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(intent); } }); reviewLinkBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Uri uri = Uri.parse("market://details?id=" + v.getContext().getPackageName()); Intent goToMarket = new Intent(Intent.ACTION_VIEW, uri); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { goToMarket.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_NEW_DOCUMENT | Intent.FLAG_ACTIVITY_MULTIPLE_TASK); } try { startActivity(goToMarket); } catch (ActivityNotFoundException e) { startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("http://play.google.com/store/apps/details?id=" + v.getContext().getPackageName()))); } } }); //TODO: Update link when app releases to marketplace shareLink.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Intent sendIntent = new Intent(); sendIntent.setAction(Intent.ACTION_SEND); sendIntent.putExtra(Intent.EXTRA_TEXT, getResources().getString(R.string.share_application_text) + " " + getResources().getString(R.string.trianglify_store_short_link)); sendIntent.setType("text/plain"); startActivity(sendIntent); } }); } public void displayOpenSourceLicenses() { final AlertDialog alertDialog = new AlertDialog.Builder(this).create(); alertDialog.setTitle(this.getApplicationInfo().name); alertDialog.setMessage(getResources().getString(R.string.license_text)); alertDialog.setButton(AlertDialog.BUTTON_POSITIVE, "OK", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }); alertDialog.setButton(AlertDialog.BUTTON_NEUTRAL, "view license", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(getResources().getString(R.string.license_link)))); } }); alertDialog.show(); } // Sets action for Action Bar Items public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: onBackPressed(); return true; default: return super.onOptionsItemSelected(item); } } } ================================================ FILE: app/src/main/java/com/sdsmdg/kd/trianglifyexample/CustomPalettePickerActivity.java ================================================ package com.sdsmdg.kd.trianglifyexample; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.graphics.Color; import android.support.v7.app.ActionBar; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.ImageView; import com.flask.colorpicker.ColorPickerView; import com.flask.colorpicker.OnColorSelectedListener; import com.flask.colorpicker.builder.ColorPickerClickListener; import com.flask.colorpicker.builder.ColorPickerDialogBuilder; import com.sdsmdg.kd.trianglify.models.Palette; import com.sdsmdg.kd.trianglify.views.TrianglifyView; public class CustomPalettePickerActivity extends AppCompatActivity { private static final String TAG = "CustomPalleteActivity"; private Palette palette; private TrianglifyView trianglifyView; private Context context; private ImageView[] imageViews = new ImageView[9]; private int[] colors = {Color.BLACK, Color.BLUE, Color.BLACK, Color.CYAN, Color.DKGRAY, Color.GREEN, Color.RED, Color.MAGENTA, Color.LTGRAY}; public static final String CUSTOM_PALETTE_COLOR_ARRAY = "Custom Palette Color Array"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActionBar actionBar = getSupportActionBar(); setContentView(R.layout.activity_custom_palette_picker); try { if (actionBar != null) { actionBar.setDisplayHomeAsUpEnabled(true); } } catch (java.lang.NullPointerException e) { Log.e(TAG, "Null pointer exception in generating back action button"); } try { if (actionBar != null) { actionBar.setTitle("Custom Palette Picker"); } } catch (java.lang.NullPointerException e) { Log.e(TAG, "Null pointer exception on setting About activity title"); } colors = getIntent().getIntArrayExtra(getResources().getString(R.string.palette_color_array)); context = this; trianglifyView = findViewById(R.id.trianglify_custom_palette_view); trianglifyView.setPalette(new Palette(colors)); trianglifyView.smartUpdate(); for (int i = 0; i < imageViews.length; i++) { String imageViewNumber = "custom_palette_c" + String.valueOf(i); int resID = getResources().getIdentifier(imageViewNumber, "id", getPackageName()); imageViews[i] = findViewById(resID); imageViews[i].setBackgroundColor(colors[i] + 0xff000000); final int finalI = i; imageViews[i].setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { final AlertDialog dialog = ColorPickerDialogBuilder .with(context) .initialColor(colors[finalI] + 0xff000000) .setTitle("Choose Color") .wheelType(ColorPickerView.WHEEL_TYPE.FLOWER) .density(9) .showColorEdit(true) .lightnessSliderOnly() .setColorEditTextColor(0xff000000) .showColorPreview(true) .setOnColorSelectedListener(new OnColorSelectedListener() { @Override public void onColorSelected(int color) { } }) .setPositiveButton("ok", new ColorPickerClickListener() { @Override public void onClick(DialogInterface dialog, int color, Integer[] allColors) { imageViews[finalI].setBackgroundColor(color); colors[finalI] = color - 0xff000000; palette = new Palette(colors); trianglifyView.setPalette(palette); trianglifyView.smartUpdate(); } }) .setNegativeButton("cancel", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { dialog.dismiss(); } }) .build(); dialog.show(); } }); } } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.menu_custom_palette_picker, menu); return true; } public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: onBackPressed(); return true; case R.id.custom_palette_ok: Intent intent = new Intent(); intent.putExtra(CUSTOM_PALETTE_COLOR_ARRAY, colors); setResult(RESULT_OK, intent); onBackPressed(); return true; default: return super.onOptionsItemSelected(item); } } } ================================================ FILE: app/src/main/java/com/sdsmdg/kd/trianglifyexample/MainActivity.java ================================================ package com.sdsmdg.kd.trianglifyexample; import android.Manifest; import android.app.WallpaperManager; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.graphics.Bitmap; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.provider.MediaStore; import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.util.DisplayMetrics; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.SeekBar; import android.widget.Toast; import com.sdsmdg.kd.trianglify.models.Palette; import com.sdsmdg.kd.trianglify.views.TrianglifyView; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import java.util.Random; public class MainActivity extends AppCompatActivity { public TrianglifyView trianglifyView; private SeekBar varianceSeekBar; private SeekBar cellSizeSeekBar; private SeekBar paletteSeekBar; private CheckBox strokeCheckBox; private CheckBox fillCheckBox; private CheckBox randomColoringCheckbox; private CheckBox customPaletteCheckbox; private Palette customPalette; private final int PERMISSION_CODE = 69; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); trianglifyView = findViewById(R.id.trianglify_main_view); trianglifyView.setBitmapQuality(TrianglifyView.DRAWING_CACHE_QUALITY_HIGH); customPalette = trianglifyView.getPalette(); varianceSeekBar = findViewById(R.id.variance_seekbar); varianceSeekBar.setMax(100); varianceSeekBar.setProgress(trianglifyView.getVariance()); varianceSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { trianglifyView.setVariance(progress + 1); trianglifyView.smartUpdate(); } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { } }); cellSizeSeekBar = findViewById(R.id.cell_size_seekbar); int maxCellSize = 150; cellSizeSeekBar.setMax(maxCellSize); cellSizeSeekBar.setProgress(trianglifyView.getCellSize() - 100); cellSizeSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { trianglifyView.setCellSize(progress + 100); trianglifyView.smartUpdate(); } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { } }); paletteSeekBar = findViewById(R.id.palette_seekbar); paletteSeekBar.setMax(Palette.DEFAULT_PALETTE_COUNT - 1); paletteSeekBar.setProgress(Palette.indexOf(trianglifyView.getPalette())); paletteSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { trianglifyView.setPalette(Palette.getPalette(progress)); customPaletteCheckbox.setChecked(false); trianglifyView.smartUpdate(); } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { } }); strokeCheckBox = findViewById(R.id.draw_stroke_checkbox); strokeCheckBox.setChecked(trianglifyView.isDrawStrokeEnabled()); strokeCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked || trianglifyView.isFillTriangle()) { trianglifyView.setDrawStrokeEnabled(isChecked); strokeCheckBox.setChecked(isChecked); trianglifyView.smartUpdate(); } else { strokeCheckBox.setChecked(!isChecked); showColoringError(); } } }); fillCheckBox = findViewById(R.id.draw_fill_checkbox); fillCheckBox.setChecked(trianglifyView.isFillTriangle()); fillCheckBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked || trianglifyView.isDrawStrokeEnabled()) { trianglifyView.setFillTriangle(isChecked); fillCheckBox.setChecked(isChecked); trianglifyView.smartUpdate(); } else { fillCheckBox.setChecked(!isChecked); showColoringError(); } } }); randomColoringCheckbox = findViewById(R.id.random_coloring_checkbox); randomColoringCheckbox.setChecked(trianglifyView.isRandomColoringEnabled()); randomColoringCheckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { trianglifyView.setRandomColoring(isChecked); trianglifyView.smartUpdate(); } }); customPaletteCheckbox = findViewById(R.id.custom_palette_checkbox); customPaletteCheckbox.setChecked(false); customPaletteCheckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked) { trianglifyView.setPalette(customPalette); trianglifyView.smartUpdate(); } else { trianglifyView.setPalette(Palette.getPalette(paletteSeekBar.getProgress())); trianglifyView.smartUpdate(); } } }); } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.menu, menu); return true; } public void updateUIElements(TrianglifyView trianglifyView) { fillCheckBox.setChecked(trianglifyView.isFillTriangle()); strokeCheckBox.setChecked(trianglifyView.isDrawStrokeEnabled()); randomColoringCheckbox.setChecked(trianglifyView.isRandomColoringEnabled()); varianceSeekBar.setProgress(trianglifyView.getVariance()); cellSizeSeekBar.setProgress(trianglifyView.getCellSize()); paletteSeekBar.setProgress(Palette.indexOf(trianglifyView.getPalette())); } public void randomizeTrianglifyParameters(TrianglifyView trianglifyView) { Random rnd = new Random(System.currentTimeMillis()); trianglifyView.setCellSize(dpToPx(rnd.nextInt(10) + 35)) .setPalette(Palette.getPalette(rnd.nextInt(28))) .setRandomColoring(rnd.nextInt(2) == 0) .setFillTriangle(rnd.nextInt(2) == 0) .setDrawStrokeEnabled(rnd.nextInt(2) == 0) .setVariance(rnd.nextInt(60)); if (!trianglifyView.isFillTriangle() && !trianglifyView.isDrawStrokeEnabled()) { trianglifyView.setDrawStrokeEnabled(true); trianglifyView.setFillTriangle(true); } updateUIElements(trianglifyView); } public int dpToPx(int dp) { DisplayMetrics displayMetrics = this.getResources().getDisplayMetrics(); return Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT)); } public int pxToDp(int px) { DisplayMetrics displayMetrics = this.getResources().getDisplayMetrics(); return Math.round(px / (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT)); } // Click handlers for action bar menu items @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.action_save: try { exportImage(); } catch (IOException e) { Toast.makeText(MainActivity.this, "Storage access failed!",Toast.LENGTH_LONG).show(); } break; case R.id.action_about: Intent aboutActivityIntent = new Intent(this, AboutActivity.class); startActivity(aboutActivityIntent); break; case R.id.action_refresh: randomizeTrianglifyParameters(trianglifyView); trianglifyView.generateAndInvalidate(); break; case R.id.custom_palette_picker: Intent customPalettePickerIntent = new Intent(this, CustomPalettePickerActivity.class); customPalettePickerIntent.putExtra(getResources().getString(R.string.palette_color_array), trianglifyView.getPalette().getColors()); startActivityForResult(customPalettePickerIntent, 1); customPaletteCheckbox.setChecked(true); break; case R.id.action_set_wall: setWallpaper(MainActivity.this.trianglifyView); break; default: break; } return true; } public void showColoringError() { Toast.makeText(this, "View should at least be set to draw strokes or fill triangles or both.", Toast.LENGTH_LONG).show(); } public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == 1 && resultCode == RESULT_OK) { customPalette = new Palette(data.getIntArrayExtra(CustomPalettePickerActivity.CUSTOM_PALETTE_COLOR_ARRAY)); if (customPaletteCheckbox.isChecked()) { trianglifyView.setPalette(customPalette); trianglifyView.smartUpdate(); } } } private void exportImage() throws IOException { // Checks if permission is required for android version > 6 boolean permissionStatus = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED; if (permissionStatus) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_CODE); } else { Bitmap bitmap = trianglifyView.getBitmap(); if (bitmap != null) addImageToGallery(bitmap, this); else Toast.makeText(this, "Unable to generate image, please try again", Toast.LENGTH_LONG).show(); } } public static void addImageToGallery(Bitmap bitmap, Context context) throws IOException { String timeStamp = "IMG_" + new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.US).format(new Date()) + ".png"; OutputStream os = new FileOutputStream(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + File.separator + timeStamp); bitmap.compress(Bitmap.CompressFormat.PNG, 100, os); os.flush(); os.close(); Toast.makeText(context, "Saved!", Toast.LENGTH_SHORT).show(); } // Sets bitmap from trianglify view as wallpaper of device public void setWallpaper(final TrianglifyView view) { AlertDialog.Builder alertDgBuilder = new AlertDialog.Builder(this); alertDgBuilder.setMessage(getString(R.string.wall_alert_dg_text)); alertDgBuilder.setPositiveButton("Yes", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { WallpaperManager trianglifyWallpaperManager = WallpaperManager.getInstance(getApplicationContext()); try { trianglifyWallpaperManager.setBitmap(view.getBitmap()); Toast.makeText(MainActivity.this, "Wallpaper set successfuly", Toast.LENGTH_SHORT).show(); } catch (IOException e) { Toast.makeText(MainActivity.this, "Something went wrong, please try again.", Toast.LENGTH_LONG).show(); } } }); alertDgBuilder.setNegativeButton("No", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // Perform inbuilt functions } }); alertDgBuilder.create().show(); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) { switch (requestCode) { case PERMISSION_CODE: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { try { exportImage(); } catch (IOException e) { Toast.makeText(MainActivity.this, "Storage access failed!",Toast.LENGTH_LONG).show(); } } else { Toast.makeText(this, "Storage access failed, check permission", Toast.LENGTH_LONG).show(); } break; } } } ================================================ FILE: app/src/main/res/drawable/color_picked_imageview_border.xml ================================================ ================================================ FILE: app/src/main/res/drawable/ok_icon.xml ================================================ ================================================ FILE: app/src/main/res/drawable/palette_icon.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_about.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_custom_palette_picker.xml ================================================ ================================================ FILE: app/src/main/res/layout/activity_main.xml ================================================ ================================================ FILE: app/src/main/res/menu/menu.xml ================================================

================================================ FILE: app/src/main/res/menu/menu_custom_palette_picker.xml ================================================ ================================================ FILE: app/src/main/res/values/colors.xml ================================================ #009688 #00796B #3F51B5 ================================================ FILE: app/src/main/res/values/dimens.xml ================================================ 16dp 16dp ================================================ FILE: app/src/main/res/values/strings.xml ================================================ Trianglify Demo A place where \nIdeas meet reality! Check out trianglify a library to generate beautiful view for Android. Link: http://bit.ly/trianglifyDemo Trianglify library and all related material are distributed under \'MIT license\' a copy of which can be obtained from link provided. https://github.com/sdsmdg/trianglify/blob/develop/LICENSE.md Save Refresh Customize colors About Set as wallpaper Set current art as wallpaper? Palette Color Array About Version ================================================ FILE: app/src/main/res/values/styles.xml ================================================ ================================================ FILE: app/src/main/res/values-w820dp/dimens.xml ================================================ 64dp ================================================ FILE: app/src/test/java/com/sdsmdg/kd/trianglifyexample/ExampleUnitTest.java ================================================ package com.sdsmdg.kd.trianglifyexample; import org.junit.Test; import static org.junit.Assert.*; /** * Example local unit test, which will execute on the development machine (host). * * @see Testing documentation */ public class ExampleUnitTest { @Test public void addition_isCorrect() throws Exception { assertEquals(4, 2 + 2); } } ================================================ FILE: build.gradle ================================================ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { jcenter() google() } dependencies { classpath 'com.android.tools.build:gradle:3.0.1' classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.4' classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } allprojects { repositories { jcenter() maven { url "https://jitpack.io" } google() } } task clean(type: Delete) { delete rootProject.buildDir } subprojects { tasks.withType(Javadoc).all { enabled = false } } ================================================ FILE: gradle/wrapper/gradle-wrapper.properties ================================================ #Wed Feb 07 13:07:53 IST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip ================================================ FILE: gradle.properties ================================================ # Project-wide Gradle settings. # IDE (e.g. Android Studio) users: # Gradle settings configured through the IDE *will override* # any settings specified in this file. # For more details on how to configure your build environment visit # http://www.gradle.org/docs/current/userguide/build_environment.html # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. org.gradle.jvmargs=-Xmx1536m # When configured, Gradle will run in incubating parallel mode. # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true ================================================ FILE: gradlew ================================================ #!/usr/bin/env bash ############################################################################## ## ## Gradle start up script for UN*X ## ############################################################################## # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. DEFAULT_JVM_OPTS="" APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" warn ( ) { echo "$*" } die ( ) { echo echo "$*" echo exit 1 } # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false case "`uname`" in CYGWIN* ) cygwin=true ;; Darwin* ) darwin=true ;; MINGW* ) msys=true ;; esac # Attempt to set APP_HOME # Resolve links: $0 may be a link PRG="$0" # Need this for relative symlinks. while [ -h "$PRG" ] ; do ls=`ls -ld "$PRG"` link=`expr "$ls" : '.*-> \(.*\)$'` if expr "$link" : '/.*' > /dev/null; then PRG="$link" else PRG=`dirname "$PRG"`"/$link" fi done SAVED="`pwd`" cd "`dirname \"$PRG\"`/" >/dev/null APP_HOME="`pwd -P`" cd "$SAVED" >/dev/null CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables JAVACMD="$JAVA_HOME/jre/sh/java" else JAVACMD="$JAVA_HOME/bin/java" fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else JAVACMD="java" which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi # Increase the maximum file descriptors if we can. if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then MAX_FD_LIMIT=`ulimit -H -n` if [ $? -eq 0 ] ; then if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then MAX_FD="$MAX_FD_LIMIT" fi ulimit -n $MAX_FD if [ $? -ne 0 ] ; then warn "Could not set maximum file descriptor limit: $MAX_FD" fi else warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" fi fi # For Darwin, add options to specify how the application appears in the dock if $darwin; then GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" fi # For Cygwin, switch paths to Windows format before running java if $cygwin ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` SEP="" for dir in $ROOTDIRSRAW ; do ROOTDIRS="$ROOTDIRS$SEP$dir" SEP="|" done OURCYGPATTERN="(^($ROOTDIRS))" # Add a user-defined pattern to the cygpath arguments if [ "$GRADLE_CYGPATTERN" != "" ] ; then OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" fi # Now convert the arguments - kludge to limit ourselves to /bin/sh i=0 for arg in "$@" ; do CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` else eval `echo args$i`="\"$arg\"" fi i=$((i+1)) done case $i in (0) set -- ;; (1) set -- "$args0" ;; (2) set -- "$args0" "$args1" ;; (3) set -- "$args0" "$args1" "$args2" ;; (4) set -- "$args0" "$args1" "$args2" "$args3" ;; (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; esac fi # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules function splitJvmOpts() { JVM_OPTS=("$@") } eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" ================================================ FILE: gradlew.bat ================================================ @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## @rem Set local scope for the variables with windows NT shell if "%OS%"=="Windows_NT" setlocal @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. set DEFAULT_JVM_OPTS= set DIRNAME=%~dp0 if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 if "%ERRORLEVEL%" == "0" goto init echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto init echo. echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% echo. echo Please set the JAVA_HOME variable in your environment to match the echo location of your Java installation. goto fail :init @rem Get command-line arguments, handling Windowz variants if not "%OS%" == "Windows_NT" goto win9xME_args if "%@eval[2+2]" == "4" goto 4NT_args :win9xME_args @rem Slurp the command line arguments. set CMD_LINE_ARGS= set _SKIP=2 :win9xME_args_slurp if "x%~1" == "x" goto execute set CMD_LINE_ARGS=%* goto execute :4NT_args @rem Get arguments from the 4NT Shell from JP Software set CMD_LINE_ARGS=%$ :execute @rem Setup the command line set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% :end @rem End local scope for the variables with windows NT shell if "%ERRORLEVEL%"=="0" goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 exit /b 1 :mainEnd if "%OS%"=="Windows_NT" endlocal :omega ================================================ FILE: resources/tables/palette-methods-table.tgn ================================================ {"rows_views":[[{"style":{"borders":"","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"middle","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"middle","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"middle","padding":{"top":10,"right":5,"bottom":10,"left":5}}}],[{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}}],[{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}}],[{"style":{"borders":"","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"middle","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"middle","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"middle","padding":{"top":10,"right":5,"bottom":10,"left":5}}}],[{"style":{"borders":"","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"middle","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"middle","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"middle","padding":{"top":10,"right":5,"bottom":10,"left":5}}}],[{"style":{"borders":"","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"middle","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"middle","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"middle","padding":{"top":10,"right":5,"bottom":10,"left":5}}}]],"model":{"rows":[[{"value":"Method","cspan":1,"rspan":1},{"value":"Return Type\n","cspan":1,"rspan":1},{"value":"Type\n","cspan":1,"rspan":1},{"value":"Parameters","cspan":1,"rspan":1},{"value":"Description","cspan":1,"rspan":1}],[{"value":"Constructor","cspan":1,"rspan":1},{"value":"-","cspan":1,"rspan":1},{"value":"Constructor","cspan":1,"rspan":1},{"value":"int c0, int c1, int c2, int c3, int c4, int c5, int c6, int c7, int c8","cspan":1,"rspan":1},{"value":"Takes eight parameters as colors to construct palette\n","cspan":1,"rspan":1}],[{"value":"Constructor","cspan":1,"rspan":1},{"value":"-","cspan":1,"rspan":1},{"value":"Constructor","cspan":1,"rspan":1},{"value":"int[] colors","cspan":1,"rspan":1},{"value":"Takes eight parameters as an array of colors to construct palette","cspan":1,"rspan":1}],[{"value":"getPalette","cspan":1,"rspan":1},{"value":"Palette","cspan":1,"rspan":1},{"value":"static","cspan":1,"rspan":1},{"value":"int paletteIndex\n","cspan":1,"rspan":1},{"value":"Returns palette object corresponding to supplied value of paletteIndex, palette is constructed from a predefined set of colors.","cspan":1,"rspan":1}],[{"value":"indexOf","cspan":1,"rspan":1},{"value":"int","cspan":1,"rspan":1},{"value":"static","cspan":1,"rspan":1},{"value":"Palette palette","cspan":1,"rspan":1},{"value":"Returns index of palette object in predefined palette, -1 if doesn't exists\n","cspan":1,"rspan":1}],[{"value":"","cspan":1,"rspan":1},{"value":"int","cspan":1,"rspan":1},{"value":"-\n","cspan":1,"rspan":1},{"value":"int index","cspan":1,"rspan":1},{"value":"Returns color corresponding to index passed from the set of color for a palette","cspan":1,"rspan":1}]]},"theme":null,"fixed_layout":false} ================================================ FILE: resources/tables/trianglify-view-attributes-table.tgn ================================================ {"rows_views":[[{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}}],[{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}}],[{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}}],[{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}}],[{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}}],[{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}}],[{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}}],[{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}}],[{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}}],[{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}}],[{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}}],[{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}}],[{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}},{"style":{"borders":"lrtb","font_style":{},"text_color":"","bg_color":"","halign":"left","valign":"top","padding":{"top":10,"right":5,"bottom":10,"left":5}}}]],"model":{"rows":[[{"value":"Property","cspan":1,"rspan":1},{"value":"Default values\n","cspan":1,"rspan":1},{"value":"Java method\n","cspan":1,"rspan":1},{"value":"Attribute\n","cspan":1,"rspan":1},{"value":"Description","cspan":1,"rspan":1}],[{"value":"Grid Height\n","cspan":1,"rspan":1},{"value":"NA","cspan":1,"rspan":1},{"value":".setGridHeight(...)","cspan":1,"rspan":1},{"value":"grid_height","cspan":1,"rspan":1},{"value":"Height of the grid to generate","cspan":1,"rspan":1}],[{"value":"Grid Width\n","cspan":1,"rspan":1},{"value":"NA","cspan":1,"rspan":1},{"value":".setGridWidth(...)","cspan":1,"rspan":1},{"value":"grid_width\n","cspan":1,"rspan":1},{"value":"Width of the grid to generate","cspan":1,"rspan":1}],[{"value":"X-axis Bleed\n","cspan":1,"rspan":1},{"value":"0","cspan":1,"rspan":1},{"value":".setBleedX(...)","cspan":1,"rspan":1},{"value":"bleed_x","cspan":1,"rspan":1},{"value":"TrianglifyView generates total area having width = gridWidth + 2*bleedX to avoid unfilled triangles at the edges of the view","cspan":1,"rspan":1}],[{"value":"Y-Axis Bleed\n","cspan":1,"rspan":1},{"value":"0 \n","cspan":1,"rspan":1},{"value":".setBleedY(...)","cspan":1,"rspan":1},{"value":"bleed_y","cspan":1,"rspan":1},{"value":"TrianglifyView generates total area having height = gridWidth + 2*bleedY to avoid unfilled triangles at the edges of the view","cspan":1,"rspan":1}],[{"value":"Variance","cspan":1,"rspan":1},{"value":"10 px\n","cspan":1,"rspan":1},{"value":".setVariance(...)","cspan":1,"rspan":1},{"value":"variance\n","cspan":1,"rspan":1},{"value":"Displacement of points from original grid position to create triangles of different sizes","cspan":1,"rspan":1}],[{"value":"Cell Size\n","cspan":1,"rspan":1},{"value":"40 px","cspan":1,"rspan":1},{"value":".setCellSize(...)","cspan":1,"rspan":1},{"value":"cell_size\n","cspan":1,"rspan":1},{"value":"Size of cells of rectangular grid used to generated vertices of the triangles","cspan":1,"rspan":1}],[{"value":"Grid Type*\n","cspan":1,"rspan":1},{"value":"0","cspan":1,"rspan":1},{"value":".setGridType(...)","cspan":1,"rspan":1},{"value":"grid_type\n","cspan":1,"rspan":1},{"value":"Type of grid 0 for Rectangular","cspan":1,"rspan":1}],[{"value":"Fill Triangles with color**\n","cspan":1,"rspan":1},{"value":"true\n","cspan":1,"rspan":1},{"value":".setFillTriangle(...)","cspan":1,"rspan":1},{"value":"fill_triangles","cspan":1,"rspan":1},{"value":"Fills the triangle generated with color chosen","cspan":1,"rspan":1}],[{"value":"Draw strokes\n","cspan":1,"rspan":1},{"value":"false","cspan":1,"rspan":1},{"value":".setDrawStrokes(...)","cspan":1,"rspan":1},{"value":"draw_strokes","cspan":1,"rspan":1},{"value":"Draws triangle's border with neighboring triangle's color","cspan":1,"rspan":1}],[{"value":"Color Palette\n","cspan":1,"rspan":1},{"value":"YlGn","cspan":1,"rspan":1},{"value":".setPalette(...)","cspan":1,"rspan":1},{"value":"palette","cspan":1,"rspan":1},{"value":"Set of existing colors to color triangles","cspan":1,"rspan":1}],[{"value":"Random Coloring\n","cspan":1,"rspan":1},{"value":"false","cspan":1,"rspan":1},{"value":".setRandomColoring(...)","cspan":1,"rspan":1},{"value":"random_coloring","cspan":1,"rspan":1},{"value":"If random coloring is on triangles will be colored randomly instead of linear interpolation","cspan":1,"rspan":1}],[{"value":"","cspan":1,"rspan":1},{"value":"","cspan":1,"rspan":1},{"value":"","cspan":1,"rspan":1},{"value":"","cspan":1,"rspan":1},{"value":"","cspan":1,"rspan":1}]]},"theme":null,"fixed_layout":false} ================================================ FILE: settings.gradle ================================================ include ':app', ':trianglify' ================================================ FILE: trianglify/.classpath ================================================ ================================================ FILE: trianglify/.gitignore ================================================ /build ================================================ FILE: trianglify/.project ================================================ trianglify-trianglify Project trianglify-trianglify created by Buildship. org.eclipse.jdt.core.javabuilder org.eclipse.buildship.core.gradleprojectbuilder org.eclipse.jdt.core.javanature org.eclipse.buildship.core.gradleprojectnature ================================================ FILE: trianglify/.settings/org.eclipse.buildship.core.prefs ================================================ connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER) connection.project.dir=.. eclipse.preferences.version=1 ================================================ FILE: trianglify/build.gradle ================================================ apply plugin: 'com.android.library' ext { bintrayRepo = 'trianglify' bintrayName = 'trianglify' // Has to be same as library module name PUBLISH_GROUP_ID = 'com.sdsmdg.kd' PUBLISH_ARTIFACT_ID = 'trianglify' PUBLISH_VERSION = '1.0.0' publishedGroupId = 'com.sdsmdg.kd' libraryName = 'trianglify' artifact = 'trianglify' // Has to be same as library module name siteUrl = 'https://github.com/sdsmdg/trianglify' gitUrl = 'https://github.com/sdsmdg/trianglify.git' githubRepository= 'sdsmdg/trianglify' libraryVersion = '1.0.0' libraryDescription = 'Generate beautiful view for android.' developerId = 'sdsmdg' developerName = 'SDS, MDG' developerEmail = 'sdsmdg@gmail.com' licenseName = 'MIT License' licenseUrl = 'https://github.com/sdsmdg/trianglify/blob/develop/LICENSE.md' allLicenses = ["MIT"] } android { compileSdkVersion 27 buildToolsVersion '26.0.2' defaultConfig { minSdkVersion 16 targetSdkVersion 27 versionCode 0 versionName "1.0.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } lintOptions { abortOnError false } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) implementation 'com.android.support:appcompat-v7:27.0.2' testCompile 'junit:junit:4.12' testCompile 'org.mockito:mockito-core:2.13.0' } //apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle' //apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/bintrayv1.gradle' ================================================ FILE: trianglify/proguard-rules.pro ================================================ # Add project specific ProGuard rules here. # By default, the flags in this file are appended to flags specified # in /root/Android/Sdk/tools/proguard/proguard-android.txt # You can edit the include path and order by changing the proguardFiles # directive in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html # Add any project specific keep options here: # If your project uses WebView with JS, uncomment the following # and specify the fully qualified class name to the JavaScript interface # class: #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} ================================================ FILE: trianglify/src/androidTest/java/com/sdsmdg/kd/trianglify/ExampleInstrumentedTest.java ================================================ package com.sdsmdg.kd.trianglify; import android.content.Context; import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import static org.junit.Assert.*; /** * Instrumentation test, which will execute on an Android device. * * @see Testing documentation */ @RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest { @Test public void useAppContext() throws Exception { // Context of the app under test. Context appContext = InstrumentationRegistry.getTargetContext(); assertEquals("com.sdsmdg.kd.trianglify.test", appContext.getPackageName()); } } ================================================ FILE: trianglify/src/main/AndroidManifest.xml ================================================ ================================================ FILE: trianglify/src/main/java/com/sdsmdg/kd/trianglify/models/Grid.java ================================================ package com.sdsmdg.kd.trianglify.models; import com.sdsmdg.kd.trianglify.utilities.triangulator.Vector2D; import java.util.List; /** * Created by shyam on 12-Mar-17. */ public class Grid { private List gridPoints; public Grid(List gridPoints) { this.gridPoints = gridPoints; } public List getGridPoints() { return gridPoints; } } ================================================ FILE: trianglify/src/main/java/com/sdsmdg/kd/trianglify/models/Palette.java ================================================ package com.sdsmdg.kd.trianglify.models; /** *

Palette

* Description : * Set of 9 colors that are used to color a triangulation. Palette contains few predefined color sets * as well as method to perform operations on palette. * * @author kriti * @since 18/3/17. */ public class Palette { public static final int DEFAULT_PALETTE_COUNT = 28; private static final int YL_GN = 0; private static final int YL = 1; private static final int YL_GN_BU = 2; private static final int GN_BU = 3; private static final int BU_GN = 4; private static final int PU_BU_GN = 5; private static final int PU_BU = 6; private static final int BU_PU = 7; private static final int RD_PU = 8; private static final int PU_RD = 9; private static final int OR_RD = 10; private static final int YL_OR_RD = 11; private static final int YL_OR_BR = 12; private static final int PURPLES = 13; private static final int BLUES = 14; private static final int GREENS = 15; private static final int ORANGES = 16; private static final int REDS = 17; private static final int GREYS = 18; private static final int PU_OR = 19; private static final int BR_BL = 20; private static final int PU_RD_GN = 21; private static final int PI_YL_GN = 22; private static final int RD_BU = 23; private static final int RD_GY = 24; private static final int RD_YL_BU = 25; private static final int SPECTRAL = 26; private static final int RD_YL_GN = 27; private int[] colors; public int[] getColors() { return colors; } public void setColors(int[] colors) { if (colors.length != 9) { throw new IllegalArgumentException("Colors array length should exactly be 9"); } this.colors = colors; } /** * Return palette object corresponding to supplied value of paletteIndex, palette is constructed * from a predefined set of colors * @param paletteIndex Index of palette to return * @return Palette object generated from predefined set of colors */ public static Palette getPalette(int paletteIndex) { switch (paletteIndex) { case YL: return new Palette(0xffffe0, 0xffffcc, 0xfffacd, 0xffff00, 0xffef00, 0xffd300, 0xf8de7e, 0xffd700, 0xc3b091); case YL_GN: return new Palette(0xffffe5, 0xf7fcb9, 0xd9f0a3, 0xaddd8e, 0x78c679, 0x41ab5d, 0x238443, 0x006837, 0x004529); case YL_GN_BU: return new Palette(0xffffd9, 0xedf8b1, 0xc7e9b4, 0x7fcdbb, 0x41b6c4, 0x1d91c0, 0x225ea8, 0x253494, 0x081d58); case GN_BU: return new Palette(0xf7fcf0, 0xe0f3db, 0xccebc5, 0xa8ddb5, 0x7bccc4, 0x4eb3d3, 0x2b8cbe, 0x0868ac, 0x084081); case BU_GN: return new Palette(0xf7fcfd, 0xe5f5f9, 0xccece6, 0x99d8c9, 0x66c2a4, 0x41ae76, 0x238b45, 0x006d2c, 0x00441c); case PU_BU_GN: return new Palette(0xfff7fb, 0xece2f0, 0xd0d1e6, 0xa6bddb, 0x67a9cf, 0x3690c0, 0x02818a, 0x016c59, 0x014636); case PU_BU: return new Palette(0xfff7fb, 0xece7f2, 0xd0d1e6, 0xa6bddb, 0x74a9cf, 0x3690c0, 0x0570b0, 0x045a8d, 0x023858); case BU_PU: return new Palette(0xf7fcfd, 0xe0ecf4, 0xbfd3e6, 0x9ebcda, 0x8c96c6, 0x8c6bb1, 0x88419d, 0x810f7c, 0x4d004b); case RD_PU: return new Palette(0xfff7f3, 0xfde0dd, 0xfcc5c0, 0xfa9fb5, 0xf768a1, 0xdd3497, 0xae017e, 0x7a0177, 0x49006a); case PU_RD: return new Palette(0xf7f4f9, 0xe7e1ef, 0xd4b9da, 0xc994c7, 0xdf65b0, 0xe7298a, 0xce1256, 0x980043, 0x67001f); case OR_RD: return new Palette(0xfff7ec, 0xfee8c8, 0xfdd49e, 0xfdbb84, 0xfc8d59, 0xef6548, 0xd7301f, 0xb30000, 0x7f0000); case YL_OR_RD: return new Palette(0xffffcc, 0xffeda0, 0xfed976, 0xfeb24c, 0xfd8d3c, 0xfc4e2a, 0xe31a1c, 0xbd0026, 0x800026); case YL_OR_BR: return new Palette(0xffffe5, 0xfff7bc, 0xfee391, 0xfec44f, 0xfe9929, 0xec7014, 0xcc4c02, 0x993404, 0x662506); case PURPLES: return new Palette(0xfcfbfd, 0xefedf5, 0xdadaeb, 0xbcbddc, 0x9e9ac8, 0x807dba, 0x6a51a3, 0x54278f, 0x3f007d); case BLUES: return new Palette(0xf7fbff, 0xdeebf7, 0xc6dbef, 0x9ecae1, 0x6baed6, 0x4292c6, 0x2171b5, 0x08519c, 0x08306b); case GREENS: return new Palette(0xf7fcf5, 0xe5f5e0, 0xc7e9c0, 0xa1d99b, 0x74c476, 0x41ab5d, 0x238b45, 0x006d2c, 0x00441b); case ORANGES: return new Palette(0xfff5eb, 0xfee6ce, 0xfdd0a2, 0xfdae6b, 0xfd8d3c, 0xf16913, 0xd94801, 0xa63603, 0x7f2704); case REDS: return new Palette(0xfff5f0, 0xfee0d2, 0xfcbba1, 0xfc9272, 0xfb6a4a, 0xef3b2c, 0xcb181d, 0xa50f15, 0x67000d); case GREYS: return new Palette(0xffffff, 0xf0f0f0, 0xd9d9d9, 0xbdbdbd, 0x969696, 0x737373, 0x525252, 0x252525, 0x000000); case PU_OR: return new Palette(0x7f3b08, 0xb35806, 0xe08214, 0xfdb863, 0xfee0b6, 0xf7f7f7, 0xd8daeb, 0xb2abd2, 0x8073ac); case BR_BL: return new Palette(0x543005, 0x8c510a, 0xbf812d, 0xdfc27d, 0xf6e8c3, 0xf5f5f5, 0xc7eae5, 0x80cdc1, 0x35978f); case PU_RD_GN: return new Palette(0x40004b, 0x762a83, 0x9970ab, 0xc2a5cf, 0xe7d4e8, 0xf7f7f7, 0xd9f0d3, 0xa6dba0, 0x5aae61); case PI_YL_GN: return new Palette(0x8e0152, 0xc51b7d, 0xde77ae, 0xf1b6da, 0xfde0ef, 0xf7f7f7, 0xe6f5d0, 0xb8e186, 0x7fbc41); case RD_BU: return new Palette(0x67001f, 0xb2182b, 0xd6604d, 0xf4a582, 0xfddbc7, 0xf7f7f7, 0xd1e5f0, 0x92c5de, 0x4393c3); case RD_GY: return new Palette(0x67001f, 0xb2182b, 0xd6604d, 0xf4a582, 0xfddbc7, 0xffffff, 0xe0e0e0, 0xbababa, 0x878787); case RD_YL_BU: return new Palette(0xa50026, 0xd73027, 0xf46d43, 0xfdae61, 0xfee090, 0xffffbf, 0xe0f3f8, 0xabd9e9, 0x74add1); case SPECTRAL: return new Palette(0x9e0142, 0xd53e4f, 0xf46d43, 0xfdae61, 0xfee08b, 0xffffbf, 0xe6f598, 0xabdda4, 0x66c2a5); case RD_YL_GN: return new Palette(0xa50026, 0xd73027, 0xf46d43, 0xfdae61, 0xfee08b, 0xffffbf, 0xd9ef8b, 0xa6d96a, 0x66bd63); default: throw new IllegalArgumentException("Index should be less Palette.DEFAULT_PALETTE_COUNT"); } } /** * Returns index of palette object passed from list of palettes predefined in Palette * @param palette Object for finding index * @return Index from predefined pallete or -1 if not found */ public static int indexOf(Palette palette) { int pos = -1; int[] passedPaletteColors = palette.getColors(); for (int i = 0; i < Palette.DEFAULT_PALETTE_COUNT; i++) { int[] calledPaletteColors = Palette.getPalette(i).getColors(); for (int j = 0; j < 9; j++) { if (passedPaletteColors[j] != calledPaletteColors[j]) { break; } if (j == 8) { return i; } } } return pos; } public Palette(int c0, int c1, int c2, int c3, int c4, int c5, int c6, int c7, int c8) { colors = new int[9]; colors[0] = c0; colors[1] = c1; colors[2] = c2; colors[3] = c3; colors[4] = c4; colors[5] = c5; colors[6] = c6; colors[7] = c7; colors[8] = c8; } public Palette(int[] colors) { if (colors.length != 9) { throw new IllegalArgumentException("Colors array length should exactly be 9"); } this.colors = colors; } /** * Returns color corresponding to index passed from the set of color for a palette * @param index Index of color in set of color for current palette object * @return color as int without alpha channel */ public int getColor(int index) { switch (index) { case 0: return colors[0]; case 1: return colors[1]; case 2: return colors[2]; case 3: return colors[3]; case 4: return colors[4]; case 5: return colors[5]; case 6: return colors[6]; case 7: return colors[7]; case 8: return colors[8]; default: throw new IllegalArgumentException("Index should be less than 9"); } } } ================================================ FILE: trianglify/src/main/java/com/sdsmdg/kd/trianglify/models/Triangulation.java ================================================ package com.sdsmdg.kd.trianglify.models; import com.sdsmdg.kd.trianglify.utilities.triangulator.Triangle2D; import java.util.List; /** * Created by shyam on 12-Mar-17. */ public class Triangulation { private List triangleList; public Triangulation(List triangleList) { this.triangleList = triangleList; } public List getTriangleList() { return triangleList; } } ================================================ FILE: trianglify/src/main/java/com/sdsmdg/kd/trianglify/presenters/Presenter.java ================================================ package com.sdsmdg.kd.trianglify.presenters; import android.os.AsyncTask; import com.sdsmdg.kd.trianglify.models.Triangulation; import com.sdsmdg.kd.trianglify.utilities.colorizers.Colorizer; import com.sdsmdg.kd.trianglify.utilities.colorizers.FixedPointsColorizer; import com.sdsmdg.kd.trianglify.utilities.patterns.Circle; import com.sdsmdg.kd.trianglify.utilities.patterns.Patterns; import com.sdsmdg.kd.trianglify.utilities.patterns.Rectangle; import com.sdsmdg.kd.trianglify.utilities.triangulator.DelaunayTriangulator; import com.sdsmdg.kd.trianglify.utilities.triangulator.NotEnoughPointsException; import com.sdsmdg.kd.trianglify.utilities.triangulator.Vector2D; import com.sdsmdg.kd.trianglify.views.TrianglifyViewInterface; import java.util.List; /** *

Presenter.java

* Description : * P of MVP implemented to present data generated using models * to a view. * * @author suyash * @since 18/3/17. */ public class Presenter { private TrianglifyViewInterface view; private Triangulation triangulation; private TriangleGeneratorTask generatorTask; /** * Flag for keeping track of changes in attributes of the view. Helpful in increasing * performance by stopping unnecessary regeneration of triangulation. Look at smartUpdate method for more. * if triangulation is null then value is NULL_TRIANGULATION * if triangulation is unchanged then value is UNCHANGED_TRIANGULATION * if change in fillTriangle or drawStroke then value is PAINT_STYLE_CHANGED * if change in grid width, grid height, variance, bleedX, bleedY, typeGrid or cell size then value is GRID_PARAMETERS_CHANGED * if change in palette or random coloring then value is COLOR_SCHEME_CHANGED */ public ViewState viewState = ViewState.NULL_TRIANGULATION; public enum ViewState { NULL_TRIANGULATION, UNCHANGED_TRIANGULATION, PAINT_STYLE_CHANGED, COLOR_SCHEME_CHANGED, GRID_PARAMETERS_CHANGED } /** * flag that keeps track of whether just the color of the triangulation is to be changed or not. */ private boolean generateOnlyColor; public Presenter(TrianglifyViewInterface view) { this.view = view; } public void setGenerateOnlyColor(boolean generateOnlyColor) { this.generateOnlyColor = generateOnlyColor; } public void updateView() { viewState = view.getViewState(); if (viewState == ViewState.PAINT_STYLE_CHANGED || viewState == ViewState.UNCHANGED_TRIANGULATION) { view.invalidateView(triangulation); } else if (viewState == ViewState.COLOR_SCHEME_CHANGED) { generateNewColoredSoupAndInvalidate(); } else if (viewState == ViewState.GRID_PARAMETERS_CHANGED || viewState == ViewState.NULL_TRIANGULATION) { generateOnlyColor = false; generateSoupAndInvalidateView(); } } /** * generateNewColoredSoupAndInvalidate method is called when only the coloration of the view is to be changed. It sets the * GenerateOnlyColor boolean of presenter to true, so that when generateSoupAndInvalidateView is * called, only the new colors are assigned to the triangles in the triangulation, since the * grid parameters have not been changed, thereby bypassing the unnecessary regeneration of grid and delaunay triangulation. */ private void generateNewColoredSoupAndInvalidate() { setGenerateOnlyColor(true); generateSoupAndInvalidateView(); } /** * Generates a grid on basis of selected grid type * @return Grid of Vector2D */ private List generateGrid() { int gridType = view.getTypeGrid(); Patterns patterns; switch (gridType) { case TrianglifyViewInterface.GRID_RECTANGLE: patterns = new Rectangle( view.getBleedX(), view.getBleedY(), view.getGridHeight(), view.getGridWidth(), view.getCellSize(), view.getVariance()); break; case TrianglifyViewInterface.GRID_CIRCLE: patterns = new Circle( view.getBleedX(), view.getBleedY(), 8, view.getGridHeight(), view.getGridWidth(), view.getCellSize(), view.getVariance()); break; default: patterns = new Rectangle( view.getBleedX(), view.getBleedY(), view.getGridHeight(), view.getGridWidth(), view.getCellSize(), view.getVariance()); break; } return patterns.generate(); } /** * Generates soup corresponding to current instance parameters * @return triangulation generated */ private Triangulation getSoup() { if (generateOnlyColor) { triangulation = generateColoredSoup(triangulation); } else { generateSoup(); } return triangulation; } /** * Generates colored triangulation. */ private void generateSoup() { triangulation = generateTriangulation(generateGrid()); triangulation = generateColoredSoup(triangulation); } /** * Creates triangles from a list of points * @param inputGrid Grid of points for generating triangles * @return List of Triangles generated from list of input points */ private Triangulation generateTriangulation(List inputGrid) { DelaunayTriangulator triangulator = new DelaunayTriangulator(inputGrid); try { triangulator.triangulate(); } catch (NotEnoughPointsException e) { e.printStackTrace(); } return new Triangulation(triangulator.getTriangles()); } /** * Colors each triangle in triangulation and stores color as triangle's color variable * @param inputTriangulation triangulation to color * @return Colored triangulation of input triangulation */ private Triangulation generateColoredSoup(Triangulation inputTriangulation) { Colorizer colorizer = new FixedPointsColorizer(inputTriangulation, view.getPalette(), view.getGridHeight() + 2*view.getBleedY(), view.getGridWidth() + 2*view.getBleedX(), view.isRandomColoringEnabled()); return colorizer.getColororedTriangulation(); } public void clearSoup() { triangulation = null; viewState = ViewState.NULL_TRIANGULATION; } /** * generateSoupAndInvalidateView method starts a new thread to regenerate the triangulation so * that the regeneration is not done on the UI thread, thereby reducing the workload on the UI thread. */ public void generateSoupAndInvalidateView() { if (generatorTask != null) { generatorTask.cancel(true); } generatorTask = new TriangleGeneratorTask(); generatorTask.execute(); } /** * TriangleGeneratorTask specifies the task of the thread. It regenerates the triangulation in the background in * accordance to the value of the generateOnlyColor boolean. Upon the generation of the triangulation, it calls * upon the invalidateView method to render the triangulation to the view. */ class TriangleGeneratorTask extends AsyncTask { @Override protected Triangulation doInBackground(Void... params) { return getSoup(); } @Override protected void onPostExecute(Triangulation triangulation) { super.onPostExecute(triangulation); view.invalidateView(triangulation); } } } ================================================ FILE: trianglify/src/main/java/com/sdsmdg/kd/trianglify/utilities/ExtendedColor.java ================================================ package com.sdsmdg.kd.trianglify.utilities; /** *

Title

* Description : Extends android.graphics.Color to add function that helps colorizer work. *

* * @author suyash * @since 1/4/17. */ public class ExtendedColor extends android.graphics.Color { public int a; public int r; public int g; public int b; public ExtendedColor(int palleteColor) { this(0xFF, (palleteColor >> 4*4), ((palleteColor >> 4*2) & 0xFF), ((palleteColor) & 0xFF)); } public ExtendedColor(int r, int g, int b) { this(0xFF, r, g, b); } private ExtendedColor(int a, int r, int g, int b) { this.a = a; this.r = r; this.g = g; this.b = b; } public static ExtendedColor avg(ExtendedColor c0, ExtendedColor c1) { return new ExtendedColor((c0.a + c1.a) / 2, (c0.r + c1.r) / 2, (c0.g + c1.g) / 2, (c0.b + c1.b) / 2 ); } public int toInt() { return argb(a, r, g, b); } } ================================================ FILE: trianglify/src/main/java/com/sdsmdg/kd/trianglify/utilities/Point.java ================================================ package com.sdsmdg.kd.trianglify.utilities; /** *

Title

* Description : *

* * @author suyash * @since 25/3/17. */ public class Point{ public int x; public int y; public Point() { this(0, 0); } public Point(int x, int y) { this.x = x; this.y = y; } public static Point subtract(Point a, Point b) { return new Point(a.x - a.y, b.x - b.y); } public static Point add(Point a, Point b) { return new Point(a.x + a.y, b.x + b.y); } /** * Calculates mid point of two given points using integer arithmetic * @param a First Point * @param b Second Point * @return Mid Point */ public static Point midPoint(Point a, Point b) { return new Point((a.x + a.y) / 2, (b.x + b.y) / 2); } } ================================================ FILE: trianglify/src/main/java/com/sdsmdg/kd/trianglify/utilities/ThreadLocalRandom.java ================================================ package com.sdsmdg.kd.trianglify.utilities; /** *

Thread Local Random

* Description : Generates Random numbers that are local to a thread thus reducing instructions * needed to generate a random number. * * @author suyash * @since 2/5/17. */ public class ThreadLocalRandom { private long seed = 0x5DEECE66DL; public ThreadLocalRandom() { } public ThreadLocalRandom(long seed) { this.seed = seed; } /** * Generates a psuedoRandom integer that has computational costs of several instructions less * than that of java.util.random.nextInt(). * @param mod limit for generation of pseudo random number * @return Random number between 0 and mod (exclusive) */ public int nextInt(int mod) { if ( mod != 0 ) { seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1); return (int) (seed % mod); } return 0; } } ================================================ FILE: trianglify/src/main/java/com/sdsmdg/kd/trianglify/utilities/Utilities.java ================================================ package com.sdsmdg.kd.trianglify.utilities; import android.content.Context; import android.util.DisplayMetrics; /** *

Title

* Description : *

* * @author suyash * @since 5/5/17. */ public class Utilities { public int dpToPx(int dp, Context context) { DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); return Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT)); } public int pxToDp(int px, Context context) { DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); return Math.round(px / (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT)); } } ================================================ FILE: trianglify/src/main/java/com/sdsmdg/kd/trianglify/utilities/colorizers/Colorizer.java ================================================ package com.sdsmdg.kd.trianglify.utilities.colorizers; import com.sdsmdg.kd.trianglify.models.Triangulation; /** *

Title

* Description : *

* * @author suyash * @since 24/3/17. */ public interface Colorizer { Triangulation getColororedTriangulation(); } ================================================ FILE: trianglify/src/main/java/com/sdsmdg/kd/trianglify/utilities/colorizers/FixedPointsColorizer.java ================================================ package com.sdsmdg.kd.trianglify.utilities.colorizers; import android.util.Log; import com.sdsmdg.kd.trianglify.models.Palette; import com.sdsmdg.kd.trianglify.models.Triangulation; import com.sdsmdg.kd.trianglify.utilities.ExtendedColor; import com.sdsmdg.kd.trianglify.utilities.Point; import com.sdsmdg.kd.trianglify.utilities.triangulator.Triangle2D; import com.sdsmdg.kd.trianglify.utilities.triangulator.Vector2D; import com.sdsmdg.kd.trianglify.utilities.ThreadLocalRandom; import static android.content.ContentValues.TAG; /** *

Fixed Point Colorizer

* Description : * Fixed point colorizer contains methods that colorize triangles * based on the color palette provided in the constructor. * * @author suyash * @since 24/3/17. */ public class FixedPointsColorizer implements Colorizer { private ThreadLocalRandom random; private Triangulation triangulation; private Palette colorPalette; private int gridWidth; private int gridHeight; private Boolean randomColoring = false; public Palette getColorPalette() { return colorPalette; } public void setColorPalette(Palette colorPalette) { this.colorPalette = colorPalette; } public Triangulation getTriangulation() { return triangulation; } public void setTriangulation(Triangulation triangulation) { this.triangulation = triangulation; } public FixedPointsColorizer(Triangulation triangulation, Palette colorPalette, int gridHeight, int gridWidth) { this(triangulation, colorPalette, gridHeight, gridWidth, false); } public FixedPointsColorizer(Triangulation triangulation, Palette colorPalette, int gridHeight, int gridWidth, Boolean randomColoring) { this.randomColoring = randomColoring; random = new ThreadLocalRandom(System.currentTimeMillis()); this.triangulation = triangulation; this.colorPalette = colorPalette; this.gridHeight = gridHeight; this.gridWidth = gridWidth; } @Override public Triangulation getColororedTriangulation() { if (triangulation != null) { for (Triangle2D triangle : triangulation.getTriangleList()) { triangle.setColor(getColorForPoint(triangle.getCentroid())); } } else { Log.i(TAG, "colorizeTriangulation: Triangulation cannot be null!"); } return getTriangulation(); } /** * Returns color corresponding to the point passed in parameter by * calculating average of specified by the palette. * * * Relation between palette color and position on rectangle is * depicted in the following figure: * *(c1 are corresponding int values representing color in ColorPalette.java) * c0 c1 c2 * +-------------+--------------+ * | | | * | r1 | r2 | * | | | * c7 +------------c8--------------+ c3 * | | | * | r3 | r4 | * | | | * +-------------+--------------+ * c6 c5 c4 * * * Algorithm * Grid provided is divided into four regions r1 to r4. Each of the region * is considered independent on calculating color for a point. * * Sub-rectangle in which given point lies has four vertices, denoted by * Point topLeft, topRight, bottomLeft and bottomRight. Algorith then * calculates weighted mean of color corresponding to vertices (seperately * in x-axis and y-axis). Result of this calculation is returned as int. * * @param point Point to get color for * @return Color corresponding to current point */ // Sorry for such long method, here's a ASCII potato // __ // / \ // / o \ // | o \ // / o | // / o | // \______o__/ // private int getColorForPoint(Vector2D point) { if (randomColoring) { return colorPalette.getColor(random.nextInt(9)); } else { ExtendedColor topLeftColor, topRightColor; ExtendedColor bottomLeftColor, bottomRightColor; Point topLeft, topRight; Point bottomLeft, bottomRight; // Following if..else identifies which sub-rectangle given point lies if (point.x < gridWidth/2 && point.y < gridHeight/2) { topLeftColor = new ExtendedColor(colorPalette.getColor(0)); topRightColor = new ExtendedColor(colorPalette.getColor(1)); bottomLeftColor = new ExtendedColor(colorPalette.getColor(7)); bottomRightColor = new ExtendedColor(colorPalette.getColor(8)); } else if (point.x >= gridWidth/2 && point.y < gridHeight/2) { topLeftColor = new ExtendedColor(colorPalette.getColor(1)); topRightColor = new ExtendedColor(colorPalette.getColor(2)); bottomLeftColor = new ExtendedColor(colorPalette.getColor(8)); bottomRightColor = new ExtendedColor(colorPalette.getColor(3)); } else if (point.x >= gridWidth/2 && point.y >= gridHeight/2) { topLeftColor = new ExtendedColor(colorPalette.getColor(8)); topRightColor = new ExtendedColor(colorPalette.getColor(3)); bottomLeftColor = new ExtendedColor(colorPalette.getColor(5)); bottomRightColor = new ExtendedColor(colorPalette.getColor(4)); } else { topLeftColor = new ExtendedColor(colorPalette.getColor(7)); topRightColor = new ExtendedColor(colorPalette.getColor(8)); bottomLeftColor = new ExtendedColor(colorPalette.getColor(6)); bottomRightColor = new ExtendedColor(colorPalette.getColor(5)); } // Calculate corners of sub rectangle in which point is identified topLeft = new Point( (point.x >= gridWidth/2) ? gridWidth/2 : 0, (point.y >= gridHeight/2) ? gridHeight/2 : 0); topRight = new Point( (point.x >= gridWidth/2) ? gridWidth : gridWidth/2, (point.y >= gridHeight/2) ? gridHeight/2 : 0); bottomLeft = new Point( (point.x >= gridWidth/2) ? gridWidth/2 : 0, (point.y >= gridHeight/2) ? gridHeight : gridHeight/2); bottomRight = new Point( (point.x >= gridWidth/2) ? gridWidth : gridWidth/2, (point.y >= gridHeight/2) ? gridHeight : gridHeight/2); // Calculates weighted mean of colors ExtendedColor weightedTopColor = new ExtendedColor( (int)((topRightColor.r*(point.x - topLeft.x) + (topLeftColor.r)*(topRight.x - point.x)) / ((topRight.x - topLeft.x))), (int)((topRightColor.g*(point.x - topLeft.x) + (topLeftColor.g)*(topRight.x - point.x)) / ((topRight.x - topLeft.x))), (int)((topRightColor.b*(point.x - topLeft.x) + (topLeftColor.b)*(topRight.x - point.x)) / ((topRight.x - topLeft.x))) ); ExtendedColor weightedBottomColor = new ExtendedColor( (int) ((bottomRightColor.r*(point.x - topLeft.x) + (bottomLeftColor.r)*(topRight.x - point.x)) / ((topRight.x - topLeft.x))), (int)((bottomRightColor.g*(point.x - topLeft.x) + (bottomLeftColor.g)*(topRight.x - point.x)) / ((topRight.x - topLeft.x))), (int)((bottomRightColor.b*(point.x - topLeft.x) + (bottomLeftColor.b)*(topRight.x - point.x)) / ((topRight.x - topLeft.x))) ); ExtendedColor weightedLeftColor = new ExtendedColor( (int)((bottomLeftColor.r*(point.y - topLeft.y) + (topLeftColor.r)*(bottomLeft.y - point.y)) / ((bottomLeft.y - topLeft.y))), (int)((bottomLeftColor.g*(point.y - topLeft.y) + (topLeftColor.g)*(bottomLeft.y - point.y)) / ((bottomLeft.y - topLeft.y))), (int)((bottomLeftColor.b*(point.y - topLeft.y) + (topLeftColor.b)*(bottomLeft.y - point.y)) / ((bottomLeft.y - topLeft.y))) ); ExtendedColor weightedRightColor = new ExtendedColor( (int)((bottomRightColor.r*(point.y - topRight.y) + (topRightColor.r)*(bottomRight.y - point.y)) / ((bottomRight.y - topRight.y))), (int)((bottomRightColor.g*(point.y - topRight.y) + (topRightColor.g)*(bottomRight.y - point.y)) / ((bottomRight.y - topRight.y))), (int)((bottomRightColor.b*(point.y - topRight.y) + (topRightColor.b)*(bottomRight.y - point.y)) / ((bottomRight.y - topRight.y))) ); ExtendedColor weightedYColor = new ExtendedColor( (int)((weightedRightColor.r*(point.x - topLeft.x) + (weightedLeftColor.r)*(topRight.x - point.x)) / ((topRight.x - topLeft.x))), (int)((weightedRightColor.g*(point.x - topLeft.x) + (weightedLeftColor.g)*(topRight.x - point.x)) / ((topRight.x - topLeft.x))), (int)((weightedRightColor.b*(point.x - topLeft.x) + (weightedLeftColor.b)*(topRight.x - point.x)) / ((topRight.x - topLeft.x))) ); ExtendedColor weightedXColor = new ExtendedColor( (int)((weightedBottomColor.r*(point.y - topLeft.y) + (weightedTopColor.r)*(bottomLeft.y - point.y)) / ((bottomLeft.y - topLeft.y))), (int)((weightedBottomColor.g*(point.y - topLeft.y) + (weightedTopColor.g)*(bottomLeft.y - point.y)) / ((bottomLeft.y - topLeft.y))), (int)((weightedBottomColor.b*(point.y - topLeft.y) + (weightedTopColor.b)*(bottomLeft.y - point.y)) / ((bottomLeft.y - topLeft.y))) ); return ExtendedColor.avg(weightedXColor, weightedYColor).toInt(); } } /** * Calculates average of given numbers without using floating point * operations * * @param args Values to calculate average of * @return Average of values provided */ private int avg(int...args) { int sum = 0; for (int arg : args) { sum += arg; } return sum/args.length; } } ================================================ FILE: trianglify/src/main/java/com/sdsmdg/kd/trianglify/utilities/patterns/Circle.java ================================================ package com.sdsmdg.kd.trianglify.utilities.patterns; import com.sdsmdg.kd.trianglify.utilities.triangulator.Vector2D; import java.util.List; import java.util.Random; /** * Created by suyash on 12/3/17. */ public class Circle implements Patterns { private final Random random = new Random(); private int bleedX = 0; private int bleedY = 0; private int pointsPerCircle = 8; private int height = 0; private int width = 0; private int cellSize = 0; private int variance = 0; public List grid; public Random getRandom() { return random; } public int getBleedX() { return bleedX; } public void setBleedX(int bleedX) { this.bleedX = bleedX; } public int getBleedY() { return bleedY; } public void setBleedY(int bleedY) { this.bleedY = bleedY; } public int getPointsPerCircle() { return pointsPerCircle; } public void setPointsPerCircle(int pointsPerCircle) { this.pointsPerCircle = pointsPerCircle; } public int getHeight() { return height; } public void setHeight(int height) { this.height = height; } public int getWidth() { return width; } public void setWidth(int width) { this.width = width; } public int getCellSize() { return cellSize; } public void setCellSize(int cellSize) { this.cellSize = cellSize; } public int getVariance() { return variance; } public void setVariance(int variance) { this.variance = variance; } public Circle(int bleedX, int bleedY, int pointsPerCircle, int height, int width, int cellSize, int variance) { this.bleedX = bleedX; this.bleedY = bleedY; this.pointsPerCircle = pointsPerCircle; this.height = height; this.width = width; this.cellSize = cellSize; this.variance = variance; } public List generate() { Vector2D center = new Vector2D(width / 2, height / 2); grid.clear(); int maxRadius = Math.max(width + bleedX, height + bleedY); this.grid.add(center); double slice, angle; int x, y; for (int radius = cellSize; radius < maxRadius; radius += cellSize) { slice = 2 * Math.PI / pointsPerCircle; for (int i = 0; i < pointsPerCircle; i++) { angle = slice * i; x = (int) (center.x + radius * Math.cos(angle)) + random.nextInt(variance); y = (int) (center.y + radius * Math.sin(angle)) + random.nextInt(variance); this.grid.add(new Vector2D(x, y)); } } return grid; } } ================================================ FILE: trianglify/src/main/java/com/sdsmdg/kd/trianglify/utilities/patterns/Patterns.java ================================================ package com.sdsmdg.kd.trianglify.utilities.patterns; import com.sdsmdg.kd.trianglify.utilities.triangulator.Vector2D; import java.util.List; /** * Created by suyash on 17/3/17. */ public interface Patterns { public List generate(); } ================================================ FILE: trianglify/src/main/java/com/sdsmdg/kd/trianglify/utilities/patterns/Rectangle.java ================================================ package com.sdsmdg.kd.trianglify.utilities.patterns; import com.sdsmdg.kd.trianglify.utilities.triangulator.Vector2D; import java.util.ArrayList; import java.util.List; import java.util.Random; import com.sdsmdg.kd.trianglify.utilities.ThreadLocalRandom; /** * Created by suyash on 12/3/17. */ public class Rectangle implements Patterns { private final ThreadLocalRandom random; private int bleedX = 0; private int bleedY = 0; private int height= 0; private int width= 0; private int cellSize = 0; private int variance= 0; List grid; public Rectangle(int bleedX, int bleedY, int height, int width, int cellSize, int variance) { this.bleedX = bleedX; this.bleedY = bleedY; this.variance = variance; this.cellSize = cellSize; this.height = height; this.width = width; random = new ThreadLocalRandom(); grid = new ArrayList<>(); } /** * Generates array of points arranged in a grid of rectangles with deviation from their positions * on the basis of bleed value. * @return List of Vector2D containing points that resembles rectangular grid */ @Override public List generate() { grid.clear(); int x, y; for (int j = 0; j < height + 2*bleedY; j += cellSize) { for (int i = 0; i < width + 2*bleedX; i += cellSize) { x = i + random.nextInt(variance); y = j + random.nextInt(variance); this.grid.add(new Vector2D(x, y)); } } return grid; } } ================================================ FILE: trianglify/src/main/java/com/sdsmdg/kd/trianglify/utilities/triangulator/DelaunayTriangulation.java ================================================ package com.sdsmdg.kd.trianglify.utilities.triangulator; //import Triangulation.Triagulation; /** *

Title

* Description : *

* * @author suyash * @since 31/3/17. */ public class DelaunayTriangulation { } ================================================ FILE: trianglify/src/main/java/com/sdsmdg/kd/trianglify/utilities/triangulator/DelaunayTriangulator.java ================================================ package com.sdsmdg.kd.trianglify.utilities.triangulator; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * A Java implementation of an incremental 2D Delaunay triangulation algorithm. * * @author Johannes Diemke */ public class DelaunayTriangulator { private List pointSet; private TriangleSoup triangleSoup; /** * Constructor of the SimpleDelaunayTriangulator class used to create a new * triangulator instance. * * @param pointSet * The point set to be triangulated * @throws NotEnoughPointsException * Thrown when the point set contains less than three points */ public DelaunayTriangulator(List pointSet) { this.pointSet = pointSet; this.triangleSoup = new TriangleSoup(); } /** * This method generates a Delaunay triangulation from the specified point * set. * * @throws com.sdsmdg.kd.trianglify.utilities.triangulator.NotEnoughPointsException */ public void triangulate() throws NotEnoughPointsException { triangleSoup = new TriangleSoup(); if (pointSet == null || pointSet.size() < 3) { throw new NotEnoughPointsException("Less than three points in point set."); } /** * In order for the in circumcircle test to not consider the vertices of * the super triangle we have to start out with a big triangle * containing the whole point set. We have to scale the super triangle * to be very large. Otherwise the triangulation is not convex. */ float maxOfAnyCoordinate = 0.0f; for (Vector2D vector : getPointSet()) { maxOfAnyCoordinate = Math.max(Math.max(vector.x, vector.y), maxOfAnyCoordinate); } maxOfAnyCoordinate *= 16.0d; float newCoordinate = 3.0f * maxOfAnyCoordinate; final Vector2D p1 = new Vector2D(0.0f, newCoordinate); final Vector2D p2 = new Vector2D(newCoordinate, 0.0f); final Vector2D p3 = new Vector2D(-newCoordinate, -newCoordinate); final Triangle2D superTriangle = new Triangle2D(p1, p2, p3); triangleSoup.add(superTriangle); for (int i = 0; i < pointSet.size(); i++) { Triangle2D triangle = triangleSoup.findContainingTriangle(pointSet.get(i)); if (triangle == null) { /** * If no containing triangle exists, then the vertex is not * inside a triangle (this can also happen due to numerical * errors) and lies on an edge. In order to find this edge we * search all edges of the triangle soup and select the one * which is nearest to the point we try to add. This edge is * removed and four new edges are added. */ final Edge2D edge = triangleSoup.findNearestEdge(pointSet.get(i)); final Triangle2D first = triangleSoup.findOneTriangleSharing(edge); final Triangle2D second = triangleSoup.findNeighbour(first, edge); final Vector2D firstNoneEdgeVertex = first.getNoneEdgeVertex(edge); final Vector2D secondNoneEdgeVertex = second.getNoneEdgeVertex(edge); triangleSoup.remove(first); triangleSoup.remove(second); final Triangle2D triangle1 = new Triangle2D(edge.a, firstNoneEdgeVertex, pointSet.get(i)); final Triangle2D triangle2 = new Triangle2D(edge.b, firstNoneEdgeVertex, pointSet.get(i)); final Triangle2D triangle3 = new Triangle2D(edge.a, secondNoneEdgeVertex, pointSet.get(i)); final Triangle2D triangle4 = new Triangle2D(edge.b, secondNoneEdgeVertex, pointSet.get(i)); triangleSoup.add(triangle1); triangleSoup.add(triangle2); triangleSoup.add(triangle3); triangleSoup.add(triangle4); legalizeEdge(triangle1, new Edge2D(edge.a, firstNoneEdgeVertex), pointSet.get(i)); legalizeEdge(triangle2, new Edge2D(edge.b, firstNoneEdgeVertex), pointSet.get(i)); legalizeEdge(triangle3, new Edge2D(edge.a, secondNoneEdgeVertex), pointSet.get(i)); legalizeEdge(triangle4, new Edge2D(edge.b, secondNoneEdgeVertex), pointSet.get(i)); } else { /** * The vertex is inside a triangle. */ final Vector2D a = triangle.a; final Vector2D b = triangle.b; final Vector2D c = triangle.c; triangleSoup.remove(triangle); final Triangle2D first = new Triangle2D(a, b, pointSet.get(i)); final Triangle2D second = new Triangle2D(b, c, pointSet.get(i)); final Triangle2D third = new Triangle2D(c, a, pointSet.get(i)); triangleSoup.add(first); triangleSoup.add(second); triangleSoup.add(third); legalizeEdge(first, new Edge2D(a, b), pointSet.get(i)); legalizeEdge(second, new Edge2D(b, c), pointSet.get(i)); legalizeEdge(third, new Edge2D(c, a), pointSet.get(i)); } } /** * Remove all triangles that contain vertices of the super triangle. */ triangleSoup.removeTrianglesUsing(superTriangle.a); triangleSoup.removeTrianglesUsing(superTriangle.b); triangleSoup.removeTrianglesUsing(superTriangle.c); } /** * This method legalizes edges by recursively flipping all illegal edges. * * @param triangle * The triangle * @param edge * The edge to be legalized * @param newVertex * The new vertex */ private void legalizeEdge(Triangle2D triangle, Edge2D edge, Vector2D newVertex) { final Triangle2D neighbourTriangle = triangleSoup.findNeighbour(triangle, edge); /** * If the triangle has a neighbor, then legalize the edge */ if (neighbourTriangle != null) { if (neighbourTriangle.isPointInCircumcircle(newVertex)) { triangleSoup.remove(triangle); triangleSoup.remove(neighbourTriangle); final Vector2D noneEdgeVertex = neighbourTriangle.getNoneEdgeVertex(edge); final Triangle2D firstTriangle = new Triangle2D(noneEdgeVertex, edge.a, newVertex); final Triangle2D secondTriangle = new Triangle2D(noneEdgeVertex, edge.b, newVertex); triangleSoup.add(firstTriangle); triangleSoup.add(secondTriangle); legalizeEdge(firstTriangle, new Edge2D(noneEdgeVertex, edge.a), newVertex); legalizeEdge(secondTriangle, new Edge2D(noneEdgeVertex, edge.b), newVertex); } } } /** * Creates a random permutation of the specified point set. Based on the * implementation of the Delaunay algorithm this can speed up the * computation. */ public void shuffle() { Collections.shuffle(pointSet); } /** * Shuffles the point set using a custom permutation sequence. * * @param permutation * The permutation used to shuffle the point set */ public void shuffle(int[] permutation) { List temp = new ArrayList(); for (int i = 0; i < permutation.length; i++) { temp.add(pointSet.get(permutation[i])); } pointSet = temp; } /** * Returns the point set in form of a vector of 2D vectors. * * @return Returns the points set. */ public List getPointSet() { return pointSet; } /** * Returns the trianges of the triangulation in form of a vector of 2D * triangles. * * @return Returns the triangles of the triangulation. */ public List getTriangles() { return triangleSoup.getTriangles(); } } ================================================ FILE: trianglify/src/main/java/com/sdsmdg/kd/trianglify/utilities/triangulator/Edge2D.java ================================================ package com.sdsmdg.kd.trianglify.utilities.triangulator; /** * 2D edge class implementation. * * @author Johannes Diemke */ public class Edge2D { public Vector2D a; public Vector2D b; /** * Constructor of the 2D edge class used to create a new edge instance from * two 2D vectors describing the edge's vertices. * * @param a * The first vertex of the edge * @param b * The second vertex of the edge */ public Edge2D(Vector2D a, Vector2D b) { this.a = a; this.b = b; } } ================================================ FILE: trianglify/src/main/java/com/sdsmdg/kd/trianglify/utilities/triangulator/EdgeDistancePack.java ================================================ package com.sdsmdg.kd.trianglify.utilities.triangulator; /** * Edge distance pack class implementation used to describe the distance to a * given edge. * * @author Johannes Diemke */ public class EdgeDistancePack implements Comparable { public Edge2D edge; public double distance; /** * Constructor of the edge distance pack class used to create a new edge * distance pack instance from a 2D edge and a scalar value describing a * distance. * * @param edge * The edge * @param distance * The distance of the edge to some point */ public EdgeDistancePack(Edge2D edge, double distance) { this.edge = edge; this.distance = distance; } @Override public int compareTo(EdgeDistancePack o) { return Double.compare(this.distance, o.distance); } } ================================================ FILE: trianglify/src/main/java/com/sdsmdg/kd/trianglify/utilities/triangulator/NotEnoughPointsException.java ================================================ package com.sdsmdg.kd.trianglify.utilities.triangulator; /** * Exception thrown by the Delaunay triangulator when it is initialized with * less than three points. * * @author Johannes Diemke */ public class NotEnoughPointsException extends Exception { private static final long serialVersionUID = 7061712854155625067L; public NotEnoughPointsException() { } public NotEnoughPointsException(String s) { super(s); } } ================================================ FILE: trianglify/src/main/java/com/sdsmdg/kd/trianglify/utilities/triangulator/Triangle2D.java ================================================ package com.sdsmdg.kd.trianglify.utilities.triangulator; import java.util.Arrays; /** * 2D triangle class implementation. * * @author Johannes Diemke */ public class Triangle2D { public Vector2D a; public Vector2D b; public Vector2D c; private int color; /** * Constructor of the 2D triangle class used to create a new triangle * instance from three 2D vectors describing the triangle's vertices. * * @param a * The first vertex of the triangle * @param b * The second vertex of the triangle * @param c * The third vertex of the triangle */ public Triangle2D(Vector2D a, Vector2D b, Vector2D c) { this.a = a; this.b = b; this.c = c; } /** * Tests if a 2D point lies inside this 2D triangle. See Real-Time Collision * Detection, chap. 5, p. 206. * * @param point * The point to be tested * @return Returns true iff the point lies inside this 2D triangle */ public boolean contains(Vector2D point) { final double pab = point.sub(a).cross(b.sub(a)); final double pbc = point.sub(b).cross(c.sub(b)); if (!hasSameSign(pab, pbc)) { return false; } double pca = point.sub(c).cross(a.sub(c)); if (!hasSameSign(pab, pca)) { return false; } return true; } /** * Tests if a given point lies in the circumcircle of this triangle. Let the * triangle ABC appear in counterclockwise (CCW) order. Then when det > * 0, the point lies inside the circumcircle through the three points a, b * and c. If instead det < 0, the point lies outside the circumcircle. * When det = 0, the four points are cocircular. If the triangle is oriented * clockwise (CW) the result is reversed. See Real-Time Collision Detection, * chap. 3, p. 34. * * @param point * The point to be tested * @return Returns true iff the point lies inside the circumcircle through * the three points a, b, and c of the triangle */ public boolean isPointInCircumcircle(Vector2D point) { final double a11 = a.x - point.x; final double a21 = b.x - point.x; final double a31 = c.x - point.x; final double a12 = a.y - point.y; final double a22 = b.y - point.y; final double a32 = c.y - point.y; final double a13 = (a.x - point.x) * (a.x - point.x) + (a.y - point.y) * (a.y - point.y); final double a23 = (b.x - point.x) * (b.x - point.x) + (b.y - point.y) * (b.y - point.y); final double a33 = (c.x - point.x) * (c.x - point.x) + (c.y - point.y) * (c.y - point.y); final double det = a11 * a22 * a33 + a12 * a23 * a31 + a13 * a21 * a32 - a13 * a22 * a31 - a12 * a21 * a33 - a11 * a23 * a32; if (isOrientedCCW()) { return det > 0.0d; } return det < 0.0d; } /** * Test if this triangle is oriented counterclockwise (CCW). Let A, B and C * be three 2D points. If det > 0, C lies to the left of the directed * line AB. Equivalently the triangle ABC is oriented counterclockwise. When * det < 0, C lies to the right of the directed line AB, and the triangle * ABC is oriented clockwise. When det = 0, the three points are colinear. * See Real-Time Collision Detection, chap. 3, p. 32 * * @return Returns true iff the triangle ABC is oriented counterclockwise * (CCW) */ public boolean isOrientedCCW() { final double a11 = a.x - c.x; final double a21 = b.x - c.x; final double a12 = a.y - c.y; final double a22 = b.y - c.y; final double det = a11 * a22 - a12 * a21; return det > 0.0d; } /** * Returns true if this triangle contains the given edge. * * @param edge * The edge to be tested * @return Returns true if this triangle contains the edge */ public boolean isNeighbour(Edge2D edge) { return (a == edge.a || b == edge.a || c == edge.a) && (a == edge.b || b == edge.b || c == edge.b); } /** * Returns the vertex of this triangle that is not part of the given edge. * * @param edge * The edge * @return The vertex of this triangle that is not part of the edge */ public Vector2D getNoneEdgeVertex(Edge2D edge) { if (a != edge.a && a != edge.b) { return a; } else if (b != edge.a && b != edge.b) { return b; } else if (c != edge.a && c != edge.b) { return c; } return null; } /** * Returns true if the given vertex is one of the vertices describing this * triangle. * * @param vertex * The vertex to be tested * @return Returns true if the Vertex is one of the vertices describing this * triangle */ public boolean hasVertex(Vector2D vertex) { if (a == vertex || b == vertex || c == vertex) { return true; } return false; } /** * Returns an EdgeDistancePack containing the edge and its distance nearest * to the specified point. * * @param point * The point the nearest edge is queried for * @return The edge of this triangle that is nearest to the specified point */ public EdgeDistancePack findNearestEdge(Vector2D point) { EdgeDistancePack[] edges = new EdgeDistancePack[3]; edges[0] = new EdgeDistancePack(new Edge2D(a, b), computeClosestPoint(new Edge2D(a, b), point).sub(point).mag()); edges[1] = new EdgeDistancePack(new Edge2D(b, c), computeClosestPoint(new Edge2D(b, c), point).sub(point).mag()); edges[2] = new EdgeDistancePack(new Edge2D(c, a), computeClosestPoint(new Edge2D(c, a), point).sub(point).mag()); Arrays.sort(edges); return edges[0]; } /** * Computes the closest point on the given edge to the specified point. * * @param edge * The edge on which we search the closest point to the specified * point * @param point * The point to which we search the closest point on the edge * @return The closest point on the given edge to the specified point */ private Vector2D computeClosestPoint(Edge2D edge, Vector2D point) { final Vector2D ab = edge.b.sub(edge.a); float t = point.sub(edge.a).dot(ab) / ab.dot(ab); if (t < 0.0f) { t = 0.0f; } else if (t > 1.0f) { t = 1.0f; } return edge.a.add(ab.mult(t)); } /** * Tests if the two arguments have the same sign. * * @param a * The first floating point argument * @param b * The second floating point argument * @return Returns true iff both arguments have the same sign */ private boolean hasSameSign(double a, double b) { return Math.signum(a) == Math.signum(b); } public void setColor(int color) { this.color = color; } public int getColor() { return color; } @Override public String toString() { return "Triangle2D[" + a + ", " + b + ", " + c + "]"; } public Vector2D getCentroid() { Vector2D centroid = new Vector2D(0,0); centroid.x = ((a.x) + (b.x) + (c.x))/3; centroid.y = ((a.y) + (b.y) + (c.y))/3; return centroid; } } ================================================ FILE: trianglify/src/main/java/com/sdsmdg/kd/trianglify/utilities/triangulator/TriangleSoup.java ================================================ package com.sdsmdg.kd.trianglify.utilities.triangulator; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; /** * Triangle soup class implementation. * * @author Johannes Diemke */ class TriangleSoup { private HashSet triangleSoup; /** * Constructor of the triangle soup class used to create a new triangle soup * instance. */ public TriangleSoup() { this.triangleSoup = new HashSet<>(); } /** * Adds a triangle to this triangle soup. * * @param triangle * The triangle to be added to this triangle soup */ public void add(Triangle2D triangle) { this.triangleSoup.add(triangle); } /** * Removes a triangle from this triangle soup. * * @param triangle * The triangle to be removed from this triangle soup */ public void remove(Triangle2D triangle) { this.triangleSoup.remove(triangle); } /** * Returns the triangles from this triangle soup. * * @return The triangles from this triangle soup */ public List getTriangles() { return new ArrayList<>(this.triangleSoup); } /** * Returns the triangle from this triangle soup that contains the specified * point or null if no triangle from the triangle soup contains the point. * * @param point * The point * @return Returns the triangle from this triangle soup that contains the * specified point or null */ public Triangle2D findContainingTriangle(Vector2D point) { for (Triangle2D triangle : triangleSoup) { if (triangle.contains(point)) { return triangle; } } return null; } /** * Returns the neighbor triangle of the specified triangle sharing the same * edge as specified. If no neighbor sharing the same edge exists null is * returned. * * @param triangle * The triangle * @param edge * The edge * @return The triangles neighbor triangle sharing the same edge or null if * no triangle exists */ public Triangle2D findNeighbour(Triangle2D triangle, Edge2D edge) { for (Triangle2D triangleFromSoup : triangleSoup) { if (triangleFromSoup.isNeighbour(edge) && triangleFromSoup != triangle) { return triangleFromSoup; } } return null; } /** * Returns one of the possible triangles sharing the specified edge. Based * on the ordering of the triangles in this triangle soup the returned * triangle may differ. To find the other triangle that shares this edge use * the {@link findNeighbour(Triangle2D triangle, Edge2D edge)} method. * * @param edge * The edge * @return Returns one triangle that shares the specified edge */ public Triangle2D findOneTriangleSharing(Edge2D edge) { for (Triangle2D triangle : triangleSoup) { if (triangle.isNeighbour(edge)) { return triangle; } } return null; } /** * Returns the edge from the triangle soup nearest to the specified point. * * @param point * The point * @return The edge from the triangle soup nearest to the specified point */ public Edge2D findNearestEdge(Vector2D point) { List edgeList = new ArrayList(); for (Triangle2D triangle : triangleSoup) { edgeList.add(triangle.findNearestEdge(point)); } EdgeDistancePack[] edgeDistancePacks = new EdgeDistancePack[edgeList.size()]; edgeList.toArray(edgeDistancePacks); Arrays.sort(edgeDistancePacks); return edgeDistancePacks[0].edge; } /** * Removes all triangles from this triangle soup that contain the specified * vertex. * * @param vertex * The vertex */ public void removeTrianglesUsing(Vector2D vertex) { List trianglesToBeRemoved = new ArrayList(); for (Triangle2D triangle : triangleSoup) { if (triangle.hasVertex(vertex)) { trianglesToBeRemoved.add(triangle); } } triangleSoup.removeAll(trianglesToBeRemoved); } } ================================================ FILE: trianglify/src/main/java/com/sdsmdg/kd/trianglify/utilities/triangulator/Vector2D.java ================================================ package com.sdsmdg.kd.trianglify.utilities.triangulator; /** * 2D vector class implementation. * * @author Johannes Diemke */ public class Vector2D { public float x; public float y; /** * Constructor of the 2D vector class used to create new vector instances. * * @param x * The x coordinate of the new vector * @param y * The y coordinate of the new vector */ public Vector2D(float x, float y) { this.x = x; this.y = y; } /** * Subtracts the given vector from this. * * @param vector * The vector to be subtracted from this * @return A new instance holding the result of the vector subtraction */ public Vector2D sub(Vector2D vector) { return new Vector2D(this.x - vector.x, this.y - vector.y); } /** * Adds the given vector to this. * * @param vector * The vector to be added to this * @return A new instance holding the result of the vector addition */ public Vector2D add(Vector2D vector) { return new Vector2D(this.x + vector.x, this.y + vector.y); } /** * Multiplies this by the given scalar. * * @param scalar * The scalar to be multiplied by this * @return A new instance holding the result of the multiplication */ public Vector2D mult(float scalar) { return new Vector2D(this.x * scalar, this.y * scalar); } /** * Computes the magnitude or length of this. * * @return The magnitude of this */ public float mag() { return (float)Math.sqrt(this.x * this.x + this.y * this.y); } /** * Computes the dot product of this and the given vector. * * @param vector * The vector to be multiplied by this * @return A new instance holding the result of the multiplication */ public float dot(Vector2D vector) { return this.x * vector.x + this.y * vector.y; } /** * Computes the 2D pseudo cross product Dot(Perp(this), vector) of this and * the given vector. * * @param vector * The vector to be multiplied to the perpendicular vector of * this * @return A new instance holding the result of the pseudo cross product */ public float cross(Vector2D vector) { return this.y * vector.x - this.x * vector.y; } @Override public String toString() { return "Vector2D[" + x + ", " + y + "]"; } } ================================================ FILE: trianglify/src/main/java/com/sdsmdg/kd/trianglify/views/TrianglifyView.java ================================================ package com.sdsmdg.kd.trianglify.views; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; import android.util.AttributeSet; import android.view.View; import com.sdsmdg.kd.trianglify.presenters.Presenter; import com.sdsmdg.kd.trianglify.R; import com.sdsmdg.kd.trianglify.models.Palette; import com.sdsmdg.kd.trianglify.models.Triangulation; import com.sdsmdg.kd.trianglify.utilities.triangulator.Triangle2D; public class TrianglifyView extends View implements TrianglifyViewInterface{ private int bleedX; private int bleedY; private int gridHeight; private int gridWidth; private int typeGrid; private int variance; private int cellSize; private int paletteNumber; private boolean fillTriangle; private boolean drawStroke; private boolean randomColoring; private Palette palette; private Triangulation triangulation; private Presenter presenter; private int bitmapQuality; /** *This variable is used to know whether the user wants the view to completely fill the passed gridHeight * and gridWidth. If it is TRUE, then it will throw an exception whenever either bleedX or bleedY are not * greater than the cellSize. If it is FALSE, then it will not check for the above condition, and hence will * not throw an exception. */ private boolean fillViewCompletely; public TrianglifyView(Context context, AttributeSet attrs) { super(context, attrs); TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.TrianglifyView, 0, 0); attributeSetter(a); this.presenter = new Presenter(this); this.setDrawingCacheEnabled(true); this.setDrawingCacheQuality(DRAWING_CACHE_QUALITY_AUTO); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); setGridWidth(w); setGridHeight(h); smartUpdate(); } private void attributeSetter(TypedArray typedArray) { bleedX = (int) typedArray.getDimension(R.styleable.TrianglifyView_bleedX, 0); bleedY = (int) typedArray.getDimension(R.styleable.TrianglifyView_bleedY, 0); variance = (int) typedArray.getDimension(R.styleable.TrianglifyView_variance, 10); cellSize = (int) typedArray.getDimension(R.styleable.TrianglifyView_cellSize, 40); typeGrid = typedArray.getInt(R.styleable.TrianglifyView_gridType, 0); fillTriangle = typedArray.getBoolean(R.styleable.TrianglifyView_fillTriangle, true); drawStroke = typedArray.getBoolean(R.styleable.TrianglifyView_fillStrokes, false); paletteNumber = typedArray.getInt(R.styleable.TrianglifyView_palette, 0); palette = Palette.getPalette(paletteNumber); typeGrid = GRID_RECTANGLE; randomColoring = typedArray.getBoolean(R.styleable.TrianglifyView_randomColoring, false); fillViewCompletely = typedArray.getBoolean(R.styleable.TrianglifyView_fillViewCompletely, false); typedArray.recycle(); if (fillViewCompletely) { checkViewFilledCompletely(); } } @Override public int getBleedX() { return bleedX; } public TrianglifyView setBleedX(int bleedX) { this.bleedX = bleedX; presenter.viewState = Presenter.ViewState.GRID_PARAMETERS_CHANGED; if (fillViewCompletely) { checkViewFilledCompletely(); } return this; } @Override public int getBleedY() { return bleedY; } public TrianglifyView setBleedY(int bleedY) { this.bleedY = bleedY; presenter.viewState = Presenter.ViewState.GRID_PARAMETERS_CHANGED; if (fillViewCompletely) { checkViewFilledCompletely(); } return this; } @Override public int getGridHeight() { return gridHeight; } public TrianglifyView setGridHeight(int gridHeight) { this.gridHeight = gridHeight; presenter.viewState = Presenter.ViewState.GRID_PARAMETERS_CHANGED; return this; } @Override public int getGridWidth() { return gridWidth; } public TrianglifyView setGridWidth(int gridWidth) { this.gridWidth = gridWidth; presenter.viewState = Presenter.ViewState.GRID_PARAMETERS_CHANGED; return this; } @Override public int getBitmapQuality() { return bitmapQuality; } public void setBitmapQuality(int bitmapQuality) { this.bitmapQuality = bitmapQuality; setDrawingCacheQuality(bitmapQuality); } @Override public int getTypeGrid() { return typeGrid; } public TrianglifyView setTypeGrid(int typeGrid) { this.typeGrid = typeGrid; presenter.viewState = Presenter.ViewState.GRID_PARAMETERS_CHANGED; return this; } @Override public int getVariance() { return variance; } public TrianglifyView setVariance(int variance) { this.variance = variance; presenter.viewState = Presenter.ViewState.GRID_PARAMETERS_CHANGED; return this; } @Override public int getCellSize() { return cellSize; } public TrianglifyView setCellSize(int cellSize) { this.cellSize = cellSize; presenter.viewState = Presenter.ViewState.GRID_PARAMETERS_CHANGED; if (fillViewCompletely) { checkViewFilledCompletely(); } return this; } public TrianglifyView setFillViewCompletely(boolean fillViewCompletely) { this.fillViewCompletely = fillViewCompletely; if (fillViewCompletely) { checkViewFilledCompletely(); } return this; } @Override public boolean isFillViewCompletely() { return fillViewCompletely; } @Override public boolean isFillTriangle() { return fillTriangle; } public TrianglifyView setFillTriangle(boolean fillTriangle) { this.fillTriangle = fillTriangle; if (presenter.viewState != Presenter.ViewState.GRID_PARAMETERS_CHANGED && presenter.viewState != Presenter.ViewState.COLOR_SCHEME_CHANGED) { presenter.viewState = Presenter.ViewState.PAINT_STYLE_CHANGED; } return this; } @Override public boolean isDrawStrokeEnabled() { return drawStroke; } public TrianglifyView setDrawStrokeEnabled(boolean drawStroke) { this.drawStroke = drawStroke; if (presenter.viewState != Presenter.ViewState.GRID_PARAMETERS_CHANGED && presenter.viewState != Presenter.ViewState.COLOR_SCHEME_CHANGED) { presenter.viewState = Presenter.ViewState.PAINT_STYLE_CHANGED; } return this; } @Override public boolean isRandomColoringEnabled() { return randomColoring; } public TrianglifyView setRandomColoring(boolean randomColoring) { this.randomColoring = randomColoring; if (presenter.viewState != Presenter.ViewState.GRID_PARAMETERS_CHANGED) { presenter.viewState = Presenter.ViewState.COLOR_SCHEME_CHANGED; } return this; } @Override public Palette getPalette() { return palette; } public TrianglifyView setPalette(Palette palette) { this.palette = palette; if (presenter.viewState != Presenter.ViewState.GRID_PARAMETERS_CHANGED) { presenter.viewState = Presenter.ViewState.COLOR_SCHEME_CHANGED; } return this; } @Override public Presenter.ViewState getViewState() { return presenter.viewState; } private TrianglifyView setTriangulation(Triangulation triangulation) { this.triangulation = triangulation; return this; } /** * This method clears the triangulation and sets the view state to NULL_TRIANGULATION. */ public void clearView() { presenter.clearSoup(); } /** * invalidateView method invalidates the view by setting * @param triangulation to the view instance triangulation and calling invalidate method. * Once invalidated, the viewState is changed to UNCHANGED_TRIANGULATION to denote no change in the triangulation * parameters after rendering the view. */ @Override public void invalidateView(Triangulation triangulation) { this.setTriangulation(triangulation); invalidate(); presenter.viewState = Presenter.ViewState.UNCHANGED_TRIANGULATION; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); gridHeight = getHeight(); gridWidth = getWidth(); if (triangulation != null) { plotOnCanvas(canvas); } else { generateAndInvalidate(); } } /** * smartUpdate method ensures the increase in performance by generating only the necessary changes in triangulation. * According to the value of viewState, it makes the necessary method call. */ public void smartUpdate() { presenter.updateView(); } /** * generateAndInvalidate method is called when the triangulation is to be generated from scratch. It sets the * GenerateOnlyColor boolean of presenter to false so that when generateSoupAndInvalidateView is * called, the grid and delaunay triangulation is regenerated according to the new parameters, followed by * colorization and plotting of the triangulation onto the view. */ public void generateAndInvalidate() { presenter.setGenerateOnlyColor(false); presenter.generateSoupAndInvalidateView(); } private void plotOnCanvas(Canvas canvas) { for (int i = 0; i < triangulation.getTriangleList().size(); i++) { drawTriangle(canvas, triangulation.getTriangleList().get(i)); } } /** * Draws triangle on the canvas object passed using the parameters of current view instance * @param canvas Canvas to paint on * @param triangle2D Triangle to draw on canvas */ private void drawTriangle(Canvas canvas, Triangle2D triangle2D) { Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); int color = triangle2D.getColor(); /* * Add 0xff000000 for alpha channel required by android.graphics.Color */ color += 0xff000000; paint.setColor(color); paint.setStrokeWidth(4); if (isFillTriangle() && isDrawStrokeEnabled()) { paint.setStyle(Paint.Style.FILL_AND_STROKE); } else if (isFillTriangle()) { paint.setStyle(Paint.Style.FILL); } else if (isDrawStrokeEnabled()) { paint.setStyle(Paint.Style.STROKE); } else { paint.setStyle(Paint.Style.FILL_AND_STROKE); } paint.setAntiAlias(false); Path path = new Path(); path.setFillType(Path.FillType.EVEN_ODD); path.moveTo(triangle2D.a.x - bleedX, triangle2D.a.y - bleedY); path.lineTo(triangle2D.b.x - bleedX, triangle2D.b.y - bleedY); path.lineTo(triangle2D.c.x - bleedX, triangle2D.c.y - bleedY); path.lineTo(triangle2D.a.x - bleedX, triangle2D.a.y - bleedY); path.close(); canvas.drawPath(path, paint); } /** * This method checks whether the view will be filled completely by testing if both bleedY and bleedX are * greater than cellSize. If not, then it throws an illegal argument exception. * * Explaination for Condition: * Bleed defines the dimensions of extra size that TrianglifyView view generates so that triangles * on the edge don't appear to be chopped off. In most of the cases min{bleedX, bleedY} > cellSize * would ensure that the view is completely filled. */ private void checkViewFilledCompletely() { if (bleedY <= cellSize || bleedX <= cellSize) { throw new IllegalArgumentException("bleedY and bleedX should be larger than cellSize for view to be completely filled."); } } public Bitmap getBitmap() { Bitmap resultBitmap = getDrawingCache().copy(Bitmap.Config.ARGB_8888, true); this.destroyDrawingCache(); return resultBitmap; } } ================================================ FILE: trianglify/src/main/java/com/sdsmdg/kd/trianglify/views/TrianglifyViewInterface.java ================================================ package com.sdsmdg.kd.trianglify.views; /** * Created by suyash on 18/3/17. */ import com.sdsmdg.kd.trianglify.models.Palette; import com.sdsmdg.kd.trianglify.models.Triangulation; import com.sdsmdg.kd.trianglify.presenters.Presenter; public interface TrianglifyViewInterface { int GRID_RECTANGLE = 0; int GRID_CIRCLE = 1; int getBleedX(); int getBleedY(); int getTypeGrid(); int getGridWidth(); int getGridHeight(); int getVariance(); int getCellSize(); int getBitmapQuality(); Presenter.ViewState getViewState(); boolean isFillViewCompletely(); boolean isFillTriangle(); boolean isDrawStrokeEnabled(); boolean isRandomColoringEnabled(); Palette getPalette(); void invalidateView(Triangulation triangulation); } ================================================ FILE: trianglify/src/main/res/values/attrs.xml ================================================ ================================================ FILE: trianglify/src/main/res/values/strings.xml ================================================ trianglify ================================================ FILE: trianglify/src/test/java/com/sdsmdg/kd/trianglify/ExampleUnitTest.java ================================================ package com.sdsmdg.kd.trianglify; import org.junit.Test; import static org.junit.Assert.*; /** * Example local unit test, which will execute on the development machine (host). * * @see Testing documentation */ public class ExampleUnitTest { @Test public void addition_isCorrect() throws Exception { assertEquals(4, 2 + 2); } }