├── app ├── .gitignore ├── src │ └── main │ │ ├── res │ │ ├── values │ │ │ ├── strings.xml │ │ │ ├── styles.xml │ │ │ └── dimens.xml │ │ ├── drawable-hdpi │ │ │ └── avatar.png │ │ ├── drawable-ldpi │ │ │ └── avatar.png │ │ ├── drawable-mdpi │ │ │ └── avatar.png │ │ ├── drawable-xhdpi │ │ │ └── avatar.png │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ │ └── dimens.xml │ │ └── layout │ │ │ └── activity_main.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── org │ │ └── giwi │ │ └── networkgraph │ │ └── MainActivity.java ├── proguard-rules.pro ├── build.gradle └── app.iml ├── network-graph ├── .gitignore ├── libs │ ├── Config.jar │ ├── Graphs.jar │ ├── Logging.jar │ └── log4j-1.2.16.jar ├── src │ └── main │ │ ├── res │ │ └── values │ │ │ ├── strings.xml │ │ │ └── attrs_graph_view.xml │ │ ├── AndroidManifest.xml │ │ └── java │ │ └── giwi │ │ └── org │ │ └── networkgraph │ │ ├── beans │ │ ├── Dimension.java │ │ ├── Vertex.java │ │ ├── Point2D.java │ │ ├── RandomLocationTransformer.java │ │ ├── NetworkGraph.java │ │ └── ArcUtils.java │ │ ├── layout │ │ ├── Layout.java │ │ ├── AbstractLayout.java │ │ └── FRLayout.java │ │ └── GraphSurfaceView.java ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── .gitignore ├── maven-metadata.xml ├── LICENSE.md ├── android-network-graph-0.0.1.pom ├── gradle.properties └── README.md /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /network-graph/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':network-graph' 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | local.properties 3 | .idea 4 | .DS_Store 5 | build 6 | /captures 7 | gradle* 8 | *.iml -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | NetworkGraph 3 | 4 | -------------------------------------------------------------------------------- /network-graph/libs/Config.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Giwi/android-network-graph/HEAD/network-graph/libs/Config.jar -------------------------------------------------------------------------------- /network-graph/libs/Graphs.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Giwi/android-network-graph/HEAD/network-graph/libs/Graphs.jar -------------------------------------------------------------------------------- /network-graph/libs/Logging.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Giwi/android-network-graph/HEAD/network-graph/libs/Logging.jar -------------------------------------------------------------------------------- /network-graph/libs/log4j-1.2.16.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Giwi/android-network-graph/HEAD/network-graph/libs/log4j-1.2.16.jar -------------------------------------------------------------------------------- /network-graph/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | NetworkGraph 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Giwi/android-network-graph/HEAD/app/src/main/res/drawable-hdpi/avatar.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-ldpi/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Giwi/android-network-graph/HEAD/app/src/main/res/drawable-ldpi/avatar.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Giwi/android-network-graph/HEAD/app/src/main/res/drawable-mdpi/avatar.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Giwi/android-network-graph/HEAD/app/src/main/res/drawable-xhdpi/avatar.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Giwi/android-network-graph/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Giwi/android-network-graph/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Giwi/android-network-graph/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Giwi/android-network-graph/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /network-graph/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /network-graph/src/main/res/values/attrs_graph_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /maven-metadata.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | org.giwi 4 | android-network-graph 5 | 0.0.1 6 | 7 | 0.0.1 8 | 0.0.1 9 | 10 | 0.0.1 11 | 12 | 20161220050558 13 | 14 | 15 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | License 2 | Copyright 2016 Giwi Softwares. 3 | Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in 4 | compliance with the License. You may obtain a copy of the License at 5 | 6 | [http://www.apache.org/licenses/LICENSE-2.0](http://www.apache.org/licenses/LICENSE-2.0) 7 | 8 | Unless required by applicable law or agreed to in writing, software distributed under the License is 9 | distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 10 | implied. See the License for the specific language governing permissions and limitations under the 11 | License. -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /opt/android-sdk-linux/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | repositories { 3 | jcenter() 4 | } 5 | android { 6 | compileSdkVersion 25 7 | buildToolsVersion '25.0.2' 8 | 9 | defaultConfig { 10 | applicationId "org.giwi.android.networkgraph" 11 | minSdkVersion 15 12 | targetSdkVersion 25 13 | versionCode 1 14 | versionName "1.0" 15 | } 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 20 | } 21 | } 22 | lintOptions { 23 | disable 'InvalidPackage' 24 | disable 'GoogleAppIndexingWarning' 25 | disable 'IconMissingDensityFolder' 26 | disable 'IconDipSize' 27 | abortOnError false 28 | } 29 | } 30 | 31 | dependencies { 32 | compile fileTree(include: ['*.jar'], dir: 'libs') 33 | compile project(path: ':network-graph') 34 | //compile 'org.giwi:android-network-graph:0.0.1' 35 | } -------------------------------------------------------------------------------- /android-network-graph-0.0.1.pom: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | org.giwi 7 | android-network-graph 8 | 0.0.1 9 | aar 10 | 11 | 12 | com.android.support 13 | appcompat-v7 14 | 25.1.0 15 | compile 16 | 17 | 18 | org.apache.commons 19 | commons-collections4 20 | 4.0 21 | compile 22 | 23 | 24 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | ## Project-wide Gradle settings. 2 | # 3 | # For more details on how to configure your build environment visit 4 | # http://www.gradle.org/docs/current/userguide/build_environment.html 5 | # 6 | # Specifies the JVM arguments used for the daemon process. 7 | # The setting is particularly useful for tweaking memory settings. 8 | # Default value: -Xmx1024m -XX:MaxPermSize=256m 9 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 10 | # 11 | # When configured, Gradle will run in incubating parallel mode. 12 | # This option should only be used with decoupled projects. More details, visit 13 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 14 | # org.gradle.parallel=true 15 | #Wed Dec 14 09:22:11 CET 2016 16 | systemProp.http.proxyPassword=zaza1611 17 | systemProp.http.proxyHost=proxy.gicm.net 18 | systemProp.http.nonProxyHosts=*.arkea.com, *.gicm.net 19 | systemProp.http.proxyUser=b3605 20 | systemProp.http.proxyPort=3128 21 | -------------------------------------------------------------------------------- /network-graph/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /opt/android-sdk-linux/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /network-graph/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | task generateSources (type: Jar) { 3 | classifier = 'sources' 4 | from android.sourceSets.main.java.srcDirs 5 | } 6 | 7 | artifacts { 8 | archives generateSources 9 | } 10 | android { 11 | compileSdkVersion 25 12 | buildToolsVersion "25.0.2" 13 | 14 | defaultConfig { 15 | minSdkVersion 15 16 | targetSdkVersion 25 17 | versionCode 1 18 | versionName "1.0" 19 | 20 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 21 | 22 | } 23 | buildTypes { 24 | release { 25 | minifyEnabled false 26 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 27 | } 28 | } 29 | lintOptions { 30 | disable 'InvalidPackage' 31 | abortOnError false 32 | } 33 | } 34 | 35 | dependencies { 36 | compile fileTree(dir: 'libs', include: ['*.jar']) 37 | compile 'com.android.support:appcompat-v7:25.1.0' 38 | compile 'org.apache.commons:commons-collections4:4.0' 39 | } -------------------------------------------------------------------------------- /network-graph/src/main/java/giwi/org/networkgraph/beans/Dimension.java: -------------------------------------------------------------------------------- 1 | package giwi.org.networkgraph.beans; 2 | 3 | /** 4 | * The type Dimension. 5 | */ 6 | public class Dimension { 7 | 8 | int width; 9 | 10 | int height; 11 | 12 | /** 13 | * Instantiates a new Dimension. 14 | * 15 | * @param w the w 16 | * @param h the h 17 | */ 18 | public Dimension(int w, int h) { 19 | width = w; 20 | height = h; 21 | } 22 | 23 | /** 24 | * Gets width. 25 | * 26 | * @return the width 27 | */ 28 | public int getWidth() { 29 | return width; 30 | } 31 | 32 | /** 33 | * Gets height. 34 | * 35 | * @return the height 36 | */ 37 | public int getHeight() { 38 | return height; 39 | } 40 | 41 | /** 42 | * Equals boolean. 43 | * 44 | * @param o the o 45 | * @return the boolean 46 | */ 47 | @Override 48 | public boolean equals(Object o) { 49 | if (o instanceof Dimension) { 50 | Dimension other = (Dimension) o; 51 | return (width == other.width) && (height == other.height); 52 | } 53 | 54 | return false; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 11 | 12 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /network-graph/src/main/java/giwi/org/networkgraph/beans/Vertex.java: -------------------------------------------------------------------------------- 1 | package giwi.org.networkgraph.beans; 2 | 3 | import net.xqhs.graphs.graph.Node; 4 | 5 | import android.graphics.drawable.Drawable; 6 | 7 | /** 8 | * The type Vertex. 9 | */ 10 | public class Vertex { 11 | 12 | private Node node; 13 | 14 | private Drawable icon; 15 | 16 | /** 17 | * Instantiates a new Vertex. 18 | * 19 | * @param node the node 20 | * @param icon the icon 21 | */ 22 | public Vertex(final Node node, final Drawable icon) { 23 | this.node = node; 24 | this.icon = icon; 25 | } 26 | 27 | /** 28 | * Gets node. 29 | * 30 | * @return the node 31 | */ 32 | public Node getNode() { 33 | return node; 34 | } 35 | 36 | /** 37 | * Sets node. 38 | * 39 | * @param node the node 40 | */ 41 | public void setNode(final Node node) { 42 | this.node = node; 43 | } 44 | 45 | /** 46 | * Gets icon. 47 | * 48 | * @return the icon 49 | */ 50 | public Drawable getIcon() { 51 | return icon; 52 | } 53 | 54 | /** 55 | * Sets icon. 56 | * 57 | * @param icon the icon 58 | */ 59 | public void setIcon(final Drawable icon) { 60 | this.icon = icon; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /network-graph/src/main/java/giwi/org/networkgraph/beans/Point2D.java: -------------------------------------------------------------------------------- 1 | package giwi.org.networkgraph.beans; 2 | 3 | /** 4 | * The type Point 2 d. 5 | */ 6 | public class Point2D { 7 | 8 | protected double x; 9 | 10 | protected double y; 11 | 12 | /** 13 | * Instantiates a new Point 2 d. 14 | * 15 | * @param x the x 16 | * @param y the y 17 | */ 18 | Point2D(double x, double y) { 19 | this.x = x; 20 | this.y = y; 21 | } 22 | 23 | /** 24 | * Instantiates a new Point 2 d. 25 | */ 26 | public Point2D() { 27 | this.x = 0; 28 | this.y = 0; 29 | } 30 | 31 | /** 32 | * Gets x. 33 | * 34 | * @return the x 35 | */ 36 | public double getX() { 37 | return this.x; 38 | } 39 | 40 | /** 41 | * Gets y. 42 | * 43 | * @return the y 44 | */ 45 | public double getY() { 46 | return this.y; 47 | } 48 | 49 | /** 50 | * Sets location. 51 | * 52 | * @param x the x 53 | * @param y the y 54 | */ 55 | public void setLocation(double x, double y) { 56 | this.x = x; 57 | this.y = y; 58 | } 59 | 60 | /** 61 | * Sets location. 62 | * 63 | * @param p the p 64 | */ 65 | public void setLocation(Point2D p) { 66 | this.x = p.x; 67 | this.y = p.y; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /network-graph/src/main/java/giwi/org/networkgraph/beans/RandomLocationTransformer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Created on Jul 19, 2005 3 | * 4 | * Copyright (c) 2005, the JUNG Project and the Regents of the University 5 | * of California 6 | * All rights reserved. 7 | * 8 | * This software is open-source under the BSD license; see either 9 | * "license.txt" or 10 | * http://jung.sourceforge.net/license.txt for a description. 11 | */ 12 | package giwi.org.networkgraph.beans; 13 | 14 | 15 | import org.apache.commons.collections4.Transformer; 16 | 17 | import java.util.Date; 18 | import java.util.Random; 19 | 20 | /** 21 | * Transforms the input type into a random location within 22 | * the bounds of the Dimension property. 23 | * This is used as the backing Transformer for the LazyMap 24 | * for many Layouts, 25 | * and provides a random location for unmapped vertices 26 | * the first time they are accessed. 27 | * 28 | * @param the type parameter 29 | * @author Tom Nelson 30 | */ 31 | public class RandomLocationTransformer implements Transformer { 32 | 33 | /** 34 | * The D. 35 | */ 36 | private Dimension d; 37 | 38 | /** 39 | * The Random. 40 | */ 41 | private Random random; 42 | 43 | /** 44 | * Creates an instance with the specified size which uses the current time 45 | * as the random seed. 46 | * 47 | * @param d the d 48 | */ 49 | public RandomLocationTransformer(Dimension d) { 50 | this(d, new Date().getTime()); 51 | } 52 | 53 | /** 54 | * Creates an instance with the specified dimension and random seed. 55 | * 56 | * @param d the d 57 | * @param seed the seed 58 | */ 59 | private RandomLocationTransformer(final Dimension d, long seed) { 60 | this.d = d; 61 | this.random = new Random(seed); 62 | } 63 | 64 | /** 65 | * Transform point 2 d. 66 | * 67 | * @param v the v 68 | * @return the point 2 d 69 | */ 70 | public Point2D transform(V v) { 71 | return new Point2D(random.nextDouble() * d.width, random.nextDouble() * d.height); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /network-graph/src/main/java/giwi/org/networkgraph/layout/Layout.java: -------------------------------------------------------------------------------- 1 | package giwi.org.networkgraph.layout; 2 | 3 | import net.xqhs.graphs.graph.Node; 4 | 5 | import org.apache.commons.collections4.Transformer; 6 | 7 | import giwi.org.networkgraph.beans.Dimension; 8 | import giwi.org.networkgraph.beans.NetworkGraph; 9 | import giwi.org.networkgraph.beans.Point2D; 10 | 11 | /** 12 | * The interface Layout. 13 | */ 14 | interface Layout extends Transformer { 15 | 16 | /** 17 | * Initialize void. 18 | */ 19 | void initialize(); 20 | 21 | /** 22 | * provides initial locations for all vertices. 23 | * 24 | * @param initializer the initializer 25 | */ 26 | void setInitializer(Transformer initializer); 27 | 28 | /** 29 | * setter for graph 30 | * 31 | * @param graph the graph 32 | */ 33 | void setGraph(NetworkGraph graph); 34 | 35 | /** 36 | * Returns the full graph (the one that was passed in at 37 | * construction time) that this Layout refers to. 38 | * 39 | * @return the graph 40 | */ 41 | NetworkGraph getGraph(); 42 | 43 | /** 44 | * Reset void. 45 | */ 46 | void reset(); 47 | 48 | /** 49 | * Sets size. 50 | * 51 | * @param d the d 52 | */ 53 | void setSize(Dimension d); 54 | 55 | /** 56 | * Returns the current size of the visualization's space. 57 | * 58 | * @return the size 59 | */ 60 | Dimension getSize(); 61 | 62 | 63 | /** 64 | * Sets a flag which fixes this vertex in place. 65 | * 66 | * @param v vertex 67 | * @param state the state 68 | */ 69 | void lock(Node v, boolean state); 70 | 71 | /** 72 | * Returns true if the position of vertex v 73 | * is locked. 74 | * 75 | * @param v the v 76 | * @return the boolean 77 | */ 78 | boolean isLocked(Node v); 79 | 80 | /** 81 | * set the location of a vertex 82 | * 83 | * @param v the v 84 | * @param location the location 85 | */ 86 | void setLocation(Node v, Point2D location); 87 | } 88 | -------------------------------------------------------------------------------- /app/src/main/java/org/giwi/networkgraph/MainActivity.java: -------------------------------------------------------------------------------- 1 | package org.giwi.networkgraph; 2 | 3 | import net.xqhs.graphs.graph.Node; 4 | import net.xqhs.graphs.graph.SimpleEdge; 5 | import net.xqhs.graphs.graph.SimpleNode; 6 | 7 | import android.os.Bundle; 8 | import android.support.v4.content.ContextCompat; 9 | import android.support.v7.app.AppCompatActivity; 10 | 11 | import giwi.org.networkgraph.GraphSurfaceView; 12 | import giwi.org.networkgraph.beans.NetworkGraph; 13 | import giwi.org.networkgraph.beans.Vertex; 14 | 15 | 16 | /** 17 | * The type Main activity. 18 | */ 19 | public class MainActivity extends AppCompatActivity { 20 | 21 | /** 22 | * On create. 23 | * 24 | * @param savedInstanceState the saved instance state 25 | */ 26 | @Override 27 | protected void onCreate(Bundle savedInstanceState) { 28 | super.onCreate(savedInstanceState); 29 | setContentView(R.layout.activity_main); 30 | NetworkGraph graph = new NetworkGraph(); 31 | 32 | Node v1 = new SimpleNode("18"); 33 | Node v2 = new SimpleNode("24"); 34 | graph.getVertex().add(new Vertex(v1, ContextCompat.getDrawable(this, R.drawable.avatar))); 35 | graph.getVertex().add(new Vertex(v2, ContextCompat.getDrawable(this, R.drawable.avatar))); 36 | graph.addEdge(new SimpleEdge(v1, v2, "12")); 37 | 38 | Node v3 = new SimpleNode("7"); 39 | graph.getVertex().add(new Vertex(v3, ContextCompat.getDrawable(this, R.drawable.avatar))); 40 | graph.addEdge(new SimpleEdge(v2, v3, "23")); 41 | 42 | v1 = new SimpleNode("14"); 43 | graph.getVertex().add(new Vertex(v1, ContextCompat.getDrawable(this, R.drawable.avatar))); 44 | graph.addEdge(new SimpleEdge(v3, v1, "34")); 45 | 46 | v1 = new SimpleNode("10"); 47 | graph.getVertex().add(new Vertex(v1, ContextCompat.getDrawable(this, R.drawable.avatar))); 48 | graph.addEdge(new SimpleEdge(v3, v1, "35")); 49 | 50 | v1 = new SimpleNode("11"); 51 | graph.getVertex().add(new Vertex(v1, ContextCompat.getDrawable(this, R.drawable.avatar))); 52 | graph.addEdge(new SimpleEdge(v1, v3, "36")); 53 | graph.addEdge(new SimpleEdge(v3, v1, "6")); 54 | 55 | GraphSurfaceView surface = (GraphSurfaceView) findViewById(R.id.mysurface); 56 | surface.init(graph); 57 | 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /network-graph/src/main/java/giwi/org/networkgraph/beans/NetworkGraph.java: -------------------------------------------------------------------------------- 1 | package giwi.org.networkgraph.beans; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * The type Network graph. 8 | */ 9 | public class NetworkGraph extends net.xqhs.graphs.graph.SimpleGraph { 10 | 11 | private List vertex = new ArrayList<>(); 12 | 13 | private int defaultColor = android.R.color.black; 14 | 15 | private int edgeColor = android.R.color.holo_blue_light; 16 | 17 | private int nodeColor = android.R.color.holo_blue_light; 18 | 19 | private int nodeBgColor = android.R.color.white; 20 | 21 | /** 22 | * Gets vertex. 23 | * 24 | * @return the vertex 25 | */ 26 | public List getVertex() { 27 | return vertex; 28 | } 29 | 30 | /** 31 | * Sets vertex. 32 | * 33 | * @param vertex the vertex 34 | */ 35 | public void setVertex(final List vertex) { 36 | this.vertex = vertex; 37 | } 38 | 39 | /** 40 | * Gets default color. 41 | * 42 | * @return the default color 43 | */ 44 | public int getDefaultColor() { 45 | return defaultColor; 46 | } 47 | 48 | /** 49 | * Sets default color. 50 | * 51 | * @param defaultColor the default color 52 | */ 53 | public void setDefaultColor(final int defaultColor) { 54 | this.defaultColor = defaultColor; 55 | } 56 | 57 | /** 58 | * Gets edge color. 59 | * 60 | * @return the edge color 61 | */ 62 | public int getEdgeColor() { 63 | return edgeColor; 64 | } 65 | 66 | /** 67 | * Sets edge color. 68 | * 69 | * @param edgeColor the edge color 70 | */ 71 | public void setEdgeColor(final int edgeColor) { 72 | this.edgeColor = edgeColor; 73 | } 74 | 75 | /** 76 | * Gets node color. 77 | * 78 | * @return the node color 79 | */ 80 | public int getNodeColor() { 81 | return nodeColor; 82 | } 83 | 84 | /** 85 | * Sets node color. 86 | * 87 | * @param nodeColor the node color 88 | */ 89 | public void setNodeColor(final int nodeColor) { 90 | this.nodeColor = nodeColor; 91 | } 92 | 93 | /** 94 | * Gets node bg color. 95 | * 96 | * @return the node bg color 97 | */ 98 | public int getNodeBgColor() { 99 | return nodeBgColor; 100 | } 101 | 102 | /** 103 | * Sets node bg color. 104 | * 105 | * @param nodeBgColor the node bg color 106 | */ 107 | public void setNodeBgColor(final int nodeBgColor) { 108 | this.nodeBgColor = nodeBgColor; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # android-network-graph 2 | 3 | ## Description 4 | 5 | Network graph based on : 6 | 7 | - [https://github.com/andreiolaru-ro/AmIciTy-Grph](https://github.com/andreiolaru-ro/AmIciTy-Grph) 8 | - [https://github.com/andreiolaru-ro/net.xqhs.Graphs](https://github.com/andreiolaru-ro/net.xqhs.Graphs) 9 | 10 | [ ![Download](https://api.bintray.com/packages/giwi/android/android-network-graph/images/download.svg?version=0.0.1) ](https://bintray.com/giwi/android/android-network-graph/0.0.1/link) 11 | 12 | Here's a screenshot : 13 | 14 | ![screen](http://i.imgur.com/kLq1mQ6.png) 15 | 16 | ## Usage 17 | 18 | ````groovy 19 | repositories { 20 | jcenter() 21 | } 22 | dependencies { 23 | compile 'org.giwi:android-network-graph:0.0.1' 24 | } 25 | ```` 26 | 27 | ````xml 28 | 39 | ```` 40 | 41 | ````java 42 | Node v1 = new SimpleNode("18"); 43 | Node v2 = new SimpleNode("24"); 44 | graph.getVertex().add(new Vertex(v1, ContextCompat.getDrawable(this, R.drawable.avatar))); 45 | graph.getVertex().add(new Vertex(v2, ContextCompat.getDrawable(this, R.drawable.avatar))); 46 | graph.addEdge(new SimpleEdge(v1, v2, "12")); 47 | 48 | Node v3 = new SimpleNode("7"); 49 | graph.getVertex().add(new Vertex(v3, ContextCompat.getDrawable(this, R.drawable.avatar))); 50 | graph.addEdge(new SimpleEdge(v2, v3, "23")); 51 | 52 | v1 = new SimpleNode("14"); 53 | graph.getVertex().add(new Vertex(v1, ContextCompat.getDrawable(this, R.drawable.avatar))); 54 | graph.addEdge(new SimpleEdge(v3, v1, "34")); 55 | 56 | v1 = new SimpleNode("10"); 57 | graph.getVertex().add(new Vertex(v1, ContextCompat.getDrawable(this, R.drawable.avatar))); 58 | graph.addEdge(new SimpleEdge(v3, v1, "35")); 59 | 60 | v1 = new SimpleNode("11"); 61 | graph.getVertex().add(new Vertex(v1, ContextCompat.getDrawable(this, R.drawable.avatar))); 62 | graph.addEdge(new SimpleEdge(v1, v3, "36")); 63 | graph.addEdge(new SimpleEdge(v3, v1, "6")); 64 | 65 | GraphSurfaceView surface = (GraphSurfaceView) findViewById(R.id.mysurface); 66 | surface.init(graph); 67 | ```` 68 | 69 | Setting colors programmatically 70 | 71 | ````java 72 | graph.setDefaultColor(ContextCompat.getColor(this, android.R.color.black)); 73 | graph.setEdgeColor(ContextCompat.getColor(this, android.R.color.holo_blue_light)); 74 | graph.setNodeColor(ContextCompat.getColor(this, android.R.color.holo_blue_light)); 75 | graph.setNodeBgColor(ContextCompat.getColor(this, android.R.color.white)); 76 | ```` 77 | 78 | -------------------------------------------------------------------------------- /network-graph/src/main/java/giwi/org/networkgraph/beans/ArcUtils.java: -------------------------------------------------------------------------------- 1 | package giwi.org.networkgraph.beans; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Paint; 5 | import android.graphics.Path; 6 | import android.graphics.PointF; 7 | import android.graphics.RectF; 8 | 9 | 10 | /** 11 | * The type Arc utils. 12 | */ 13 | public class ArcUtils { 14 | 15 | /** 16 | * Instantiates a new Arc utils. 17 | */ 18 | private ArcUtils() { 19 | } 20 | 21 | /** 22 | * https://www.tbray.org/ongoing/When/200x/2009/01/02/Android-Draw-a-Curved-Line 23 | *

24 | * Draw arc. 25 | * 26 | * @param e1 the e 1 27 | * @param e2 the e 2 28 | * @param radius the radius 29 | * @param canvas the canvas 30 | * @param paint the paint 31 | * @param textPaint the text paint 32 | * @param recPaint the rec paint 33 | * @param value the value 34 | */ 35 | public static void drawArc(PointF e1, PointF e2, float radius, Canvas canvas, Paint paint, Paint textPaint, Paint recPaint, int value) { 36 | double a1 = Math.toRadians(radius + 5); 37 | // l1 is half the length of the line from e1 to e2 38 | double dx = e2.x - e1.x, dy = e2.y - e1.y; 39 | double l = Math.sqrt((dx * dx) + (dy * dy)); 40 | double l1 = l / 2.0; 41 | // h is length of the line from the middle of the connecting line to the center of the circle. 42 | double h = l1 / (Math.tan(a1 / 2.0)); 43 | // r is the radius of the circle 44 | double r = l1 / (Math.sin(a1 / 2.0)); 45 | // a2 is the angle at which L intersects the x axis 46 | double a2 = Math.atan2(dy, dx); 47 | // a3 is the angle at which H intersects the x axis 48 | double a3 = (Math.PI / 2.0) - a2; 49 | // m is the midpoint of the line from e1 to e2 50 | double mX = (e1.x + e2.x) / 2.0; 51 | double mY = (e1.y + e2.y) / 2.0; 52 | 53 | // c is the the center of the circle 54 | double cY = mY + (h * Math.sin(a3)); 55 | double cX = mX - (h * Math.cos(a3)); 56 | // rect is the square RectF that bounds the "oval" 57 | RectF oval = new RectF((float) (cX - r), (float) (cY - r), (float) (cX + r), (float) (cY + r)); 58 | 59 | // a4 is the starting sweep angle 60 | double rawA4 = Math.atan2(e1.y - cY, e1.x - cX); 61 | float a4 = (float) Math.toDegrees(rawA4); 62 | paint.setStrokeWidth(value + 1); 63 | drawArrow(e2.x, e2.y, a4 + radius + 45f, paint, canvas); 64 | canvas.drawArc(oval, a4, radius, false, paint); 65 | double deltay = -Math.sin(a3) * (r - h); 66 | double deltax = Math.cos(a3) * (r - h); 67 | canvas.drawRect( 68 | (float) (mX + deltax) - 20f, 69 | (float) (mY + deltay) + 20f, 70 | (float) (mX + deltax) + 20f, (float) (mY + deltay) - 20f, recPaint); 71 | 72 | canvas.drawText(String.valueOf(value), (float) (mX + deltax), 73 | (float) (mY + deltay) + 10, textPaint); 74 | } 75 | 76 | /** 77 | * Draw arrow. 78 | * 79 | * @param x the x 80 | * @param y the y 81 | * @param degrees the degrees 82 | * @param paint the paint 83 | * @param canvas the canvas 84 | */ 85 | private static void drawArrow(float x, float y, float degrees, Paint paint, Canvas canvas) { 86 | canvas.save(); 87 | canvas.rotate(degrees, x, y); 88 | Path path = new Path(); 89 | path.setFillType(Path.FillType.EVEN_ODD); 90 | path.moveTo(x - 40f, y - 40f); 91 | path.lineTo(x - 60f, y - 40f); 92 | path.lineTo(x - 40f, y - 60f); 93 | path.lineTo(x - 40f, y - 40f); 94 | path.close(); 95 | canvas.drawPath(path, paint); 96 | canvas.restore(); 97 | paint.setStyle(Paint.Style.STROKE); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /app/app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 8 | 9 | 10 | 11 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /network-graph/src/main/java/giwi/org/networkgraph/GraphSurfaceView.java: -------------------------------------------------------------------------------- 1 | package giwi.org.networkgraph; 2 | 3 | import net.xqhs.graphs.graph.Edge; 4 | 5 | import android.content.Context; 6 | import android.content.res.TypedArray; 7 | import android.graphics.Bitmap; 8 | import android.graphics.Canvas; 9 | import android.graphics.Color; 10 | import android.graphics.Paint; 11 | import android.graphics.PixelFormat; 12 | import android.graphics.PointF; 13 | import android.graphics.PorterDuff; 14 | import android.graphics.PorterDuffXfermode; 15 | import android.graphics.Rect; 16 | import android.graphics.drawable.BitmapDrawable; 17 | import android.support.annotation.NonNull; 18 | import android.util.AttributeSet; 19 | import android.view.MotionEvent; 20 | import android.view.ScaleGestureDetector; 21 | import android.view.SurfaceHolder; 22 | import android.view.SurfaceView; 23 | 24 | import giwi.org.networkgraph.beans.ArcUtils; 25 | import giwi.org.networkgraph.beans.Dimension; 26 | import giwi.org.networkgraph.beans.NetworkGraph; 27 | import giwi.org.networkgraph.beans.Point2D; 28 | import giwi.org.networkgraph.beans.Vertex; 29 | import giwi.org.networkgraph.layout.FRLayout; 30 | 31 | /** 32 | * The type NetworkGraph surface view. 33 | */ 34 | public class GraphSurfaceView extends SurfaceView { 35 | 36 | private ScaleGestureDetector mScaleDetector; 37 | 38 | private TypedArray attributes; 39 | 40 | private float mScaleFactor = 1.f; 41 | 42 | /** 43 | * Instantiates a new NetworkGraph surface view. 44 | * 45 | * @param context the context 46 | */ 47 | public GraphSurfaceView(Context context) { 48 | super(context); 49 | mScaleDetector = new ScaleGestureDetector(context, new ScaleListener()); 50 | } 51 | 52 | /** 53 | * Instantiates a new Graph surface view. 54 | * 55 | * @param context the context 56 | * @param attrs the attrs 57 | */ 58 | public GraphSurfaceView(Context context, AttributeSet attrs) { 59 | super(context, attrs); 60 | attributes = getContext().obtainStyledAttributes(attrs, R.styleable.GraphSurfaceView); 61 | mScaleDetector = new ScaleGestureDetector(context, new ScaleListener()); 62 | } 63 | 64 | /** 65 | * Instantiates a new Graph surface view. 66 | * 67 | * @param context the context 68 | * @param attrs the attrs 69 | * @param defStyle the def style 70 | */ 71 | public GraphSurfaceView(Context context, AttributeSet attrs, int defStyle) { 72 | super(context, attrs, defStyle); 73 | attributes = getContext().obtainStyledAttributes(attrs, R.styleable.GraphSurfaceView); 74 | mScaleDetector = new ScaleGestureDetector(context, new ScaleListener()); 75 | } 76 | 77 | 78 | /** 79 | * Init. 80 | * 81 | * @param graph the graph 82 | */ 83 | public void init(final NetworkGraph graph) { 84 | setZOrderOnTop(true); 85 | getHolder().setFormat(PixelFormat.TRANSLUCENT); 86 | getHolder().addCallback(new SurfaceHolder.Callback() { 87 | @Override 88 | public void surfaceCreated(SurfaceHolder holder) { 89 | Canvas canvas = holder.lockCanvas(null); 90 | canvas.drawARGB(0, 225, 225, 255); 91 | drawGraph(canvas, graph); 92 | holder.unlockCanvasAndPost(canvas); 93 | } 94 | 95 | @Override 96 | public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 97 | // TODO Auto-generated method stub 98 | 99 | } 100 | 101 | @Override 102 | public void surfaceDestroyed(SurfaceHolder holder) { 103 | // TODO Auto-generated method stub 104 | 105 | } 106 | }); 107 | } 108 | 109 | private void drawGraph(final Canvas canvas, final NetworkGraph graph) { 110 | Paint paint = new Paint(); 111 | Paint whitePaint = new Paint(); 112 | paint.setAntiAlias(true); 113 | FRLayout layout = new FRLayout(graph, new Dimension(getWidth(), getHeight())); 114 | whitePaint.setColor(attributes.getColor(R.styleable.GraphSurfaceView_nodeBgColor, graph.getNodeBgColor())); 115 | whitePaint.setStyle(Paint.Style.FILL_AND_STROKE); 116 | whitePaint.setStrokeWidth(2f); 117 | whitePaint.setShadowLayer(5, 0, 0, attributes.getColor(R.styleable.GraphSurfaceView_defaultColor, graph 118 | .getDefaultColor())); 119 | paint.setTextAlign(Paint.Align.CENTER); 120 | paint.setTextSize(20f); 121 | paint.setColor(attributes.getColor(R.styleable.GraphSurfaceView_defaultColor, graph.getDefaultColor())); 122 | for (Edge edge : graph.getEdges()) { 123 | Point2D p1 = layout.transform(edge.getFrom()); 124 | Point2D p2 = layout.transform(edge.getTo()); 125 | paint.setStrokeWidth(Float.valueOf(edge.getLabel()) + 1f); 126 | paint.setColor(attributes.getColor(R.styleable.GraphSurfaceView_edgeColor, graph.getEdgeColor())); 127 | Paint curve = new Paint(); 128 | curve.setAntiAlias(true); 129 | curve.setStyle(Paint.Style.STROKE); 130 | curve.setStrokeWidth(2); 131 | curve.setColor(attributes.getColor(R.styleable.GraphSurfaceView_edgeColor, graph.getEdgeColor())); 132 | PointF e1 = new PointF((float) p1.getX(), (float) p1.getY()); 133 | PointF e2 = new PointF((float) p2.getX(), (float) p2.getY()); 134 | ArcUtils.drawArc(e1, e2, 36f, canvas, curve, paint, whitePaint, Integer.parseInt(edge.getLabel())); 135 | } 136 | paint.setStyle(Paint.Style.FILL); 137 | paint.setTextAlign(Paint.Align.CENTER); 138 | paint.setTextSize(30f); 139 | paint.setStrokeWidth(0f); 140 | paint.setColor(attributes.getColor(R.styleable.GraphSurfaceView_nodeColor, graph.getNodeColor())); 141 | for (Vertex node : graph.getVertex()) { 142 | Point2D position = layout.transform(node.getNode()); 143 | canvas.drawCircle((float) position.getX(), (float) position.getY(), 40, whitePaint); 144 | if (node.getIcon() != null) { 145 | Bitmap b = ((BitmapDrawable) node.getIcon()).getBitmap(); 146 | Bitmap bitmap = b.copy(Bitmap.Config.ARGB_8888, true); 147 | Bitmap roundBitmap = getCroppedBitmap(bitmap, 75); 148 | canvas.drawBitmap(roundBitmap, 149 | (float) position.getX() - 38f, (float) position.getY() - 38f, null); 150 | } 151 | canvas.drawRect( 152 | (float) position.getX() - 20, 153 | (float) position.getY() + 50, 154 | (float) position.getX() + 20, (float) position.getY() + 10, whitePaint); 155 | canvas.drawText(node.getNode().getLabel(), (float) position.getX(), 156 | (float) position.getY() + 40, paint); 157 | } 158 | } 159 | 160 | private Bitmap getCroppedBitmap(Bitmap bmp, int radius) { 161 | Bitmap sbmp; 162 | if (bmp.getWidth() != radius || bmp.getHeight() != radius) { 163 | sbmp = Bitmap.createScaledBitmap(bmp, radius, radius, false); 164 | } else { 165 | sbmp = bmp; 166 | } 167 | Bitmap output = Bitmap.createBitmap(sbmp.getWidth(), sbmp.getHeight(), Bitmap.Config.ARGB_8888); 168 | Canvas canvas = new Canvas(output); 169 | final Paint paint = new Paint(); 170 | final Rect rect = new Rect(0, 0, sbmp.getWidth(), sbmp.getHeight()); 171 | paint.setColor(Color.BLACK); 172 | paint.setAntiAlias(true); 173 | paint.setFilterBitmap(true); 174 | paint.setDither(true); 175 | canvas.drawARGB(0, 0, 0, 0); 176 | canvas.drawCircle( 177 | sbmp.getWidth() / 2 + 0.7f, sbmp.getHeight() / 2 + 0.7f, sbmp.getWidth() / 2 + 0.1f, paint); 178 | paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); 179 | canvas.drawBitmap(sbmp, rect, rect, paint); 180 | return output; 181 | } 182 | 183 | /** 184 | * On touch event. 185 | * 186 | * @param ev the ev 187 | * @return the boolean 188 | */ 189 | @Override 190 | public boolean onTouchEvent(@NonNull MotionEvent ev) { 191 | mScaleDetector.onTouchEvent(ev); 192 | return true; 193 | } 194 | 195 | /** 196 | * Gets scale factor. 197 | * 198 | * @return the scale factor 199 | */ 200 | public float getScaleFactor() { 201 | return mScaleFactor; 202 | } 203 | 204 | /** 205 | * The type Scale listener. 206 | */ 207 | private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener { 208 | 209 | /** 210 | * On scale. 211 | * 212 | * @param detector the detector 213 | * @return the boolean 214 | */ 215 | @Override 216 | public boolean onScale(ScaleGestureDetector detector) { 217 | mScaleFactor *= detector.getScaleFactor(); 218 | mScaleFactor = Math.max(0.1f, Math.min(mScaleFactor, 5.0f)); 219 | invalidate(); 220 | return true; 221 | } 222 | } 223 | 224 | } 225 | -------------------------------------------------------------------------------- /network-graph/src/main/java/giwi/org/networkgraph/layout/AbstractLayout.java: -------------------------------------------------------------------------------- 1 | package giwi.org.networkgraph.layout; 2 | 3 | import net.xqhs.graphs.graph.Node; 4 | 5 | import org.apache.commons.collections4.Transformer; 6 | import org.apache.commons.collections4.functors.ChainedTransformer; 7 | import org.apache.commons.collections4.map.LazyMap; 8 | 9 | import android.util.Log; 10 | 11 | import java.util.ConcurrentModificationException; 12 | import java.util.HashMap; 13 | import java.util.HashSet; 14 | import java.util.Map; 15 | import java.util.Set; 16 | 17 | import giwi.org.networkgraph.beans.Dimension; 18 | import giwi.org.networkgraph.beans.NetworkGraph; 19 | import giwi.org.networkgraph.beans.Point2D; 20 | 21 | /** 22 | * The type Abstract layout. 23 | */ 24 | abstract class AbstractLayout implements Layout { 25 | 26 | private Set dontmove = new HashSet<>(); 27 | 28 | private Dimension size; 29 | 30 | private NetworkGraph graph; 31 | 32 | boolean initialized; 33 | 34 | private Map locations 35 | = LazyMap.lazyMap(new HashMap(), new Transformer() { 36 | public Point2D transform(Node arg0) { 37 | return new Point2D(); 38 | } 39 | }); 40 | 41 | /** 42 | * Creates an instance which does not initialize the vertex locations. 43 | * 44 | * @param graph the graph for which the layout algorithm is to be created. 45 | */ 46 | AbstractLayout(NetworkGraph graph) { 47 | if (graph == null) { 48 | throw new IllegalArgumentException("NetworkGraph must be non-null"); 49 | } 50 | this.graph = graph; 51 | } 52 | 53 | /** 54 | * Instantiates a new Abstract layout. 55 | * 56 | * @param graph the graph 57 | * @param initializer the initializer 58 | */ 59 | @SuppressWarnings("unchecked") 60 | protected AbstractLayout(NetworkGraph graph, Transformer initializer) { 61 | this.graph = graph; 62 | Transformer chain 63 | = ChainedTransformer.chainedTransformer(new Transformer[]{initializer}); 64 | this.locations 65 | = LazyMap.lazyMap(new HashMap(), (Transformer) chain); 66 | initialized = true; 67 | } 68 | 69 | /** 70 | * Instantiates a new Abstract layout. 71 | * 72 | * @param graph the graph 73 | * @param size the size 74 | */ 75 | protected AbstractLayout(NetworkGraph graph, Dimension size) { 76 | this.graph = graph; 77 | this.size = size; 78 | } 79 | 80 | /** 81 | * Instantiates a new Abstract layout. 82 | * 83 | * @param graph the graph 84 | * @param initializer the initializer 85 | * @param size the size 86 | */ 87 | AbstractLayout(NetworkGraph graph, Transformer initializer, Dimension size) { 88 | this.graph = graph; 89 | this.locations = LazyMap.lazyMap(new HashMap(), initializer); 90 | this.size = size; 91 | } 92 | 93 | /** 94 | * Sets graph. 95 | * 96 | * @param graph the graph 97 | */ 98 | public void setGraph(NetworkGraph graph) { 99 | this.graph = graph; 100 | if (size != null && graph != null) { 101 | initialize(); 102 | } 103 | } 104 | 105 | /** 106 | * When a visualization is resized, it presumably wants to fix the 107 | * locations of the vertices and possibly to reinitialize its data. The 108 | * current method calls initializeLocations followed by initialize_local. 109 | * 110 | * @param size the size 111 | */ 112 | public void setSize(Dimension size) { 113 | if (size != null && graph != null) { 114 | Dimension oldSize = this.size; 115 | this.size = size; 116 | initialize(); 117 | if (oldSize != null) { 118 | adjustLocations(oldSize, size); 119 | } 120 | } 121 | } 122 | 123 | /** 124 | * Adjust locations. 125 | * 126 | * @param oldSize the old size 127 | * @param size the size 128 | */ 129 | private void adjustLocations(Dimension oldSize, Dimension size) { 130 | int xOffset = (size.getWidth() - oldSize.getWidth()) / 2; 131 | int yOffset = (size.getHeight() - oldSize.getHeight()) / 2; 132 | // now, move each vertex to be at the new screen center 133 | while (true) { 134 | try { 135 | for (Node v : getGraph().getNodes()) { 136 | offsetVertex(v, xOffset, yOffset); 137 | } 138 | break; 139 | } catch (ConcurrentModificationException cme) { 140 | Log.e(AbstractLayout.class.getName(), cme.getMessage()); 141 | } 142 | } 143 | } 144 | 145 | /** 146 | * Is locked. 147 | * 148 | * @param v the v 149 | * @return the boolean 150 | */ 151 | public boolean isLocked(Node v) { 152 | return dontmove.contains(v); 153 | } 154 | 155 | /** 156 | * Sets initializer. 157 | * 158 | * @param initializer the initializer 159 | */ 160 | public void setInitializer(Transformer initializer) { 161 | if (this.equals(initializer)) { 162 | throw new IllegalArgumentException("Layout cannot be initialized with itself"); 163 | } 164 | this.locations = LazyMap.lazyMap(new HashMap(), initializer); 165 | initialized = true; 166 | } 167 | 168 | /** 169 | * Returns the current size of the visualization space, according to the 170 | * last call to resize(). 171 | * 172 | * @return the current size of the screen 173 | */ 174 | public Dimension getSize() { 175 | return size; 176 | } 177 | 178 | /** 179 | * Returns the Coordinates object that stores the vertex' x and y location. 180 | * 181 | * @param v A Vertex that is a part of the NetworkGraph being visualized. 182 | * @return A Coordinates object with x and y locations. 183 | */ 184 | private Point2D getCoordinates(Node v) { 185 | return locations.get(v); 186 | } 187 | 188 | /** 189 | * Transform point 2 d. 190 | * 191 | * @param v the v 192 | * @return the point 2 d 193 | */ 194 | public Point2D transform(Node v) { 195 | return getCoordinates(v); 196 | } 197 | 198 | /** 199 | * Returns the x coordinate of the vertex from the Coordinates object. 200 | * in most cases you will be better off calling transform(v). 201 | * 202 | * @param v the v 203 | * @return the x 204 | */ 205 | public double getX(Node v) { 206 | assert getCoordinates(v) != null : "Cannot getX for an unmapped vertex " + v; 207 | return getCoordinates(v).getX(); 208 | } 209 | 210 | /** 211 | * Returns the y coordinate of the vertex from the Coordinates object. 212 | * In most cases you will be better off calling transform(v). 213 | * 214 | * @param v the v 215 | * @return the y 216 | */ 217 | public double getY(Node v) { 218 | assert getCoordinates(v) != null : "Cannot getY for an unmapped vertex " + v; 219 | return getCoordinates(v).getY(); 220 | } 221 | 222 | /** 223 | * Offset vertex. 224 | * 225 | * @param v the v 226 | * @param xOffset the x offset 227 | * @param yOffset the y offset 228 | */ 229 | private void offsetVertex(Node v, double xOffset, double yOffset) { 230 | Point2D c = getCoordinates(v); 231 | c.setLocation(c.getX() + xOffset, c.getY() + yOffset); 232 | setLocation(v, c); 233 | } 234 | 235 | /** 236 | * Accessor for the graph that represets all vertices. 237 | * 238 | * @return the graph that contains all vertices. 239 | */ 240 | public NetworkGraph getGraph() { 241 | return graph; 242 | } 243 | 244 | /** 245 | * Forcibly moves a vertex to the (x,y) location by setting its x and y 246 | * locations to the inputted location. Does not add the vertex to the 247 | * "dontmove" list, and (in the default implementation) does not make any 248 | * adjustments to the rest of the graph. 249 | * 250 | * @param picked the picked 251 | * @param x the x 252 | * @param y the y 253 | */ 254 | public void setLocation(Node picked, double x, double y) { 255 | Point2D coord = getCoordinates(picked); 256 | coord.setLocation(x, y); 257 | } 258 | 259 | /** 260 | * Sets location. 261 | * 262 | * @param picked the picked 263 | * @param p the p 264 | */ 265 | public void setLocation(Node picked, Point2D p) { 266 | Point2D coord = getCoordinates(picked); 267 | coord.setLocation(p); 268 | } 269 | 270 | /** 271 | * Locks {@code v} in place if {@code state} is {@code true}, otherwise unlocks it. 272 | * 273 | * @param v the v 274 | * @param state the state 275 | */ 276 | public void lock(Node v, boolean state) { 277 | if (state) { 278 | dontmove.add(v); 279 | } else { 280 | dontmove.remove(v); 281 | } 282 | } 283 | 284 | /** 285 | * Locks all vertices in place if {@code lock} is {@code true}, otherwise unlocks all vertices. 286 | * 287 | * @param lock the lock 288 | */ 289 | public void lock(boolean lock) { 290 | for (Node v : graph.getNodes()) { 291 | lock(v, lock); 292 | } 293 | } 294 | } 295 | -------------------------------------------------------------------------------- /network-graph/src/main/java/giwi/org/networkgraph/layout/FRLayout.java: -------------------------------------------------------------------------------- 1 | package giwi.org.networkgraph.layout; 2 | 3 | import net.xqhs.graphs.graph.Edge; 4 | import net.xqhs.graphs.graph.Node; 5 | 6 | import org.apache.commons.collections4.Factory; 7 | import org.apache.commons.collections4.map.LazyMap; 8 | 9 | import android.util.Log; 10 | 11 | import java.util.ConcurrentModificationException; 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | 15 | import giwi.org.networkgraph.beans.Dimension; 16 | import giwi.org.networkgraph.beans.NetworkGraph; 17 | import giwi.org.networkgraph.beans.Point2D; 18 | import giwi.org.networkgraph.beans.RandomLocationTransformer; 19 | 20 | /** 21 | * The type FR layout. 22 | */ 23 | public class FRLayout extends AbstractLayout { 24 | 25 | private double temperature; 26 | 27 | private int currentIteration; 28 | 29 | private int mMaxIterations = 700; 30 | 31 | private Map frVertexData = LazyMap.lazyMap(new HashMap(), new Factory() { 32 | public FRVertexData create() { 33 | return new FRVertexData(); 34 | } 35 | }); 36 | 37 | private double attraction_multiplier = 0.75; 38 | 39 | private double attraction_constant; 40 | 41 | private double repulsion_multiplier = 0.75; 42 | 43 | private double repulsion_constant; 44 | 45 | private double max_dimension; 46 | 47 | /** 48 | * Creates an instance for the specified graph. 49 | * 50 | * @param g the g 51 | */ 52 | public FRLayout(NetworkGraph g) { 53 | super(g); 54 | } 55 | 56 | /** 57 | * Creates an instance of size {@code d} for the specified graph. 58 | * 59 | * @param g the g 60 | * @param d the d 61 | */ 62 | public FRLayout(NetworkGraph g, Dimension d) { 63 | super(g, new RandomLocationTransformer(d), d); 64 | initialize(); 65 | max_dimension = Math.max(d.getHeight(), d.getWidth()); 66 | } 67 | 68 | /** 69 | * Sets size. 70 | * 71 | * @param size the size 72 | */ 73 | @Override 74 | public void setSize(Dimension size) { 75 | if (!initialized) { 76 | setInitializer(new RandomLocationTransformer(size)); 77 | } 78 | super.setSize(size); 79 | max_dimension = Math.max(size.getHeight(), size.getWidth()); 80 | } 81 | 82 | /** 83 | * Sets the attraction multiplier. 84 | * 85 | * @param attraction the attraction 86 | */ 87 | public void setAttractionMultiplier(double attraction) { 88 | this.attraction_multiplier = attraction; 89 | } 90 | 91 | /** 92 | * Sets the repulsion multiplier. 93 | * 94 | * @param repulsion the repulsion 95 | */ 96 | public void setRepulsionMultiplier(double repulsion) { 97 | this.repulsion_multiplier = repulsion; 98 | } 99 | 100 | /** 101 | * Reset void. 102 | */ 103 | public void reset() { 104 | doInit(); 105 | } 106 | 107 | /** 108 | * Initialize void. 109 | */ 110 | public void initialize() { 111 | doInit(); 112 | } 113 | 114 | /** 115 | * Sets graph. 116 | * 117 | * @param graph the graph 118 | */ 119 | @Override 120 | public void setGraph(final NetworkGraph graph) { 121 | super.setGraph(graph); 122 | } 123 | 124 | /** 125 | * Do init. 126 | */ 127 | private void doInit() { 128 | NetworkGraph graph = getGraph(); 129 | Dimension d = getSize(); 130 | if (graph != null && d != null) { 131 | currentIteration = 0; 132 | temperature = d.getWidth() / 10; 133 | double forceConstant = Math.sqrt(d.getHeight() * d.getWidth() / graph.getVertex().size()); 134 | attraction_constant = attraction_multiplier * forceConstant; 135 | repulsion_constant = repulsion_multiplier * forceConstant; 136 | } 137 | } 138 | 139 | /** 140 | * The EPSILON. 141 | */ 142 | private double EPSILON = 0.000001D; 143 | 144 | /** 145 | * Moves the iteration forward one notch, calculation attraction and 146 | * repulsion between vertices and edges and cooling the temperature. 147 | */ 148 | public synchronized void step() { 149 | currentIteration++; 150 | while (true) { 151 | try { 152 | for (Node v1 : getGraph().getNodes()) { 153 | calcRepulsion(v1); 154 | } 155 | break; 156 | } catch (ConcurrentModificationException cme) { 157 | Log.e(FRLayout.class.getName(), cme.getMessage()); 158 | } 159 | } 160 | while (true) { 161 | try { 162 | for (Edge e : getGraph().getEdges()) { 163 | 164 | calcAttraction(e); 165 | } 166 | break; 167 | } catch (ConcurrentModificationException cme) { 168 | Log.e(FRLayout.class.getName(), cme.getMessage()); 169 | } 170 | } 171 | while (true) { 172 | try { 173 | for (Node v : getGraph().getNodes()) { 174 | if (isLocked(v)) { 175 | continue; 176 | } 177 | calcPositions(v); 178 | } 179 | break; 180 | } catch (ConcurrentModificationException cme) { 181 | Log.e(FRLayout.class.getName(), cme.getMessage()); 182 | } 183 | } 184 | cool(); 185 | } 186 | 187 | /** 188 | * Calc positions. 189 | * 190 | * @param v the v 191 | */ 192 | private synchronized void calcPositions(Node v) { 193 | FRVertexData fvd = getFRData(v); 194 | if (fvd == null) { 195 | return; 196 | } 197 | Point2D xyd = transform(v); 198 | double deltaLength = Math.max(EPSILON, fvd.norm()); 199 | double newXDisp = fvd.getX() / deltaLength * Math.min(deltaLength, temperature); 200 | if (Double.isNaN(newXDisp)) { 201 | throw new IllegalArgumentException("Unexpected mathematical result in FRLayout:calcPositions [xdisp]"); 202 | } 203 | double newYDisp = fvd.getY() / deltaLength * Math.min(deltaLength, temperature); 204 | xyd.setLocation(xyd.getX() + newXDisp, xyd.getY() + newYDisp); 205 | double borderWidth = getSize().getWidth() / 50.0; 206 | double newXPos = xyd.getX(); 207 | if (newXPos < borderWidth) { 208 | newXPos = borderWidth + Math.random() * borderWidth * 2.0; 209 | } else { 210 | if (newXPos > (getSize().getWidth() - borderWidth)) { 211 | newXPos = getSize().getWidth() - borderWidth - Math.random() * borderWidth * 2.0; 212 | } 213 | } 214 | 215 | double newYPos = xyd.getY(); 216 | if (newYPos < borderWidth) { 217 | newYPos = borderWidth + Math.random() * borderWidth * 2.0; 218 | } else { 219 | if (newYPos > (getSize().getHeight() - borderWidth)) { 220 | newYPos = getSize().getHeight() - borderWidth - Math.random() * borderWidth * 2.0; 221 | } 222 | } 223 | xyd.setLocation(newXPos, newYPos); 224 | } 225 | 226 | /** 227 | * Calc attraction. 228 | * 229 | * @param e the e 230 | */ 231 | private void calcAttraction(Edge e) { 232 | Node v1 = e.getFrom(); 233 | Node v2 = e.getTo(); 234 | boolean v1_locked = isLocked(v1); 235 | boolean v2_locked = isLocked(v2); 236 | 237 | if (v1_locked && v2_locked) { 238 | // both locked, do nothing 239 | return; 240 | } 241 | Point2D p1 = transform(v1); 242 | Point2D p2 = transform(v2); 243 | if (p1 == null || p2 == null) { 244 | return; 245 | } 246 | double xDelta = p1.getX() - p2.getX(); 247 | double yDelta = p1.getY() - p2.getY(); 248 | 249 | double deltaLength = Math.max(EPSILON, Math.sqrt((xDelta * xDelta) + (yDelta * yDelta))); 250 | 251 | double force = (deltaLength * deltaLength) / attraction_constant; 252 | 253 | if (Double.isNaN(force)) { 254 | throw new IllegalArgumentException("Unexpected mathematical result in FRLayout:calcPositions [force]"); 255 | } 256 | 257 | double dx = (xDelta / deltaLength) * force; 258 | double dy = (yDelta / deltaLength) * force; 259 | if (!v1_locked) { 260 | FRVertexData fvd1 = getFRData(v1); 261 | fvd1.offset(-dx, -dy); 262 | } 263 | if (!v2_locked) { 264 | FRVertexData fvd2 = getFRData(v2); 265 | fvd2.offset(dx, dy); 266 | } 267 | } 268 | 269 | /** 270 | * Calc repulsion. 271 | * 272 | * @param v1 the v 1 273 | */ 274 | private void calcRepulsion(Node v1) { 275 | FRVertexData fvd1 = getFRData(v1); 276 | if (fvd1 == null) { 277 | return; 278 | } 279 | fvd1.setLocation(0, 0); 280 | 281 | try { 282 | for (Node v2 : getGraph().getNodes()) { 283 | // if (isLocked(v2)) continue; 284 | if (v1 != v2) { 285 | Point2D p1 = transform(v1); 286 | Point2D p2 = transform(v2); 287 | if (p1 == null || p2 == null) { 288 | continue; 289 | } 290 | double xDelta = p1.getX() - p2.getX(); 291 | double yDelta = p1.getY() - p2.getY(); 292 | 293 | double deltaLength = Math.max(EPSILON, Math.sqrt((xDelta * xDelta) + (yDelta * yDelta))); 294 | 295 | double force = (repulsion_constant * repulsion_constant) / deltaLength; 296 | 297 | if (Double.isNaN(force)) { 298 | throw new RuntimeException("Unexpected mathematical result in FRLayout:calcPositions [repulsion]"); 299 | } 300 | fvd1.offset((xDelta / deltaLength) * force, (yDelta / deltaLength) * force); 301 | } 302 | } 303 | } catch (ConcurrentModificationException cme) { 304 | calcRepulsion(v1); 305 | } 306 | } 307 | 308 | /** 309 | * Cool void. 310 | */ 311 | private void cool() { 312 | temperature *= (1.0 - currentIteration / (double) mMaxIterations); 313 | } 314 | 315 | /** 316 | * Sets the maximum number of iterations. 317 | * 318 | * @param maxIterations the max iterations 319 | */ 320 | public void setMaxIterations(int maxIterations) { 321 | mMaxIterations = maxIterations; 322 | } 323 | 324 | /** 325 | * Gets fR data. 326 | * 327 | * @param v the v 328 | * @return the fR data 329 | */ 330 | private FRVertexData getFRData(Node v) { 331 | return frVertexData.get(v); 332 | } 333 | 334 | /** 335 | * This one is an incremental visualization. 336 | * 337 | * @return the boolean 338 | */ 339 | public boolean isIncremental() { 340 | return true; 341 | } 342 | 343 | /** 344 | * Returns true once the current iteration has passed the maximum count, 345 | * MAX_ITERATIONS. 346 | * 347 | * @return the boolean 348 | */ 349 | public boolean done() { 350 | return currentIteration > mMaxIterations || temperature < 1.0 / max_dimension; 351 | } 352 | 353 | /** 354 | * The type FR vertex data. 355 | */ 356 | private static class FRVertexData extends Point2D { 357 | 358 | /** 359 | * Offset void. 360 | * 361 | * @param x the x 362 | * @param y the y 363 | */ 364 | void offset(double x, double y) { 365 | this.x += x; 366 | this.y += y; 367 | } 368 | 369 | /** 370 | * Norm double. 371 | * 372 | * @return the double 373 | */ 374 | double norm() { 375 | return Math.sqrt(x * x + y * y); 376 | } 377 | } 378 | } --------------------------------------------------------------------------------