Author: mes Date: 2012-04-03 11:48:47 -0700 (Tue, 03 Apr 2012) New Revision: 28725
Added: core3/impl/trunk/edge-bundler-impl/ core3/impl/trunk/edge-bundler-impl/pom.xml core3/impl/trunk/edge-bundler-impl/src/ core3/impl/trunk/edge-bundler-impl/src/main/ core3/impl/trunk/edge-bundler-impl/src/main/java/ core3/impl/trunk/edge-bundler-impl/src/main/java/org/ core3/impl/trunk/edge-bundler-impl/src/main/java/org/cytoscape/ core3/impl/trunk/edge-bundler-impl/src/main/java/org/cytoscape/edge/ core3/impl/trunk/edge-bundler-impl/src/main/java/org/cytoscape/edge/bundler/ core3/impl/trunk/edge-bundler-impl/src/main/java/org/cytoscape/edge/bundler/internal/ core3/impl/trunk/edge-bundler-impl/src/main/java/org/cytoscape/edge/bundler/internal/CyActivator.java core3/impl/trunk/edge-bundler-impl/src/main/java/org/cytoscape/edge/bundler/internal/EdgeBundlerRunner.java core3/impl/trunk/edge-bundler-impl/src/main/java/org/cytoscape/edge/bundler/internal/EdgeBundlerTask.java core3/impl/trunk/edge-bundler-impl/src/main/java/org/cytoscape/edge/bundler/internal/EdgeBundlerTaskFactory.java Modified: core3/impl/trunk/pom.xml Log: added edge bundler Added: core3/impl/trunk/edge-bundler-impl/pom.xml =================================================================== --- core3/impl/trunk/edge-bundler-impl/pom.xml (rev 0) +++ core3/impl/trunk/edge-bundler-impl/pom.xml 2012-04-03 18:48:47 UTC (rev 28725) @@ -0,0 +1,90 @@ +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <artifactId>impl-parent</artifactId> + <groupId>org.cytoscape</groupId> + <version>3.0.0-alpha8-SNAPSHOT</version> + </parent> + + <properties> + <bundle.symbolicName>edge-bundler</bundle.symbolicName> + <bundle.namespace>org.cytoscape.edge.bundler.internal</bundle.namespace> + </properties> + + <artifactId>edge-bundler-impl</artifactId> + <name>${bundle.symbolicName}</name> + <packaging>bundle</packaging> + + + <repositories> + <repository> + <id>cytoscape_snapshots</id> + <snapshots> + <enabled>true</enabled> + </snapshots> + <releases> + <enabled>false</enabled> + </releases> + <name>Cytoscape Snapshots</name> + <url>http://code.cytoscape.org/nexus/content/repositories/snapshots/</url> + </repository> + <repository> + <id>cytoscape_releases</id> + <snapshots> + <enabled>false</enabled> + </snapshots> + <releases> + <enabled>true</enabled> + </releases> + <name>Cytoscape Releases</name> + <url>http://code.cytoscape.org/nexus/content/repositories/releases/</url> + </repository> + </repositories> + + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <version>${maven-bundle-plugin.version}</version> + <extensions>true</extensions> + <configuration> + <instructions> + <Bundle-SymbolicName>${bundle.symbolicName}</Bundle-SymbolicName> + <Bundle-Version>${project.version}</Bundle-Version> + <Export-Package>!${bundle.namespace}.*</Export-Package> + <Private-Package>${bundle.namespace}.*</Private-Package> + <Bundle-Activator>${bundle.namespace}.CyActivator</Bundle-Activator> + </instructions> + </configuration> + </plugin> + </plugins> + </build> + + + <dependencies> + <dependency> + <groupId>org.cytoscape</groupId> + <artifactId>presentation-api</artifactId> + </dependency> + <dependency> + <groupId>org.cytoscape</groupId> + <artifactId>application-api</artifactId> + </dependency> + <dependency> + <groupId>org.cytoscape</groupId> + <artifactId>work-api</artifactId> + </dependency> + <dependency> + <groupId>org.cytoscape</groupId> + <artifactId>core-task-api</artifactId> + </dependency> + <dependency> + <groupId>org.cytoscape</groupId> + <artifactId>service-api</artifactId> + </dependency> + </dependencies> + + <description>Written by Gregory Hannum, March 2012</description> +</project> Added: core3/impl/trunk/edge-bundler-impl/src/main/java/org/cytoscape/edge/bundler/internal/CyActivator.java =================================================================== --- core3/impl/trunk/edge-bundler-impl/src/main/java/org/cytoscape/edge/bundler/internal/CyActivator.java (rev 0) +++ core3/impl/trunk/edge-bundler-impl/src/main/java/org/cytoscape/edge/bundler/internal/CyActivator.java 2012-04-03 18:48:47 UTC (rev 28725) @@ -0,0 +1,49 @@ +package org.cytoscape.edge.bundler.internal; + +import org.cytoscape.task.NetworkViewTaskFactory; +import org.cytoscape.view.vizmap.VisualMappingFunctionFactory; +import org.cytoscape.view.vizmap.VisualMappingManager; +import org.cytoscape.view.presentation.property.values.HandleFactory; +import org.cytoscape.view.presentation.property.values.BendFactory; +import org.cytoscape.service.util.AbstractCyActivator; +import java.util.Properties; +import org.osgi.framework.BundleContext; + + +public class CyActivator extends AbstractCyActivator { + public CyActivator() { + super(); + } + + public void start(BundleContext bc) { + + HandleFactory hf = getService(bc, HandleFactory.class); + BendFactory bf = getService(bc, BendFactory.class); + VisualMappingManager vmm = getService(bc, VisualMappingManager.class); + VisualMappingFunctionFactory discreteFactory = getService(bc,VisualMappingFunctionFactory.class,"(mapping.type=discrete)"); + + EdgeBundlerTaskFactory edgeBundlerTaskFactory = new EdgeBundlerTaskFactory(hf, bf, vmm, discreteFactory, 0); + Properties edgeBundlerTaskFactoryProps = new Properties(); + edgeBundlerTaskFactoryProps.setProperty("preferredMenu","Layout.Bundle Edges"); + edgeBundlerTaskFactoryProps.setProperty("menuGravity","11.0"); + edgeBundlerTaskFactoryProps.setProperty("title","All Nodes and Edges"); + registerService(bc,edgeBundlerTaskFactory,NetworkViewTaskFactory.class, edgeBundlerTaskFactoryProps); + + + edgeBundlerTaskFactory = new EdgeBundlerTaskFactory(hf, bf, vmm, discreteFactory, 1); + edgeBundlerTaskFactoryProps = new Properties(); + edgeBundlerTaskFactoryProps.setProperty("preferredMenu","Layout.Bundle Edges"); + edgeBundlerTaskFactoryProps.setProperty("menuGravity","12.0"); + edgeBundlerTaskFactoryProps.setProperty("title","Selected Nodes Only"); + registerService(bc,edgeBundlerTaskFactory,NetworkViewTaskFactory.class, edgeBundlerTaskFactoryProps); + + + edgeBundlerTaskFactory = new EdgeBundlerTaskFactory(hf, bf, vmm, discreteFactory, 2); + edgeBundlerTaskFactoryProps = new Properties(); + edgeBundlerTaskFactoryProps.setProperty("preferredMenu","Layout.Bundle Edges"); + edgeBundlerTaskFactoryProps.setProperty("menuGravity","13.0"); + edgeBundlerTaskFactoryProps.setProperty("title","Selected Edges Only"); + registerService(bc,edgeBundlerTaskFactory,NetworkViewTaskFactory.class, edgeBundlerTaskFactoryProps); + } +} + Added: core3/impl/trunk/edge-bundler-impl/src/main/java/org/cytoscape/edge/bundler/internal/EdgeBundlerRunner.java =================================================================== --- core3/impl/trunk/edge-bundler-impl/src/main/java/org/cytoscape/edge/bundler/internal/EdgeBundlerRunner.java (rev 0) +++ core3/impl/trunk/edge-bundler-impl/src/main/java/org/cytoscape/edge/bundler/internal/EdgeBundlerRunner.java 2012-04-03 18:48:47 UTC (rev 28725) @@ -0,0 +1,52 @@ +package org.cytoscape.edge.bundler.internal; + +public class EdgeBundlerRunner implements Runnable{ + + private final int ni; + private final int numNubs; + private final boolean[][] edgeAlign; + private final double[][][] nubs; + private final double[][][] forces; + private final double[][] edgeCompatability; + private final int[][] edgeMatcher; + + public EdgeBundlerRunner(int ni, int numNubs, boolean[][] edgeAlign, double[][][] nubs, double[][][] forces, double[][] edgeCompatability, int[][] edgeMatcher) + { + this.ni = ni; + this.numNubs = numNubs; + this.edgeAlign = edgeAlign; + this.nubs = nubs; + this.forces = forces; + this.edgeCompatability = edgeCompatability; + this.edgeMatcher = edgeMatcher; + } + + public void run() + { + for (int ei=0;ei<edgeAlign.length;ei++) + for (int em=0;em<edgeMatcher[ei].length;em++) + { + int ej = edgeMatcher[ei][em]; + + int nj = (edgeAlign[ei][ej]) ? ni : numNubs-ni-1; + + double diffx = (nubs[ni][0][ei]-nubs[nj][0][ej]); + double diffy = (nubs[ni][1][ei]-nubs[nj][1][ej]); + + if (Math.abs(diffx) > 1) + { + double fx = edgeCompatability[ei][ej] / diffx; + + forces[ni][0][ei] -= fx; + forces[nj][0][ej] += fx; + } + + if (Math.abs(diffy) > 1) + { + double fy = edgeCompatability[ei][ej] / diffy; + forces[ni][1][ei] -= fy; + forces[nj][1][ej] += fy; + } + } + } +} Added: core3/impl/trunk/edge-bundler-impl/src/main/java/org/cytoscape/edge/bundler/internal/EdgeBundlerTask.java =================================================================== --- core3/impl/trunk/edge-bundler-impl/src/main/java/org/cytoscape/edge/bundler/internal/EdgeBundlerTask.java (rev 0) +++ core3/impl/trunk/edge-bundler-impl/src/main/java/org/cytoscape/edge/bundler/internal/EdgeBundlerTask.java 2012-04-03 18:48:47 UTC (rev 28725) @@ -0,0 +1,530 @@ +//Written by Gregory Hannum +//May 2012 +//Based on Holten and Wijk. Force-directed edge bundling for graph visualization. Eurographics/IEEE-VGTC Symposium on Visualization. 2009 + +package org.cytoscape.edge.bundler.internal; + +import java.util.*; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import org.cytoscape.model.CyEdge; +import org.cytoscape.model.CyNode; +import org.cytoscape.model.CyTable; +import org.cytoscape.task.AbstractNetworkViewTask; +import org.cytoscape.view.model.CyNetworkView; +import org.cytoscape.view.model.View; +import static org.cytoscape.view.presentation.property.BasicVisualLexicon.*; +import org.cytoscape.view.vizmap.VisualMappingFunctionFactory; +import org.cytoscape.view.vizmap.VisualMappingManager; +import org.cytoscape.view.vizmap.mappings.DiscreteMapping; +import org.cytoscape.work.TaskMonitor; +import org.cytoscape.work.Tunable; +import org.cytoscape.view.presentation.property.values.Bend; +import org.cytoscape.view.presentation.property.values.BendFactory; +import org.cytoscape.view.presentation.property.values.Handle; +import org.cytoscape.view.presentation.property.values.HandleFactory; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class EdgeBundlerTask extends AbstractNetworkViewTask { + + private static final Logger logger = LoggerFactory.getLogger(EdgeBundlerTask.class); + + @Tunable(description="Number of handles") + public int numNubs = 3; + + @Tunable(description="Spring constant") + public double K = 3e-3; + + @Tunable(description="Compatability threshold") + public double COMPATABILITY_THRESHOLD = 0.3; + + @Tunable(description="Maximum iterations") + public int maxIterations = 10000; + //private double networkScale; + + private boolean animate = false; + + private double[][][] edgePos; //source/target, X/Y, edgeIndex + private double[][][] nubs; //nubLocation, X/Y, edgeIndex + private double[][] edgeCompatability; + private boolean[][] edgeAlign; + private double[] edgeLength; + private int[][] edgeMatcher; + + private HandleFactory hf; + private BendFactory bf; + private VisualMappingManager vmm; + private VisualMappingFunctionFactory discreteFactory; + + private int numEdges; + + private int selection; + + EdgeBundlerTask(CyNetworkView v, HandleFactory hf, BendFactory bf, VisualMappingManager vmm, VisualMappingFunctionFactory discreteFactory, int selection) { + super(v); + + this.hf = hf; + this.bf = bf; + this.vmm = vmm; + this.discreteFactory = discreteFactory; + this.selection = selection; + } + + public void run(TaskMonitor tm) { + + + //Check tunables + if (numNubs<1) numNubs = 1; + if (numNubs>50) + { + logger.warn("Maximum handles is 50."); + numNubs = 50; + } + + tm.setTitle("Edge Bundle Layout"); + + //Pre-cache data structures + tm.setStatusMessage("Caching network data"); + Collection<View<CyEdge>> edges = null; + + //Get selection + if (selection==0) //Use all edges + { + edges = this.view.getEdgeViews(); + } + else if (selection==1) //Use selected nodes only + { + Collection<View<CyEdge>> edgeView = this.view.getEdgeViews(); + + edges = new ArrayList<View<CyEdge>>(edgeView.size()); + + for (View<CyEdge> e : edgeView) + { + boolean n1 = view.getNodeView(e.getModel().getSource()).getVisualProperty(EDGE_SELECTED); + boolean n2 = view.getNodeView(e.getModel().getTarget()).getVisualProperty(EDGE_SELECTED); + if (n1 && n2) edges.add(e); + } + + }else if (selection==2) //Use selected edges only + { + Collection<View<CyEdge>> edgeView = this.view.getEdgeViews(); + + edges = new ArrayList<View<CyEdge>>(edgeView.size()); + + for (View<CyEdge> e : edgeView) + if (e.getVisualProperty(EDGE_SELECTED)) edges.add(e); + } + + int ei = 0; + for (View<CyEdge> e : edges) + { + View<CyNode> eSource = view.getNodeView(e.getModel().getSource()); + View<CyNode> eTarget = view.getNodeView(e.getModel().getTarget()); + + if (eSource.getSUID().equals(eTarget.getSUID())) continue; + + ei++; + } + + numEdges = ei; + + if (numEdges<2) + { + logger.warn("Less than two edges found."); + return; + } + + edgePos = new double[2][2][numEdges]; + nubs = new double[numNubs][2][numEdges]; + edgeLength = new double[numEdges]; + + ei = 0; + for (View<CyEdge> e : edges) + { + //System.out.println("SUID: "+e.getModel().getSUID()); + + View<CyNode> eSource = view.getNodeView(e.getModel().getSource()); + View<CyNode> eTarget = view.getNodeView(e.getModel().getTarget()); + + if (eSource.getSUID().equals(eTarget.getSUID())) continue; + + edgePos[0][0][ei] = eSource.getVisualProperty(NODE_X_LOCATION); + edgePos[0][1][ei] = eSource.getVisualProperty(NODE_Y_LOCATION); + edgePos[1][0][ei] = eTarget.getVisualProperty(NODE_X_LOCATION); + edgePos[1][1][ei] = eTarget.getVisualProperty(NODE_Y_LOCATION); + + //if (edgePos[0][0][ei]>edgePos[1][0][ei]) {double temp = edgePos[0][0][ei]; edgePos[0][0][ei] = edgePos[1][0][ei]; edgePos[1][0][ei] = temp;} + //if (edgePos[0][1][ei]>edgePos[1][1][ei]) {double temp = edgePos[0][1][ei]; edgePos[0][1][ei] = edgePos[1][1][ei]; edgePos[1][1][ei] = temp;} + + double diffx = edgePos[1][0][ei] - edgePos[0][0][ei]; + double diffy = edgePos[1][1][ei] - edgePos[0][1][ei]; + + for (int ni=0;ni<numNubs;ni++) + { + nubs[ni][0][ei] = (diffx) * (ni + 1) / (numNubs + 1) + edgePos[0][0][ei]; + nubs[ni][1][ei] = (diffy) * (ni + 1) / (numNubs + 1) + edgePos[0][1][ei]; + } + + edgeLength[ei] = Math.sqrt(diffx*diffx + diffy*diffy); + + ei++; + } + + + //networkScale = computeNetworkScale(); + computeEdgeCompatability(); + + //Simulating physics + tm.setStatusMessage("Simulating physics"); + double time = System.nanoTime(); + double[][][] forces = new double[numNubs][2][numEdges]; //Nub, X/Y, edgeIndex + for (int iteri=0;iteri<maxIterations;iteri++) + { + if (this.cancelled) + { + //reset(); + logger.info("Edge bundling cancelled: iter="+iteri); + break; + } + + tm.setProgress(iteri/Double.valueOf(maxIterations)); + + updateForces(forces); + updateNubs(forces); + + //Check convergence once in awhile + if (iteri%1000==0 && isConverged(forces,.01)) + { + logger.info("Edge bundling converged: iter="+iteri); + break; + } + + if (iteri==maxIterations-1) + { + logger.info("Edge bundling did not converge: iter="+iteri); + break; + } + + if (animate && System.nanoTime()-time>3) + { + render(edges); + time = System.nanoTime(); + } + } + + render(edges); + + +// double minX = 0; +// double maxX = 0; +// double minY = 0; +// double maxY = 0; +// +// +// for (ei=0;ei<edgeLength.length;ei++) +// for (int ni=0;ni<numNubs;ni++) +// { +// double x = nubs[ni][0][ei]; +// double y = nubs[ni][1][ei]; +// +// if (x<minX) minX = x; +// if (x>maxX) maxX = x; +// if (y<minY) minY = y; +// if (y>maxY) maxY = y; +// +// if (Double.isInfinite(x) || Double.isInfinite(y) || Double.isNaN(x) || Double.isNaN(y)) +// System.out.println("Infinite or NaN positions detected!"); +// } +// +// System.out.println(minX+":"+maxX+", "+minY+":"+maxY); + } + + private double computeNetworkScale() + { + double centerX = 0; + double centerY = 0; + + for (int ei=0;ei<edgeLength.length;ei++) + { + centerX += edgePos[0][0][ei] + edgePos[1][0][ei]; + centerY += edgePos[0][1][ei] + edgePos[1][1][ei]; + } + + centerX /= 2.0*edgeLength.length; + centerY /= 2.0*edgeLength.length; + + double[] dist = new double[2*edgeLength.length]; + + for (int ei=0;ei<edgeLength.length;ei++) + { + double diff0x = edgePos[0][0][ei] - centerX; + double diff0y = edgePos[0][1][ei] - centerY; + double diff1x = edgePos[1][0][ei] - centerX; + double diff1y = edgePos[1][1][ei] - centerY; + + dist[2*ei] = Math.sqrt(diff0x*diff0x + diff0y*diff0y); + dist[2*ei+1] = Math.sqrt(diff1x*diff1x + diff1y*diff1y); + } + + Arrays.sort(dist); + double scale = dist[dist.length/2]; + + return scale; + } + + private boolean isConverged(double[][][] forces, double threshold) + { + for (int ei=0;ei<edgeLength.length;ei++) + for (int ni=0;ni<numNubs;ni++) + if (Math.abs(forces[ni][0][ei])>threshold || Math.abs(forces[ni][1][ei])>threshold) + { + //System.out.println(forces[ni][0][ei] + ", "+ forces[ni][1][ei]); + return false; + } + + return true; + } + + private void render(Collection<View<CyEdge>> edges) + { + DiscreteMapping<Long, Bend> function = (DiscreteMapping<Long,Bend>) discreteFactory.createVisualMappingFunction(CyTable.SUID, Long.class, null, EDGE_BEND); + + int ei = 0; + for (View<CyEdge> e : edges) + { + View<CyNode> eSource = view.getNodeView(e.getModel().getSource()); + View<CyNode> eTarget = view.getNodeView(e.getModel().getTarget()); + + if (eSource.getSUID().equals(eTarget.getSUID())) continue; + + Bend b = bf.createBend(); + List<Handle> hlist = b.getAllHandles(); + + for (int ni=0;ni<numNubs;ni++) + { + double x = nubs[ni][0][ei]; + double y = nubs[ni][1][ei]; + + Handle h = hf.createHandle(0, 0); + h.defineHandle(view,e, x, y); + hlist.add(h); + } + + function.putMapValue(e.getModel().getSUID(), b); + ei++; + } + + vmm.getVisualStyle(view).addVisualMappingFunction(function); + + vmm.getVisualStyle(view).apply(view); + view.updateView(); + } + + private void computeEdgeCompatability() + { + edgeCompatability = new double[edgeLength.length][]; + edgeAlign = new boolean[edgeLength.length][]; + edgeMatcher = new int[edgeLength.length][]; + + edgeMatcher[0] = new int[0]; + + for (int ei=1;ei<edgeLength.length;ei++) + { + edgeCompatability[ei] = new double[ei]; + edgeAlign[ei] = new boolean[ei]; + + List<Integer> compatibleEdges = new ArrayList<Integer>(1000); + for (int ej=0;ej<ei;ej++) + { + edgeCompatability[ei][ej] = cangle(ei,ej) * cscale(ei, ej) * cpos(ei, ej) * cvis(ei, ej); + edgeAlign[ei][ej] = cangleSign(ei,ej)>0; + + if (edgeCompatability[ei][ej]>COMPATABILITY_THRESHOLD) compatibleEdges.add(ej); + } + + edgeMatcher[ei] = new int[compatibleEdges.size()]; + for (int i=0;i<compatibleEdges.size();i++) + edgeMatcher[ei][i] = compatibleEdges.get(i); + } + } + + private double cangle(int ei, int ej) + { + double a = edgePos[1][0][ei] - edgePos[0][0][ei]; + double b = edgePos[1][1][ei] - edgePos[0][1][ei]; + double c = edgePos[1][0][ej] - edgePos[0][0][ej]; + double d = edgePos[1][1][ej] - edgePos[0][1][ej]; + + double cosAlpha = ((a*c) + (b*d))/(edgeLength[ei]*edgeLength[ej]); + + double out = Math.abs(cosAlpha); + + if (Double.isNaN(out) || Double.isInfinite(out)) return 0; + return out; + } + + private double cangleSign(int ei, int ej) + { + double a = edgePos[1][0][ei] - edgePos[0][0][ei]; + double b = edgePos[1][1][ei] - edgePos[0][1][ei]; + double c = edgePos[1][0][ej] - edgePos[0][0][ej]; + double d = edgePos[1][1][ej] - edgePos[0][1][ej]; + + double cosAlpha = ((a*c) + (b*d))/(edgeLength[ei]*edgeLength[ej]); + + double out = Math.signum(cosAlpha); + + if (Double.isNaN(out) || Double.isInfinite(out)) return 0; + return out; + } + + private double cscale(int ei, int ej) + { + double lavg = (edgeLength[ei] + edgeLength[ej])/2.0; + + //Note: the formula in the paper is wrong (*min vs. /min) + double out = 2.0/((lavg/Math.min(edgeLength[ei],edgeLength[ej])) + (Math.max(edgeLength[ei],edgeLength[ej])/lavg)); + + if (Double.isNaN(out) || Double.isInfinite(out)) return 0; + return out; + } + + private double cpos(int ei, int ej) + { + double lavg = (edgeLength[ei] + edgeLength[ej])/2.0; + + double out = lavg / (lavg + distance(mid(ei),mid(ej)) ); + + if (Double.isNaN(out) || Double.isInfinite(out)) return 0; + + return out; + } + + private double[] mid(int ei) { + return new double[]{(edgePos[1][0][ei]+edgePos[0][0][ei])/2.0,(edgePos[1][1][ei]+edgePos[0][1][ei])/2.0}; + } + + private double distance(double[] p, double[] q) { + double x = p[0] - q[0]; + double y = p[1] - q[1]; + return Math.sqrt(x*x + y*y); + } + + private double cvis(int ei, int ej) + { + return Math.min(vis(ei,ej), vis(ej,ei)); + } + + private double vis(int ei, int ej) + { + double[] I0 = getProjection(ei,ej,new double[]{edgePos[0][0][ei],edgePos[0][1][ei]}); + if (I0==null) return 0; + + double[] I1 = getProjection(ei,ej,new double[]{edgePos[1][0][ei],edgePos[1][1][ei]}); + if (I1==null) return 0; + + double[] Im = new double[]{(I1[0]-I0[0])/2 + I0[0], (I1[1]-I0[1])/2 + I0[1]}; + + double[] Pm = mid(ej); + + double a = distance(Pm, Im); + double b = distance(I0,I1); + return Math.max(1.0 - 2*a/b, 0); + } + + private double[] getProjection(int ei, int ej, double[] m) + { + double dx1 = edgePos[1][0][ei] - edgePos[0][0][ei]; + double dy1 = edgePos[1][1][ei] - edgePos[0][1][ei]; + + double dx2 = edgePos[1][0][ej] - edgePos[0][0][ej]; + double dy2 = edgePos[1][1][ej] - edgePos[0][1][ej]; + + double cx = edgePos[0][0][ej]; + double cy = edgePos[0][1][ej]; + + //INTERSECTION + //double A1 = dy1; + //double B1 = -dx1; + //double A2 = dy2; + //double B2 = -dx2; + + //PROJECTION + double A1 = dx1; + double B1 = dy1; + double A2 = dy2; + double B2 = -dx2; + + double C1 = A1*m[0]+B1*m[1]; + double C2 = A2*cx+B2*cy; + + double det = A1*B2 - A2*B1; + if (Math.abs(det)<1e-10) return null; + + double x = (B2*C1-B1*C2)/det; + double y = (A1*C2-A2*C1)/det; + + return new double[]{x,y}; + } + + + private void updateForces(double[][][] forces) + { + //Spring forces + for (int ei=0;ei<edgeLength.length;ei++) + for (int ni=0;ni<numNubs;ni++) + { + if (ni==0) + { + forces[ni][0][ei] = nubs[ni][0][ei] - edgePos[0][0][ei]; + forces[ni][1][ei] = nubs[ni][1][ei] - edgePos[0][1][ei]; + }else + { + forces[ni][0][ei] = nubs[ni][0][ei] - nubs[ni-1][0][ei]; + forces[ni][1][ei] = nubs[ni][1][ei] - nubs[ni-1][1][ei]; + } + + if (ni==numNubs-1) + { + forces[ni][0][ei] += nubs[ni][0][ei] - edgePos[1][0][ei]; + forces[ni][1][ei] += nubs[ni][1][ei] - edgePos[1][1][ei]; + }else + { + forces[ni][0][ei] += nubs[ni][0][ei] - nubs[ni+1][0][ei]; + forces[ni][1][ei] += nubs[ni][1][ei] - nubs[ni+1][1][ei]; + } + + forces[ni][0][ei] *= -K; + forces[ni][1][ei] *= -K; + + } + + //Electrostatic forces + ExecutorService exec = Executors.newFixedThreadPool(Math.min(numNubs,Runtime.getRuntime().availableProcessors())); + + for (int ni=0;ni<numNubs;ni++) + exec.execute(new EdgeBundlerRunner(ni, numNubs, edgeAlign, nubs, forces, edgeCompatability, edgeMatcher)); + + exec.shutdown(); + + try {exec.awaitTermination(30, TimeUnit.DAYS);} + catch (InterruptedException e) {e.printStackTrace();exec.shutdownNow();} + } + + private void updateNubs(double[][][] forces) + { + for (int ei=0;ei<edgeLength.length;ei++) + for (int ni=0;ni<numNubs;ni++) + { + nubs[ni][0][ei] += forces[ni][0][ei]; + nubs[ni][1][ei] += forces[ni][1][ei]; + + //nubs[ni][0][ei] += Math.signum(forces[ni][0][ei]) * Math.min(Math.abs(forces[ni][0][ei]) * stepsize, networkScale * 1e-2); + //nubs[ni][1][ei] += Math.signum(forces[ni][1][ei]) * Math.min(Math.abs(forces[ni][1][ei]) * stepsize, networkScale * 1e-2); + } + } +} Added: core3/impl/trunk/edge-bundler-impl/src/main/java/org/cytoscape/edge/bundler/internal/EdgeBundlerTaskFactory.java =================================================================== --- core3/impl/trunk/edge-bundler-impl/src/main/java/org/cytoscape/edge/bundler/internal/EdgeBundlerTaskFactory.java (rev 0) +++ core3/impl/trunk/edge-bundler-impl/src/main/java/org/cytoscape/edge/bundler/internal/EdgeBundlerTaskFactory.java 2012-04-03 18:48:47 UTC (rev 28725) @@ -0,0 +1,33 @@ +package org.cytoscape.edge.bundler.internal; + +import org.cytoscape.view.presentation.property.values.BendFactory; +import org.cytoscape.view.presentation.property.values.HandleFactory; +import org.cytoscape.model.CyNetwork; +import org.cytoscape.task.AbstractNetworkViewTaskFactory; +import org.cytoscape.view.model.CyNetworkView; +import org.cytoscape.view.vizmap.VisualMappingFunctionFactory; +import org.cytoscape.view.vizmap.VisualMappingManager; +import org.cytoscape.work.TaskIterator; + + +public class EdgeBundlerTaskFactory extends AbstractNetworkViewTaskFactory { + + private HandleFactory hf; + private BendFactory bf; + private VisualMappingManager vmm; + private VisualMappingFunctionFactory discreteFactory; + private int selection; + + public EdgeBundlerTaskFactory(HandleFactory hf, BendFactory bf, VisualMappingManager vmm, VisualMappingFunctionFactory discreteFactory, int selection) { + super(); + this.hf = hf; + this.bf = bf; + this.vmm = vmm; + this.discreteFactory = discreteFactory; + this.selection = selection; + } + + public TaskIterator createTaskIterator(CyNetworkView view) { + return new TaskIterator(new EdgeBundlerTask(view, hf, bf, vmm, discreteFactory, selection)); + } +} Modified: core3/impl/trunk/pom.xml =================================================================== --- core3/impl/trunk/pom.xml 2012-04-03 18:40:39 UTC (rev 28724) +++ core3/impl/trunk/pom.xml 2012-04-03 18:48:47 UTC (rev 28725) @@ -65,7 +65,8 @@ <module>datasource-biogrid-impl</module> <module>welcome-impl</module> <module>gui-cmdline-parser-impl</module> - <module>scripting-impl</module> + <module>edge-bundler-impl</module> + <module>scripting-impl</module> </modules> <properties> -- You received this message because you are subscribed to the Google Groups "cytoscape-cvs" group. To post to this group, send email to [email protected]. To unsubscribe from this group, send email to [email protected]. For more options, visit this group at http://groups.google.com/group/cytoscape-cvs?hl=en.
