├── run.sh ├── run.bat ├── src ├── data │ ├── Trio.tip │ ├── chaune.ged │ ├── menard.ged │ ├── Ragusan.ged │ ├── druzina.ged │ ├── Kel-Kummer.tip │ ├── basic.ped │ ├── despinosecgodurand.ged │ ├── GenealogyResources (Autosaved) │ ├── basic2.ped │ ├── basic3.ped │ ├── basic7.ped │ ├── GenealogyResources.txt │ ├── x.ped │ └── Simpsons.ged ├── main │ ├── resources │ │ └── log4j.properties │ ├── java │ │ └── geneaquilt │ │ │ ├── nodes │ │ │ ├── PVertex.java │ │ │ ├── PCircle.java │ │ │ ├── PIndi.java │ │ │ ├── FamGeneration.java │ │ │ ├── TextOutlineManager.java │ │ │ ├── PFam.java │ │ │ ├── PEdge.java │ │ │ ├── GraphicsConstants.java │ │ │ ├── IndiGeneration.java │ │ │ ├── PIsoShape.java │ │ │ ├── PSemanticText.java │ │ │ ├── PSemanticPath.java │ │ │ └── PFlatRect.java │ │ │ ├── utils │ │ │ ├── PiccoloUtils.java │ │ │ ├── PrintConstants.java │ │ │ ├── PPrintContext.java │ │ │ ├── MulticolorStroke.java │ │ │ ├── PrintUtilities.java │ │ │ └── Benchmark.java │ │ │ ├── hull │ │ │ ├── HullBin.java │ │ │ └── Path.java │ │ │ ├── algorithms │ │ │ ├── AbstractAlgorithm.java │ │ │ ├── BFSCycleFinder.java │ │ │ ├── LayerClusterer.java │ │ │ ├── VertexOrder.java │ │ │ ├── LayerRank.java │ │ │ └── GenerationRank.java │ │ │ ├── event │ │ │ ├── MouseWheelZoomController.java │ │ │ └── DisabablePanEventHandler.java │ │ │ ├── io │ │ │ ├── LayerWriter.java │ │ │ ├── TESTReader.java │ │ │ ├── LayersReader.java │ │ │ ├── DOTWriter.java │ │ │ ├── DOTLayersReader.java │ │ │ ├── GEDReader.java │ │ │ ├── JSONWriter.java │ │ │ └── PEDReader.java │ │ │ ├── PFilterAnimation.java │ │ │ ├── selection │ │ │ ├── highlight │ │ │ │ ├── Highlight.java │ │ │ │ ├── PathHighlight.java │ │ │ │ └── SelectionCombination.java │ │ │ ├── MouseSelectionController.java │ │ │ ├── DOIManager.java │ │ │ └── SelectionManager.java │ │ │ ├── ConstraintViewport.java │ │ │ ├── data │ │ │ ├── Fam.java │ │ │ ├── Edge.java │ │ │ └── Indi.java │ │ │ └── DetailsTable.java │ └── jnlp │ │ └── template.vm └── assemble │ └── distribution.xml ├── LICENSE-head ├── .settings ├── org.eclipse.core.resources.prefs ├── org.maven.ide.eclipse.prefs └── org.eclipse.jdt.core.prefs ├── .project ├── README.md ├── .classpath ├── LICENSE └── pom.xml /run.sh: -------------------------------------------------------------------------------- 1 | java -Xmx512m -jar geneaquilt-${pom.version}.jar $1 2 | -------------------------------------------------------------------------------- /run.bat: -------------------------------------------------------------------------------- 1 | java -Xmx512m -jar geneaquilt-${pom.version}.jar %1 2 | -------------------------------------------------------------------------------- /src/data/Trio.tip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdfekete/geneaquilt/HEAD/src/data/Trio.tip -------------------------------------------------------------------------------- /src/data/chaune.ged: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdfekete/geneaquilt/HEAD/src/data/chaune.ged -------------------------------------------------------------------------------- /src/data/menard.ged: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdfekete/geneaquilt/HEAD/src/data/menard.ged -------------------------------------------------------------------------------- /src/data/Ragusan.ged: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdfekete/geneaquilt/HEAD/src/data/Ragusan.ged -------------------------------------------------------------------------------- /src/data/druzina.ged: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdfekete/geneaquilt/HEAD/src/data/druzina.ged -------------------------------------------------------------------------------- /src/data/Kel-Kummer.tip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdfekete/geneaquilt/HEAD/src/data/Kel-Kummer.tip -------------------------------------------------------------------------------- /src/data/basic.ped: -------------------------------------------------------------------------------- 1 | 1 1 0 0 1 2 | 1 2 0 0 2 3 | 1 3 0 0 1 4 | 1 4 1 2 2 5 | 1 5 3 4 2 6 | 1 6 3 4 1 7 | -------------------------------------------------------------------------------- /src/data/despinosecgodurand.ged: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdfekete/geneaquilt/HEAD/src/data/despinosecgodurand.ged -------------------------------------------------------------------------------- /src/data/GenealogyResources (Autosaved): -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jdfekete/geneaquilt/HEAD/src/data/GenealogyResources (Autosaved) -------------------------------------------------------------------------------- /LICENSE-head: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | -------------------------------------------------------------------------------- /src/data/basic2.ped: -------------------------------------------------------------------------------- 1 | 1 1 0 0 1 1 x 3 3 x x 2 | 1 2 0 0 2 1 x 4 4 x x 3 | 1 3 0 0 1 1 x 1 2 x x 4 | 1 4 1 2 2 1 x 4 3 x x 5 | 1 5 3 4 2 2 1.234 1 3 2 2 6 | 1 6 3 4 1 2 4.321 2 4 2 2 7 | 8 | -------------------------------------------------------------------------------- /.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding//src/main/java=UTF-8 3 | encoding//src/main/resources=UTF-8 4 | encoding//src/site/resources=UTF-8 5 | encoding//src/test/java=UTF-8 6 | encoding//src/test/resources=UTF-8 7 | encoding/=UTF-8 8 | -------------------------------------------------------------------------------- /.settings/org.maven.ide.eclipse.prefs: -------------------------------------------------------------------------------- 1 | #Tue Jan 26 21:47:10 CET 2010 2 | activeProfiles= 3 | eclipse.preferences.version=1 4 | fullBuildGoals=process-test-resources 5 | includeModules=false 6 | resolveWorkspaceProjects=true 7 | resourceFilterGoals=process-resources resources\:testResources 8 | skipCompilerPlugin=false 9 | version=1 10 | -------------------------------------------------------------------------------- /src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.rootLogger=DEBUG, A1 2 | log4j.appender.A1=org.apache.log4j.ConsoleAppender 3 | log4j.appender.A1.layout=org.apache.log4j.PatternLayout 4 | 5 | # Print the date in ISO 8601 format 6 | log4j.appender.A1.layout.ConversionPattern=%d [%t] %-5p %c - %m%n 7 | 8 | # Print only messages of level WARN or above in the package geneaquilt 9 | #log4j.logger.geneaquilt=WARN 10 | -------------------------------------------------------------------------------- /src/data/basic3.ped: -------------------------------------------------------------------------------- 1 | 1 1 0 0 1 1 x 3 3 2 | 1 2 0 0 2 1 x 4 4 3 | 1 3 0 0 1 1 x 1 2 4 | 1 4 1 2 2 1 x 4 3 5 | 1 5 3 4 2 2 1.234 1 3 6 | 1 6 3 4 1 2 4.321 2 4 7 | 2 1 0 0 1 1 x 4 3 8 | 2 2 0 0 2 1 x 3 4 9 | 2 3 0 0 1 1 x 1 2 10 | 2 4 1 2 2 1 x 4 3 11 | 2 5 0 0 2 1 x 3 5 12 | 2 6 3 4 1 2 1.234 1 3 13 | 2 7 3 4 1 2 3.321 2 4 14 | 2 8 3 4 2 1 5.175 1 4 15 | 2 9 5 6 2 2 0.512 3 3 16 | -------------------------------------------------------------------------------- /src/data/basic7.ped: -------------------------------------------------------------------------------- 1 | 1 1 0 0 1 1 x 3 3 2 | 1 2 0 0 2 1 x 4 4 3 | 1 3 0 0 1 1 x 1 2 4 | 1 4 1 2 2 1 x 4 3 5 | 1 5 3 4 2 2 7.000 1 3 6 | 1 6 3 4 1 2 7.500 2 4 7 | 2 1 0 0 1 2 9.000 4 3 8 | 2 2 0 0 2 1 x 3 4 9 | 2 3 0 0 1 1 8.000 1 2 10 | 2 4 1 2 2 1 10.000 4 3 11 | 2 5 0 0 2 1 x 3 5 12 | 2 6 3 4 1 2 1.234 1 3 13 | 2 7 3 4 1 2 3.321 2 4 14 | 2 8 3 4 2 1 5.175 1 4 15 | 2 9 5 6 2 2 0.512 3 3 16 | 17 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/nodes/PVertex.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.nodes; 9 | 10 | import geneaquilt.data.Vertex; 11 | 12 | /** 13 | * Class PVertex 14 | * 15 | * @author Jean-Daniel Fekete 16 | * @version $Revision$ 17 | */ 18 | public interface PVertex { 19 | /** 20 | * @return the vertex 21 | */ 22 | public Vertex getVertex(); 23 | } 24 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 4 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 5 | org.eclipse.jdt.core.compiler.compliance=1.5 6 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 7 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 8 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 9 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 10 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 11 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 12 | org.eclipse.jdt.core.compiler.source=1.5 13 | -------------------------------------------------------------------------------- /src/main/jnlp/template.vm: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | GeneaQuilt 8 | INRIA & North Carolina State University 9 | 10 | Genealogy Quilt (GeneaQuilt) 11 | geneaquilt 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | $dependencies 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | geneaquilt 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.maven.ide.eclipse.maven2Builder 15 | 16 | 17 | 18 | 19 | org.eclipse.m2e.core.maven2Builder 20 | 21 | 22 | 23 | 24 | 25 | org.eclipse.m2e.core.maven2Nature 26 | org.eclipse.jdt.core.javanature 27 | org.maven.ide.eclipse.maven2Nature 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/nodes/PCircle.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.nodes; 9 | 10 | import java.awt.geom.Ellipse2D; 11 | 12 | /** 13 | * Class PCircle 14 | * 15 | * @author Jean-Daniel Fekete 16 | * @version $Revision$ 17 | */ 18 | public class PCircle extends PIsoShape { 19 | static Ellipse2D.Double ellipse = new Ellipse2D.Double(); 20 | /** 21 | * Creates a PCircle 22 | */ 23 | public PCircle() { 24 | super(ellipse); 25 | } 26 | 27 | /** 28 | * @return the shape 29 | */ 30 | public Ellipse2D getEllipse() { 31 | return ellipse; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/utils/PiccoloUtils.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.utils; 9 | 10 | import edu.umd.cs.piccolo.PNode; 11 | import edu.umd.cs.piccolo.util.PBounds; 12 | 13 | public class PiccoloUtils { 14 | 15 | /** 16 | * Sets component's global location by taking into acount its scale. 17 | * @param node 18 | * @param x 19 | * @param y 20 | * @param updateBounds 21 | */ 22 | public static void setLocation(PNode node, double x, double y, boolean invalidateBounds) { 23 | double scale = node.getScale(); 24 | PBounds b = node.getBoundsReference(); 25 | b.x = x/scale; 26 | b.y = y/scale; 27 | if (invalidateBounds) 28 | node.signalBoundsChanged(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/assemble/distribution.xml: -------------------------------------------------------------------------------- 1 | 4 | bin 5 | 6 | zip 7 | 8 | 9 | 10 | ${project.build.directory} 11 | / 12 | 13 | geneaquilt-${pom.version}.jar 14 | README* 15 | LICENSE* 16 | run* 17 | 18 | 755 19 | 20 | 21 | ${project.build.directory}/data 22 | /data 23 | 24 | * 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/hull/HullBin.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.hull; 9 | import edu.umd.cs.piccolo.util.PBounds; 10 | 11 | /** 12 | * 13 | * An object that is used as a vertical bin for computing hulls. These objects just have to store a 14 | * bin index and know their bounds, the rest of the work is done by Hull. 15 | * 16 | * Currently, only PFam and IndiGeneration need to implement this interface. 17 | * 18 | * @author dragice 19 | * 20 | */ 21 | public interface HullBin { 22 | 23 | /** 24 | * @return the hull-bin index 25 | */ 26 | public int getHullBinIndex(); 27 | 28 | /** 29 | * Sets the hull-bin index 30 | * @param index the new index 31 | */ 32 | public void setHullBinIndex(int index); 33 | 34 | /** 35 | * @return the bounds 36 | */ 37 | public PBounds getFullBoundsReference(); 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/algorithms/AbstractAlgorithm.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.algorithms; 9 | 10 | import geneaquilt.data.Network; 11 | 12 | /** 13 | * AbstractAlgorithm is the base class for 14 | * algorithms on a network. 15 | * 16 | * @author Jean-Daniel Fekete 17 | * @version $Revision$ 18 | */ 19 | public abstract class AbstractAlgorithm { 20 | protected Network network; 21 | 22 | /** 23 | * Creates an algorithm to work on the specified network 24 | * @param network the network 25 | */ 26 | public AbstractAlgorithm(Network network) { 27 | this.network = network; 28 | } 29 | 30 | /** 31 | * @return the network 32 | */ 33 | public Network getNetwork() { 34 | return network; 35 | } 36 | 37 | /** 38 | * Computes the algorithm. 39 | */ 40 | abstract public void compute(); 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/utils/PrintConstants.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.utils; 9 | 10 | import edu.umd.cs.piccolo.util.PPaintContext; 11 | import geneaquilt.nodes.GraphicsConstants; 12 | 13 | import java.awt.BasicStroke; 14 | import java.awt.Color; 15 | import java.awt.Font; 16 | import java.awt.Stroke; 17 | 18 | /** 19 | * GraphicsConstants defines graphic constants 20 | * 21 | * @author Pierre Dragicevic 22 | */ 23 | @SuppressWarnings("all") 24 | public class PrintConstants extends GraphicsConstants { 25 | 26 | @Override 27 | public Color gridColor() { return Color.BLACK; } 28 | 29 | @Override 30 | public Stroke gridStroke() { return new BasicStroke(0.2f); } 31 | 32 | @Override 33 | public Color edgeColor() { return Color.BLACK; } 34 | 35 | @Override 36 | public Color famBorderColor() { return Color.BLACK; } 37 | 38 | @Override 39 | public double getScale(PPaintContext p) { return 1; } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/data/GenealogyResources.txt: -------------------------------------------------------------------------------- 1 | Information on the Resources on this file: 2 | 3 | Bible Family Tree.ged 4 | - Description: Family tree from the Bible. 5 | - Origin: http://famousfamilytrees.blogspot.com/2008/07/links-to-religious-system-ged-file.html 6 | - Author: It was made by Jeffrey Philip Stoker of Lancashire England. Details in file 7 | 8 | royal92.ged 9 | - Description: Public domain GEDCOM file containing information on 3010 individuals and 1422 families of European royalty. Weak in French and Spanish lines. 10 | - Origin: http://www.daml.org/2001/01/gedcom/ 11 | - Author: Denis Reid 12 | 13 | GreekGods_shortnames.ged 14 | - Description: Greek Gods 15 | - Origin: Adapted from http://famousfamilytrees.blogspot.com/2008/07/links-to-religious-system-ged-file.html 16 | - Author: Blog owner 17 | 18 | Abbasids House.ged 19 | - Description: Mohammad's Ancestors, transcribed from an image family tree. This ged file was made by Juan Ignacio Pucheu under contract by blog owner. 20 | - Origin: http://famousfamilytrees.blogspot.com/2008/07/links-to-religious-system-ged-file.html 21 | - Author: Juan Ignacio Pucheu 22 | 23 | Simpsons.ged 24 | - Description: Simpsons 25 | - Origin: http://groups.google.com/group/famous-family-trees 26 | - Author: ? 27 | 28 | Kennedy+Family.ged 29 | - Description: Kennedy family tree 30 | - Origin: http://arborvita.free.fr/ 31 | 32 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/nodes/PIndi.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.nodes; 9 | 10 | import geneaquilt.data.Indi; 11 | import geneaquilt.data.Vertex; 12 | 13 | /** 14 | * PIndi is a Piccolo object for an individual node. 15 | * 16 | * @author Jean-Daniel Fekete 17 | * @version $Revision$ 18 | */ 19 | public class PIndi extends PSemanticText implements PVertex { 20 | 21 | Indi indi; 22 | /** 23 | * Creates a PIndi 24 | * @param indi the indi 25 | */ 26 | public PIndi(Indi indi) { 27 | super(indi.getLabel()); 28 | this.indi = indi; 29 | setTextPaint(GraphicsConstants.INDI_COLOR); 30 | setFont(GraphicsConstants.INDI_FONT); 31 | } 32 | 33 | /** 34 | * @return the indi 35 | */ 36 | public Indi getIndi() { 37 | return indi; 38 | } 39 | 40 | /** 41 | * {@inheritDoc} 42 | */ 43 | public Vertex getVertex() { 44 | return indi; 45 | } 46 | /* public void setPaint(Paint p) { 47 | if (p != null) 48 | super.setPaint(p); 49 | }*/ 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/event/MouseWheelZoomController.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.event; 9 | 10 | import edu.umd.cs.piccolo.PCamera; 11 | import edu.umd.cs.piccolo.event.PInputEvent; 12 | import edu.umd.cs.piccolo.event.PZoomEventHandler; 13 | 14 | /** 15 | * Class MouseWheelZoomController 16 | * 17 | * @author Jean-Daniel Fekete 18 | * @version $Revision$ 19 | */ 20 | public class MouseWheelZoomController extends PZoomEventHandler { 21 | /** 22 | * {@inheritDoc} 23 | */ 24 | public void mouseWheelRotated(PInputEvent aEvent) { 25 | PCamera camera = aEvent.getCamera(); 26 | double scaleDelta = (1.0 - (0.1 * aEvent.getWheelRotation())); 27 | 28 | double currentScale = camera.getViewScale(); 29 | double newScale = currentScale * scaleDelta; 30 | 31 | if (newScale < getMinScale()) { 32 | scaleDelta = getMinScale() / currentScale; 33 | } 34 | if ((getMaxScale() > 0) && (newScale > getMaxScale())) { 35 | scaleDelta = getMaxScale() / currentScale; 36 | } 37 | 38 | camera.scaleViewAboutPoint( 39 | scaleDelta, 40 | aEvent.getPosition().getX(), 41 | aEvent.getPosition().getY()); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/utils/PPrintContext.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.utils; 9 | 10 | import java.awt.Graphics2D; 11 | 12 | import edu.umd.cs.piccolo.util.PAffineTransform; 13 | import edu.umd.cs.piccolo.util.PPaintContext; 14 | 15 | /** 16 | * PPrintContext manages a print context 17 | * 18 | * @author Jean-Daniel Fekete 19 | * @version $Revision$ 20 | */ 21 | public class PPrintContext extends PPaintContext { 22 | 23 | /** 24 | * Creates a PPrintContext associated with the given graphics context. 25 | * 26 | * @param graphics graphics context to associate with this paint context 27 | */ 28 | public PPrintContext(Graphics2D graphics) { 29 | super(graphics); 30 | } 31 | 32 | /** 33 | * {@inheritDoc} 34 | */ 35 | @Override 36 | public void pushTransform(PAffineTransform aTransform) { 37 | if (aTransform != null && ! aTransform.isIdentity()) 38 | super.pushTransform(aTransform); 39 | } 40 | 41 | /** 42 | * {@inheritDoc} 43 | */ 44 | @Override 45 | public void popTransform(PAffineTransform aTransform) { 46 | if (aTransform != null && ! aTransform.isIdentity()) 47 | super.popTransform(aTransform); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GeneaQuilts 2 | 3 | GeneaQuilts is a new visualization technique for representing large 4 | genealogies of up to several thousand individuals. The visualization 5 | takes the form of a diagonally-filled matrix, where rows are 6 | individuals and columns are nuclear families. The GeneaQuilts system 7 | includes an overview, a timeline, search and filtering components, and 8 | a new interaction technique called Bring & Slide that allows fluid 9 | navigation in very large genealogies. 10 | 11 | ## Maven 12 | 13 | GeneaQuilts uses Maven. To compile, simply type: 14 | 15 | `mvn install` 16 | 17 | Everything will be in the `target/` directory. In particular, two 18 | scripts to start the program (run.sh and run.bat). 19 | Alternatively, you can launch GeneaQuilts by using the generated jar file. 20 | 21 | ## Developing with ECLIPSE 22 | 23 | To import the GeneaQuilts project in Eclipse, you first need to install the Subclipse plugin: 24 | - Go the the Help menu -> Install new software... 25 | - Add the site http://subclipse.tigris.org/update_1.6.x, select "Subclipse" and proceed. 26 | Then checkout the GeneaQuilts project: 27 | - Menu File -> New Project... -> SVN -> Checkout projects from SVN 28 | - Add the repository location http://scm.gforge.inria.fr/svn/geneaquilt 29 | - Select the directory trunk/geneaquilt and proceed. 30 | 31 | To compile GeneaQuilts under Eclipse, you need to install the Maven plugin: 32 | - Go to the help menu -> Install new software... 33 | - Add the site http://m2eclipse.sonatype.org/sites/m2e, select "Maven integration for Eclipse" and proceed. 34 | - Restart Eclipse. Geneaquilt should compile itself automatically. 35 | 36 | 37 | ## Command Line 38 | 39 | -------------------------------------------------------------------------------- /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/nodes/FamGeneration.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.nodes; 9 | 10 | import java.util.List; 11 | 12 | import edu.umd.cs.piccolo.PNode; 13 | import edu.umd.cs.piccolo.util.PBounds; 14 | import geneaquilt.data.Fam; 15 | import geneaquilt.utils.PiccoloUtils; 16 | 17 | /** 18 | * Class FamGeneration 19 | * 20 | * @author Jean-Daniel Fekete 21 | * @version $Revision$ 22 | */ 23 | public class FamGeneration extends PNode { 24 | 25 | /** 26 | * Creates a Generation from a layer of individuals 27 | * @param layer the layer 28 | */ 29 | public FamGeneration(List layer) { 30 | for (Fam f : layer) { 31 | addChild(f.getNode()); 32 | } 33 | setPaint(GraphicsConstants.FAM_GENERATION_COLOR); 34 | } 35 | /** 36 | * {@inheritDoc} 37 | */ 38 | @Override 39 | protected void layoutChildren() { 40 | double w = 0; 41 | double h = 0; 42 | for (Object o : getChildrenReference()) { 43 | PNode child = (PNode)o; 44 | // child.setGlobalTranslation( 45 | // new Point2D.Double( 46 | // getX()+w, 47 | // getY())); 48 | PiccoloUtils.setLocation(child, getX()+w, getY(), true); 49 | if (child.getVisible()) { 50 | PBounds b = child.getFullBoundsReference(); 51 | w += b.getWidth(); 52 | h = Math.max(h, b.getHeight()); 53 | } 54 | } 55 | setBounds(getX(), getY(), w, h); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/io/LayerWriter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.io; 9 | 10 | import geneaquilt.data.Network; 11 | import geneaquilt.data.Vertex; 12 | 13 | import java.io.IOException; 14 | import java.io.PrintWriter; 15 | 16 | /** 17 | * Class LayerWriter 18 | * 19 | * @author Jean-Daniel Fekete 20 | */ 21 | public class LayerWriter { 22 | Network network; 23 | 24 | /** 25 | * Creates a LayerWriter for a specified network 26 | * @param network the network 27 | */ 28 | public LayerWriter(Network network) { 29 | this.network = network; 30 | } 31 | 32 | /** 33 | * Writes the layers into a specified file 34 | * @param filename the file name 35 | * @throws IOException if a writing error occurs 36 | */ 37 | public void write(String filename) throws IOException { 38 | PrintWriter out = new PrintWriter(filename); 39 | try { 40 | write(out); 41 | } 42 | finally { 43 | out.close(); 44 | } 45 | } 46 | 47 | /** 48 | * Write the layers into a specified PrintWriter. 49 | * @param out the writer 50 | * @throws IOException if a writing error occurs 51 | */ 52 | public void write(PrintWriter out) throws IOException { 53 | for (Vertex v : network.getVertices()) { 54 | out.print(v.getId()); 55 | out.print(" "); 56 | out.print(v.getX()); 57 | out.print(" "); 58 | out.print(v.getLayer()); 59 | out.println(); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/event/DisabablePanEventHandler.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.event; 9 | 10 | import java.awt.geom.AffineTransform; 11 | 12 | import edu.umd.cs.piccolo.PCamera; 13 | import edu.umd.cs.piccolo.activities.PTransformActivity; 14 | import edu.umd.cs.piccolo.event.PInputEvent; 15 | import edu.umd.cs.piccolo.event.PPanEventHandler; 16 | import edu.umd.cs.piccolo.util.PBounds; 17 | 18 | /** 19 | * DisabablePanEventHandler is a pan event that can be disabled. 20 | * 21 | * @author Pierre Dragicevic 22 | */ 23 | public class DisabablePanEventHandler extends PPanEventHandler { 24 | 25 | private boolean enabled = true; 26 | 27 | /** 28 | * Sets the enabled state 29 | * @param enabled new state 30 | */ 31 | public void setEnabled(boolean enabled) { 32 | this.enabled = enabled; 33 | } 34 | 35 | /** 36 | * @return if the handler is enabled 37 | */ 38 | public boolean isEnabled() { 39 | return enabled; 40 | } 41 | 42 | protected void pan(PInputEvent e) { 43 | if (enabled) 44 | super.pan(e); 45 | } 46 | 47 | /** 48 | * {@inheritDoc} 49 | */ 50 | public void mouseClicked(PInputEvent event) { 51 | if (enabled && (event.getClickCount() == 2)) { 52 | PCamera camera = event.getCamera(); 53 | PBounds cb = camera.getBoundsReference(); 54 | AffineTransform t2 = AffineTransform.getTranslateInstance(-event.getPosition().getX() + cb.getWidth()/2, -event.getPosition().getY() + cb.getHeight()/2); 55 | PTransformActivity activity = camera.animateViewToTransform(t2, 250); 56 | activity.setSlowInSlowOut(false); 57 | } else { 58 | super.mouseClicked(event); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This software is licensed to You under the "Simplified BSD License". 2 | You may not use this software except in compliance with the License. 3 | You may obtain a copy of the License at 4 | 5 | http://www.opensource.org/licenses/bsd-license.php 6 | 7 | The legal text of the Simplified BSD License is appended below for reference: 8 | 9 | 10 | Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 11 | All rights reserved. 12 | 13 | Redistribution and use in source and binary forms, with or without 14 | modification, are permitted provided that the following conditions are met: 15 | 16 | * Redistributions of source code must retain the above copyright notice, 17 | this list of conditions and the following disclaimer. 18 | * Redistributions in binary form must reproduce the above copyright notice, 19 | this list of conditions and the following disclaimer in the documentation 20 | and/or other materials provided with the distribution. 21 | * Neither the name of the INRIA, nor 22 | the names of its contributors may be used to endorse or promote products 23 | derived from this software without specific prior written permission. 24 | 25 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 26 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 27 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 28 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 29 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 31 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 32 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 33 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 34 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 | -------------------------------------------------------------------------------- /src/data/x.ped: -------------------------------------------------------------------------------- 1 | 1 1 . . 1 2/ 2 3/ 3 1/ 1 2/ 2 2/ 2 1/ 1 2/ 2 3/ 3 4/ 4 2/ 2 92.259 2 | 1 2 . . 2 4/ 1 3/ 2 4/ 1 2/ 3 1/ 4 3/ 1 2/ 1 3/ 2 2/ 2 3/ 4 114.982 3 | 1 3 1 2 1 4/ 4 3/ 3 1/ 1 3/ 3 4/ 4 1/ 1 1/ 1 2/ 2 2/ 2 4/ 4 113.915 4 | 1 4 1 2 2 1/ 2 2/ 3 1/ 1 3/ 2 4/ 2 1/ 1 1/ 2 3/ 3 2/ 4 3/ 2 84.787 5 | 2 1 . . 1 1/ 1 1/ 1 3/ 3 2/ 2 3/ 3 3/ 3 3/ 3 2/ 2 4/ 4 1/ 1 97.429 6 | 2 2 . . 2 2/ 2 4/ 4 3/ 4 3/ 4 2/ 2 4/ 1 1/ 2 3/ 1 1/ 2 1/ 1 124.035 7 | 2 3 1 2 1 2/ 2 4/ 4 3/ 3 3/ 3 2/ 2 4/ 4 1/ 1 3/ 3 1/ 1 1/ 1 93.284 8 | 2 4 1 2 2 2/ 1 4/ 1 3/ 3 3/ 2 2/ 3 4/ 3 1/ 3 3/ 2 1/ 4 1/ 1 79.706 9 | 3 1 . . 1 3/ 3 1/ 1 1/ 1 1/ 1 2/ 2 3/ 3 2/ 2 4/ 4 4/ 4 2/ 2 115.540 10 | 3 2 . . 2 1/ 3 1/ 1 1/ 2 1/ 4 4/ 2 2/ 2 4/ 3 2/ 3 4/ 3 4/ 3 79.994 11 | 3 3 1 2 1 1/ 1 1/ 1 1/ 1 1/ 1 4/ 4 2/ 2 4/ 4 2/ 2 4/ 4 4/ 4 123.730 12 | 3 4 1 2 2 1/ 3 1/ 1 1/ 1 1/ 1 4/ 2 2/ 3 4/ 2 2/ 4 4/ 4 4/ 2 103.612 13 | 4 1 . . 1 2/ 2 4/ 4 4/ 4 4/ 4 2/ 2 2/ 2 4/ 4 2/ 2 1/ 1 2/ 2 93.574 14 | 4 2 . . 2 1/ 3 1/ 1 3/ 4 4/ 2 3/ 2 4/ 2 3/ 1 2/ 3 2/ 2 4/ 1 87.456 15 | 4 3 1 2 1 1/ 1 1/ 1 3/ 3 4/ 4 3/ 3 4/ 4 3/ 3 2/ 2 2/ 2 1/ 1 110.715 16 | 4 4 1 2 2 1/ 2 1/ 4 3/ 4 4/ 4 3/ 2 4/ 2 3/ 4 3/ 2 2/ 1 1/ 2 87.968 17 | 5 1 . . 1 4/ 4 4/ 4 3/ 3 4/ 4 2/ 2 2/ 2 3/ 3 3/ 3 4/ 4 4/ 4 96.340 18 | 5 2 . . 2 2/ 2 2/ 1 1/ 2 3/ 1 4/ 2 2/ 1 1/ 4 4/ 4 2/ 2 2/ 1 106.893 19 | 5 3 1 2 1 2/ 2 2/ 2 1/ 1 3/ 3 2/ 2 1/ 1 4/ 4 4/ 4 2/ 2 2/ 2 100.083 20 | 5 4 1 2 2 2/ 4 2/ 4 1/ 3 3/ 4 4/ 2 2/ 2 1/ 3 4/ 3 2/ 4 2/ 4 97.327 21 | end 22 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/utils/MulticolorStroke.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.utils; 9 | 10 | import java.awt.BasicStroke; 11 | import java.awt.Color; 12 | import java.awt.Graphics2D; 13 | import java.awt.Shape; 14 | import java.awt.Stroke; 15 | 16 | /** 17 | * This class allows to paint shapes using a stroke with cycling colors. 18 | * 19 | * @author dragice 20 | * 21 | */ 22 | public class MulticolorStroke { 23 | 24 | final Color[] colors; 25 | final Stroke[] strokes; 26 | final float spacing; 27 | final float width; 28 | 29 | /** 30 | * Creates a multicolor stroke. 31 | * 32 | * @param width 33 | * @param colors 34 | * @param spacing 35 | */ 36 | public MulticolorStroke(float width, Color[] colors, float spacing) { 37 | this.width = width; 38 | this.colors = colors; 39 | this.spacing = spacing; 40 | this.strokes = new Stroke[colors.length]; 41 | createStrokes(); 42 | } 43 | 44 | /** 45 | * Creates a regular single-color stroke. 46 | * 47 | * @param width 48 | * @param color 49 | */ 50 | public MulticolorStroke(float width, Color color) { 51 | this(width, new Color[]{color}, 0); 52 | } 53 | 54 | private void createStrokes() { 55 | if (colors.length == 1) { 56 | strokes[0] = new BasicStroke(width, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f); 57 | } else { 58 | float[] dash = new float[2]; 59 | dash[0] = spacing; 60 | dash[1] = spacing * (colors.length - 1); 61 | for (int i = 0; iBFSCycleFinder implements the algorithm to break cycles in graphs 18 | * described in: 19 | * Gansner, E. R., Koutsofios, E., North, S. C., and Vo, K. 1993. A 20 | * Technique for Drawing Directed Graphs. IEEE Trans. Softw. Eng. 19, 3 21 | * (Mar. 1993), 214-230 22 | * 23 | * @param the vertex class 24 | * @param the edge class 25 | * 26 | * @author Jean-Daniel Fekete 27 | * @version $Revision$ 28 | */ 29 | public class BFSCycleFinder { 30 | protected DirectedGraph graph; 31 | protected Set cycles; 32 | 33 | /** 34 | * Creates a BFSCycleFinder from the specified graph 35 | * and connected component. 36 | * @param graph the graph 37 | */ 38 | public BFSCycleFinder(DirectedGraph graph) { 39 | this.graph = graph; 40 | } 41 | 42 | /** 43 | * Finds all the cyclic edges on the specified weak connected component. 44 | * @param comp the component or null for the whole graph 45 | * @return a collection of edges to invert 46 | */ 47 | public Set findCycles(Collection comp) { 48 | if (comp == null) 49 | comp = graph.getVertices(); 50 | cycles = new HashSet(); 51 | Set mark = new HashSet(); 52 | Set onStack = new HashSet(); 53 | 54 | for (V v : comp) { 55 | dfs(v, mark, onStack); 56 | } 57 | return cycles; 58 | } 59 | 60 | private void dfs(V v, Set mark, Set onStack) { 61 | if (mark.contains(v)) 62 | return; 63 | mark.add(v); 64 | onStack.add(v); 65 | for (E e : graph.getOutEdges(v)) { 66 | V w = graph.getDest(e); 67 | if (onStack.contains(w)) { 68 | cycles.add(e); 69 | w = graph.getSource(e); 70 | } 71 | else { 72 | // mark.remove(w); 73 | dfs(w, mark, onStack); 74 | } 75 | } 76 | onStack.remove(v); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/utils/PrintUtilities.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.utils; 9 | 10 | import java.awt.*; 11 | import javax.swing.*; 12 | import java.awt.print.*; 13 | 14 | public class PrintUtilities implements Printable { 15 | private Component componentToBePrinted; 16 | 17 | public static void printComponent(Component c) { 18 | new PrintUtilities(c).print(); 19 | } 20 | 21 | public PrintUtilities(Component componentToBePrinted) { 22 | this.componentToBePrinted = componentToBePrinted; 23 | } 24 | 25 | public void print() { 26 | 27 | PageFormat pageFormat = new PageFormat(); 28 | Paper paper = new Paper(); 29 | double size = 1;// in multiples of A0 30 | paper.setSize(size * 45.4 * 72, size * 32.7 * 75); 31 | paper.setImageableArea(0, 0, paper.getWidth(), paper.getHeight()); 32 | pageFormat.setPaper(paper); 33 | pageFormat.setOrientation(PageFormat.LANDSCAPE); 34 | 35 | PrinterJob printJob = PrinterJob.getPrinterJob(); 36 | printJob.setPrintable(this); 37 | if (printJob.printDialog()) 38 | try { 39 | printJob.setPrintable(this, pageFormat); 40 | printJob.print(); 41 | } catch(PrinterException pe) { 42 | System.out.println("Error printing: " + pe); 43 | } 44 | } 45 | 46 | public int print(Graphics g, PageFormat pageFormat, int pageIndex) { 47 | 48 | System.err.println(pageFormat.getPaper().getWidth()); 49 | 50 | if (pageIndex > 0) { 51 | return(NO_SUCH_PAGE); 52 | } else { 53 | Graphics2D g2d = (Graphics2D)g; 54 | g2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY()); 55 | 56 | double dl = pageFormat.getImageableWidth(); 57 | double dh = pageFormat.getImageableHeight(); 58 | double scale = Math.min(dl / componentToBePrinted.getWidth(), dh / componentToBePrinted.getHeight()); 59 | g2d.scale(scale, scale); 60 | 61 | disableDoubleBuffering(componentToBePrinted); 62 | componentToBePrinted.paint(g2d); 63 | enableDoubleBuffering(componentToBePrinted); 64 | return(PAGE_EXISTS); 65 | } 66 | } 67 | 68 | public static void disableDoubleBuffering(Component c) { 69 | RepaintManager currentManager = RepaintManager.currentManager(c); 70 | currentManager.setDoubleBufferingEnabled(false); 71 | } 72 | 73 | public static void enableDoubleBuffering(Component c) { 74 | RepaintManager currentManager = RepaintManager.currentManager(c); 75 | currentManager.setDoubleBufferingEnabled(true); 76 | } 77 | } -------------------------------------------------------------------------------- /src/main/java/geneaquilt/nodes/TextOutlineManager.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.nodes; 9 | 10 | import java.awt.geom.Rectangle2D; 11 | 12 | import edu.umd.cs.piccolo.PNode; 13 | import edu.umd.cs.piccolo.util.PBounds; 14 | import edu.umd.cs.piccolo.util.PPaintContext; 15 | 16 | /** 17 | * This component redraws Indi labels that need to be redrawn on top 18 | * of selections, with a white outline so they are more readable. 19 | * 20 | * @author dragice 21 | * 22 | */ 23 | public class TextOutlineManager extends PNode { 24 | 25 | QuiltManager quilt; 26 | boolean enabled = false; 27 | 28 | public boolean isEnabled() { 29 | return enabled; 30 | } 31 | 32 | public void setEnabled(boolean enabled) { 33 | this.enabled = enabled; 34 | } 35 | 36 | public TextOutlineManager(QuiltManager quilt) { 37 | this.quilt = quilt; 38 | } 39 | 40 | public PBounds getFullBoundsReference() { 41 | return quilt.getFullBoundsReference(); 42 | } 43 | 44 | public PBounds getBoundsReference() { 45 | return quilt.getBoundsReference(); 46 | } 47 | 48 | 49 | /** 50 | * TODO: comment. 51 | */ 52 | protected void paint(PPaintContext paintContext) { 53 | 54 | if (!enabled) 55 | return; 56 | 57 | if (paintContext.getRenderQuality() == PPaintContext.HIGH_QUALITY_RENDERING && quilt.getSelectionManager().getSelections().size() > 0) { 58 | Rectangle2D paintclip = paintContext.getLocalClip(); 59 | PBounds selectionbounds = quilt.getSelectionManager().getHighlightManager().getFullBoundsReference(); 60 | if (selectionbounds.intersects(paintclip)) { 61 | paintContext.pushTransform(getTransform()); 62 | IndiGeneration generation; 63 | for (int g=0; g vertex; 30 | private Network net; 31 | 32 | /** 33 | * Creates a TESTReader 34 | */ 35 | public TESTReader() { 36 | } 37 | 38 | /** 39 | * Load the file. 40 | * @param filename the filename 41 | * @return the network or null 42 | */ 43 | public Network load(String filename) { 44 | try { 45 | URL url = GEDReader.class.getClassLoader().getResource(filename); 46 | InputStream bin = url.openStream(); 47 | BufferedReader in = new BufferedReader( 48 | new InputStreamReader(bin, "UTF-8")); 49 | 50 | net = new Network(); 51 | vertex = new HashMap(); 52 | 53 | String line; 54 | while ((line = in.readLine())!= null) { 55 | String[] fields = line.split(" "); 56 | Vertex v = findVertex(fields[0]); 57 | for (int i = 1; i < fields.length; i++) { 58 | Vertex other = findVertex(fields[i]); 59 | Edge e = new Edge(other.getId(), v.getId()); 60 | e.setFromVertex(other); 61 | e.setToVertex(v); 62 | net.addEdge(e, other, v); 63 | } 64 | } 65 | return net; 66 | 67 | } catch (Exception e) { 68 | e.printStackTrace(); 69 | return null; 70 | } 71 | } 72 | 73 | private Vertex findVertex(String id) { 74 | Vertex v = vertex.get(id); 75 | if (v == null) { 76 | if (id.startsWith("F")) { 77 | v = new Fam(); 78 | } 79 | else { 80 | Indi i = new Indi(); 81 | i.setName(id); 82 | v = i; 83 | } 84 | v.setId(id); 85 | vertex.put(id, v); 86 | net.addVertex(v); 87 | } 88 | return v; 89 | } 90 | 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/nodes/PFam.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.nodes; 9 | 10 | import java.awt.BasicStroke; 11 | import java.awt.Color; 12 | import java.awt.Graphics2D; 13 | 14 | import edu.umd.cs.piccolo.nodes.PText; 15 | import edu.umd.cs.piccolo.util.PPaintContext; 16 | import geneaquilt.Printer; 17 | import geneaquilt.data.Fam; 18 | import geneaquilt.data.Vertex; 19 | import geneaquilt.hull.HullBin; 20 | 21 | /** 22 | * PFam is a Piccolo object representing a Family. 23 | * 24 | * @author Jean-Daniel Fekete 25 | * @version $Revision$ 26 | */ 27 | public class PFam extends PText implements PVertex, HullBin { 28 | Fam fam; 29 | Color bordercolor; 30 | int hullBinIndex; 31 | static final BasicStroke STROKE = new BasicStroke(1); // FIXME 32 | 33 | 34 | /** 35 | * Creates a PFam from a specified Fam. 36 | * @param fam the Fam 37 | */ 38 | public PFam(Fam fam) { 39 | super(fam.getLabel()); 40 | this.fam = fam; 41 | setTextPaint(GraphicsConstants.FAM_COLOR); 42 | setFont(GraphicsConstants.FAM_FONT); 43 | setStrokePaint(GraphicsConstants.instance.famBorderColor()); 44 | } 45 | 46 | /** 47 | * @return the fam 48 | */ 49 | public Fam getFam() { 50 | return fam; 51 | } 52 | 53 | /** 54 | * {@inheritDoc} 55 | */ 56 | public Vertex getVertex() { 57 | return fam; 58 | } 59 | 60 | /** 61 | * {@inheritDoc} 62 | */ 63 | public int getHullBinIndex() { 64 | return hullBinIndex; 65 | } 66 | 67 | /** 68 | * {@inheritDoc} 69 | */ 70 | public void setHullBinIndex(int index) { 71 | hullBinIndex = index; 72 | } 73 | 74 | /** 75 | * Sets the stroke paint 76 | * @param bordercolor color 77 | */ 78 | public void setStrokePaint(Color bordercolor) { 79 | this.bordercolor = bordercolor; 80 | } 81 | 82 | @Override 83 | protected void paint(PPaintContext paintContext) { 84 | double save = getGreekThreshold(); 85 | try { 86 | if (Printer.isPrinting()) 87 | setGreekThreshold(0); 88 | super.paint(paintContext); 89 | if (bordercolor != null) { 90 | Graphics2D g = paintContext.getGraphics(); 91 | g.setColor(bordercolor); 92 | g.setStroke(STROKE); 93 | g.draw(getBoundsReference()); 94 | } 95 | } 96 | finally { 97 | if (Printer.isPrinting()) 98 | setGreekThreshold(save); 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/nodes/PEdge.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.nodes; 9 | 10 | import geneaquilt.data.Edge; 11 | import geneaquilt.data.Indi; 12 | import geneaquilt.data.Vertex; 13 | 14 | import java.awt.Polygon; 15 | import java.awt.geom.Ellipse2D; 16 | import java.awt.geom.Rectangle2D; 17 | 18 | /** 19 | * Class PEdge 20 | * 21 | * @author Jean-Daniel Fekete 22 | * @version $Revision$ 23 | */ 24 | public class PEdge extends PIsoShape { 25 | Edge edge; 26 | static final Ellipse2D.Double circle = new Ellipse2D.Double(1, 1, 8, 8); 27 | static final Rectangle2D.Double rectangle = new Rectangle2D.Double(1.5, 1.5, 7, 7); 28 | static final int[] tri_x = { 0, 5, 10}; 29 | static final int[] tri_y = { 10, 0, 10 }; 30 | static final Polygon triangle = new Polygon(tri_x, tri_y, 3); 31 | // static final PBounds rectangle = new PBounds(1.5, 1.5, 7, 7); 32 | 33 | /** 34 | * Creates a PEdge 35 | * @param edge 36 | */ 37 | public PEdge(Edge edge) { 38 | super("M".equals(edge.getSex()) ? rectangle : circle); 39 | setBounds(0, 0, 10, 10); 40 | this.edge = edge; 41 | setPaint(GraphicsConstants.instance.edgeColor()); 42 | } 43 | 44 | /** 45 | * @return the edge 46 | */ 47 | public Edge getEdge() { 48 | return edge; 49 | } 50 | 51 | // /** 52 | // * Indicate that the bounds are volatile for this group 53 | // */ 54 | // public boolean getBoundsVolatile() { 55 | // return true; 56 | // } 57 | 58 | /** 59 | * Computes the position according to the position of the 60 | * vertices. 61 | */ 62 | public void updateBounds() { 63 | double x; 64 | double y; 65 | double w; 66 | double h; 67 | Vertex from = edge.getFromVertex(); 68 | Vertex to = edge.getToVertex(); 69 | if (from instanceof Indi) { 70 | x = to.getNode().getFullBoundsReference().getX(); 71 | w = to.getNode().getFullBoundsReference().getWidth(); 72 | y = from.getNode().getFullBoundsReference().getY(); 73 | h = from.getNode().getFullBoundsReference().getHeight(); 74 | } 75 | else { 76 | x = from.getNode().getFullBoundsReference().getX(); 77 | w = from.getNode().getFullBoundsReference().getWidth(); 78 | y = to.getNode().getFullBoundsReference().getY(); 79 | h = to.getNode().getFullBoundsReference().getHeight(); 80 | } 81 | 82 | if (w <= 0) 83 | w = 0.0000000001; 84 | if (h <= 0) 85 | h = 0.0000000001; 86 | setBounds(x, y, w, h); 87 | setVisible(from.getNode().getVisible() && to.getNode().getVisible()); 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/PFilterAnimation.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt; 9 | 10 | import edu.umd.cs.piccolo.activities.PActivity; 11 | import edu.umd.cs.piccolo.activities.PInterpolatingActivity; 12 | 13 | /** 14 | * PFilterAnimation manages the animation of a filtered animation 15 | * 16 | * @author Pierre Dragicevic 17 | */ 18 | public class PFilterAnimation extends PInterpolatingActivity { 19 | 20 | private float source; 21 | private float destination; 22 | private Target target; 23 | private static PFilterAnimation currentAnimation = null; 24 | 25 | /** 26 | * Target Objects that want their color to be set by the color 27 | * activity must implement this interface. 28 | */ 29 | interface Target { 30 | void startFiltering(float destFilter); 31 | void setFilteringParameter(float filter); 32 | float getFilteringParameter(); 33 | void endFiltering(); 34 | } 35 | 36 | /** 37 | * Create a new PDOIAnimationActivity. 38 | *

39 | * @param duration the length of one loop of the activity 40 | * @param stepRate the amount of time between steps of the activity 41 | * @param aTarget the object that the activity will be applied to and where 42 | * the source state will be taken from. 43 | * @param aDestination the destination filtering state. 0 is unfiltered, 1 is filtered. 44 | */ 45 | public PFilterAnimation(long duration, long stepRate, Target aTarget, float aDestination) { 46 | super(duration, stepRate); 47 | target = aTarget; 48 | destination = aDestination; 49 | if (currentAnimation != null && currentAnimation != this && currentAnimation.isStepping()) 50 | currentAnimation.terminate(PActivity.TERMINATE_WITHOUT_FINISHING); 51 | setSlowInSlowOut(false); 52 | } 53 | 54 | protected boolean isAnimation() { 55 | return true; 56 | } 57 | 58 | /** 59 | * @return the destination filtering 60 | */ 61 | public float getDestinationFiltering() { 62 | return destination; 63 | } 64 | 65 | /** 66 | * Sets the destination filtering 67 | * @param newDestination the new value 68 | */ 69 | public void setDestinationFiltering(float newDestination) { 70 | destination = newDestination; 71 | } 72 | 73 | @Override 74 | protected void activityStarted() { 75 | source = target.getFilteringParameter(); 76 | target.startFiltering(destination); 77 | super.activityStarted(); 78 | currentAnimation = this; 79 | } 80 | 81 | @Override 82 | protected void activityFinished() { 83 | source = target.getFilteringParameter(); 84 | target.endFiltering(); 85 | super.activityFinished(); 86 | } 87 | 88 | /** 89 | * {@inheritDoc} 90 | */ 91 | public void setRelativeTargetValue(float zeroToOne) { 92 | super.setRelativeTargetValue(zeroToOne); 93 | float filteringParameter = source + (destination - source) * zeroToOne; 94 | target.setFilteringParameter(filteringParameter); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/nodes/GraphicsConstants.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.nodes; 9 | 10 | import java.awt.BasicStroke; 11 | import java.awt.Color; 12 | import java.awt.Font; 13 | import java.awt.Stroke; 14 | 15 | import edu.umd.cs.piccolo.util.PPaintContext; 16 | 17 | /** 18 | * GraphicsConstants defines graphic constants 19 | * 20 | * @author Pierre Dragicevic 21 | */ 22 | @SuppressWarnings("all") 23 | public class GraphicsConstants { 24 | 25 | public static GraphicsConstants instance = new GraphicsConstants(); 26 | 27 | public static final float MAIN_WINDOW_SIZE = 0.9f; // ratio between window size and screen size 28 | public static final int MAIN_WINDOW_MAX_WIDTH = 1200; // needed because very slow if too large 29 | public static final int MAIN_WINDOW_MAX_HEIGHT = 1000; // needed because very slow if too large 30 | public static final float BIRDSEYE_VIEW_SIZE = 0.4f; // ratio between birdseye window size and main window size 31 | 32 | public Color gridColor() { return new Color(0.75f, 0.75f, 0.75f, 1f); } 33 | public static final Color GRID_COLOR_SMALL = new Color(0.9f, 0.9f, 0.9f, 1f); 34 | public Stroke gridStroke() { return new BasicStroke(1); } 35 | public static final double CELL_SIZE = 14; // height of individual names, width of family names 36 | 37 | public static final Color INDI_COLOR = Color.BLACK; 38 | public static final Font INDI_FONT = new Font("Helvetica", Font.PLAIN, 12); 39 | public static final double INDI_LINE_SPACING = 0.85; 40 | 41 | public static final Color FAM_COLOR = Color.BLACK; 42 | public static final Font FAM_FONT = new Font("Helvetica", Font.PLAIN, 10); 43 | public Color famBorderColor() { return Color.WHITE; } 44 | 45 | public Color edgeColor() { return new Color(0.25f, 0.25f, 0.25f, 1f); } 46 | 47 | public double getScale(PPaintContext p) { return p.getScale(); } 48 | 49 | public static final Color FAM_GENERATION_COLOR = new Color(0.85f, 0.85f, 0.85f, 1f); 50 | public static final Color INDI_GENERATION_COLOR = new Color(0.75f, 0.75f, 0.75f, 1f); 51 | 52 | public static final float SELECTION_HIGHLIGHT_WIDTH = 3; 53 | public static final Stroke SELECTION_STROKE = new BasicStroke(SELECTION_HIGHLIGHT_WIDTH); 54 | public static final float PATH_HIGHLIGHT_WIDTH = 9; // FIXME 55 | public static final Stroke PATH_HIGHLIGHT_STROKE = new BasicStroke(PATH_HIGHLIGHT_WIDTH, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER); // FIXME 56 | public static final float PATH_HIGHLIGHT_MULTICOLOR_SPACING = 5; // in pixels 57 | public static final Color[] SELECTION_COLORS = new Color[] { 58 | new Color(1f, 0f, 0f), 59 | new Color(0.2f, 0.2f, 1f), 60 | new Color(0f, 0.8f, 0f), 61 | new Color(0.7f, 0.7f, 0f), 62 | new Color(0.7f, 0.0f, 0.7f), 63 | new Color(0.0f, 0.7f, 0.7f), 64 | new Color(0.4f, 0.4f, 0.4f), 65 | }; 66 | public static final double MULTICOLOR_STROKE_ZOOM_FACTOR = 0.6; 67 | 68 | public static final Color SMALL_TEXT_COLOR = new Color(0.8f, 0.8f, 0.8f, 1f); 69 | 70 | public static final BasicStroke NULL_WIDTH_STROKE = new BasicStroke(0); 71 | 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/selection/highlight/Highlight.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.selection.highlight; 9 | 10 | import java.awt.Color; 11 | import java.awt.Shape; 12 | import java.util.Collection; 13 | import java.util.Hashtable; 14 | import java.util.Set; 15 | 16 | import edu.umd.cs.piccolo.PNode; 17 | import geneaquilt.selection.Selection; 18 | 19 | /** 20 | * Highlight manages a highlight 21 | * 22 | * @author Pierre Dragicevic 23 | * @version $Revision$ 24 | */ 25 | public abstract class Highlight extends PNode { 26 | 27 | static Hashtable mixedColors = new Hashtable(); 28 | 29 | PNode from; 30 | PNode to; 31 | SelectionCombination selections = SelectionCombination.getEmptyInstance(); 32 | Shape shape = null; 33 | 34 | /** 35 | * Creates a highlight on one node, with no associated selection. 36 | * @param node 37 | */ 38 | public Highlight(PNode node) { 39 | this(node, null); 40 | } 41 | 42 | /** 43 | * Creates a highlight between two nodes, with no associated selection. 44 | * @param from the starting node 45 | * @param to the ending node 46 | */ 47 | public Highlight(PNode from, PNode to) { 48 | this.from = from; 49 | this.to = to; 50 | setPickable(false); 51 | updateShape(); 52 | } 53 | 54 | /** 55 | * Assign a selection to this highlight. Several selections can share the same highlight. 56 | * @param s 57 | */ 58 | public void addSelection(Selection s) { 59 | selections = selections.getInstanceWithSelection(s); 60 | // repaint(); 61 | } 62 | 63 | /** 64 | * Adds a collection of selections in the current hightlighted selection 65 | * @param selections the collection 66 | */ 67 | public void addSelections(Collection selections) { 68 | for (Selection s : selections) 69 | addSelection(s); 70 | } 71 | 72 | /** 73 | * Remove a selection from this highlight. 74 | * @param s 75 | */ 76 | public void removeSelection(Selection s) { 77 | selections = selections.getInstanceWithoutSelection(s); 78 | // repaint(); 79 | } 80 | 81 | /** 82 | * Tests if a specified selection is contained in this selection 83 | * @param s the selection 84 | * @return true/false 85 | */ 86 | public boolean containsSelection(Selection s) { 87 | return selections.contains(s); 88 | } 89 | 90 | /** 91 | * @return true if the selection is empty 92 | */ 93 | public boolean isEmpty() { 94 | return selections.isEmpty(); 95 | } 96 | 97 | /** 98 | * @return the selection set 99 | */ 100 | public Set getSelections() { 101 | return selections.getSelections(); 102 | } 103 | 104 | /** 105 | * @return the number of selections 106 | */ 107 | public int getSelectionCount() { 108 | return selections.getSelectionCount(); 109 | } 110 | 111 | 112 | /** 113 | * Computes the shape and bounds of the highlight according to the position of the object(s) to be highlighted. 114 | * Call this method whenever the bounds of the "from" or "to" node you passed to the constructor change. 115 | */ 116 | public abstract void updateShape(); 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/ConstraintViewport.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt; 9 | 10 | import java.beans.PropertyChangeEvent; 11 | import java.beans.PropertyChangeListener; 12 | import java.util.ArrayList; 13 | 14 | import edu.umd.cs.piccolo.PCanvas; 15 | import edu.umd.cs.piccolo.PNode; 16 | import edu.umd.cs.piccolo.util.PBounds; 17 | import geneaquilt.nodes.QuiltManager; 18 | 19 | /** 20 | * Class ConstraintViewport 21 | * 22 | * @author Jean-Daniel Fekete 23 | * @version $Revision$ 24 | */ 25 | public class ConstraintViewport implements PropertyChangeListener { 26 | PCanvas canvas; 27 | PNode root; 28 | ArrayList list = new ArrayList(); 29 | boolean enabled = true; 30 | 31 | /** 32 | * Creates a constraint viewport 33 | */ 34 | public ConstraintViewport() { 35 | } 36 | 37 | /** 38 | * Enables/disables the constraint on the viewport 39 | * @param enabled the state to set 40 | */ 41 | public void setEnabled(boolean enabled) { 42 | this.enabled = enabled; 43 | } 44 | 45 | /** 46 | * Connects to the canvas, looking at the specified root node 47 | * 48 | * @param canvas 49 | * the canvas holding the camera 50 | * @param root 51 | * the root node 52 | */ 53 | public void connect(PCanvas canvas, PNode root) { 54 | this.canvas = canvas; 55 | this.root = root; 56 | canvas.getCamera().addPropertyChangeListener(this); 57 | } 58 | 59 | /** 60 | * Disconnects. 61 | */ 62 | public void disconnect() { 63 | if (canvas == null) return; 64 | canvas.getCamera().removePropertyChangeListener(this); 65 | canvas = null; 66 | root = null; 67 | } 68 | 69 | /** 70 | * {@inheritDoc} 71 | */ 72 | public void propertyChange(PropertyChangeEvent ev) { 73 | maybeMoveView(); 74 | } 75 | 76 | protected void maybeMoveView() { 77 | if (!enabled) 78 | return; 79 | PBounds b = canvas.getCamera().getViewBounds(); 80 | list.clear(); 81 | root.findIntersectingNodes(b, list); 82 | if (list.size() < 2) { 83 | PBounds fb = root.getFullBounds(); 84 | fb.x = b.x; 85 | fb.width = b.width; 86 | list.clear(); 87 | root.findIntersectingNodes(fb, list); 88 | if (list.size() < 2) 89 | return; // nothing we can do 90 | PBounds stripBounds = new PBounds(); 91 | for (PNode n : list) { 92 | if (!(n instanceof QuiltManager)) { 93 | stripBounds.add(n.getBoundsReference()); 94 | } 95 | } 96 | if (b.getMaxY() < stripBounds.getMinY()) { 97 | // the view is below the visible portion 98 | b.y = stripBounds.getMinY(); 99 | canvas.getCamera().animateViewToPanToBounds(b, 200); 100 | } 101 | else if (b.getMinY() > stripBounds.getMaxY()) { 102 | b.y = stripBounds.getMaxY() - b.height; 103 | canvas.getCamera().animateViewToPanToBounds(b, 200); 104 | } 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/data/Fam.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.data; 9 | 10 | import edu.umd.cs.piccolo.PNode; 11 | import geneaquilt.nodes.PFam; 12 | 13 | import java.util.ArrayList; 14 | import java.util.Collections; 15 | import java.util.List; 16 | 17 | /** 18 | * Class Fam 19 | * 20 | * @author Jean-Daniel Fekete 21 | * @version $Revision$ 22 | */ 23 | public class Fam extends Vertex { 24 | private ArrayList chil; 25 | 26 | /** 27 | * Create a Fam. 28 | */ 29 | public Fam() { 30 | } 31 | 32 | /** 33 | * @return the label to use 34 | */ 35 | public String getLabel() { 36 | // return UNICODE_MARIAGE;//FIXME 37 | //return " "+UNICODE_MALE+UNICODE_FEMALE+" "; 38 | return " F "; 39 | } 40 | 41 | /** 42 | * {@inheritDoc} 43 | */ 44 | protected PNode createNode() { 45 | return new PFam(this); 46 | } 47 | 48 | /** 49 | * {@inheritDoc} 50 | */ 51 | @Override 52 | public String toString() { 53 | return "Fam["+getId()+"]"; 54 | } 55 | 56 | /** 57 | * @return the wife 58 | */ 59 | public String getWife() { 60 | return (String)getProperty("WIFE"); 61 | } 62 | 63 | /** 64 | * @param wife the wife to set 65 | */ 66 | public void setWife(String wife) { 67 | setProperty("WIFE", wife); 68 | } 69 | 70 | /** 71 | * @return the husb 72 | */ 73 | public String getHusb() { 74 | return (String)getProperty("HUSB"); 75 | } 76 | 77 | /** 78 | * @param husb the husb to set 79 | */ 80 | public void setHusb(String husb) { 81 | setProperty("HUSB", husb); 82 | } 83 | 84 | /** 85 | * @return the chil 86 | */ 87 | public List getChil() { 88 | if (chil == null) 89 | return Collections.EMPTY_LIST; 90 | return chil; 91 | } 92 | 93 | /** 94 | * @param chil the chil to set 95 | */ 96 | public void setChil(ArrayList chil) { 97 | this.chil = chil; 98 | } 99 | 100 | /** 101 | * Adds a child 102 | * @param c the child 103 | */ 104 | public void addChil(String c) { 105 | if (c == null) return; 106 | if (chil == null) { 107 | chil = new ArrayList(); 108 | } 109 | if (! chil.contains(c)) 110 | chil.add(c); 111 | } 112 | 113 | /** 114 | * @return the marriage date or null 115 | */ 116 | public DateRange getMarriage() { 117 | return (DateRange)getProperty("MARR.DATE"); 118 | } 119 | 120 | /** 121 | * Sets the marriage date. 122 | * @param date the date 123 | */ 124 | public void setMarriage(DateRange date) { 125 | setProperty("MARR.DATE", date); 126 | } 127 | 128 | /** 129 | * @return the marriage date as a date, never null, maybe invalid 130 | */ 131 | public DateRange findMarriage() { 132 | DateRange d = getMarriage(); 133 | if (d == null) { 134 | d = new DateRange(); 135 | d.clear(); 136 | setMarriage(d); 137 | } 138 | return d; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/algorithms/LayerClusterer.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.algorithms; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Collection; 12 | import java.util.List; 13 | import java.util.Map; 14 | import java.util.Set; 15 | 16 | import org.apache.commons.collections15.Transformer; 17 | import org.apache.commons.collections15.map.HashedMap; 18 | import edu.uci.ics.jung.algorithms.cluster.WeakComponentClusterer; 19 | import edu.uci.ics.jung.graph.DirectedGraph; 20 | import edu.uci.ics.jung.graph.Tree; 21 | 22 | /** 23 | * Class LayerClusterer 24 | * @param vertex class 25 | * @param edge class 26 | * 27 | * @author Jean-Daniel Fekete 28 | * @version $Revision$ 29 | */ 30 | public class LayerClusterer 31 | implements Transformer, List>> { 32 | Map rank; 33 | int maxRank; 34 | DirectedGraph graph; 35 | 36 | /** 37 | * {@inheritDoc} 38 | */ 39 | public List> transform(DirectedGraph graph) { 40 | WeakComponentClusterer clust = new WeakComponentClusterer(); 41 | Set> components = clust.transform(graph); 42 | ArrayList> res = new ArrayList>(); 43 | for (Set comp : components) { 44 | List> r = rank(graph, comp); 45 | for (int i = 0; i < r.size(); i++) { 46 | Set s = r.get(i); 47 | if (res.size() <= i) { 48 | res.add(s); 49 | } 50 | else { 51 | res.get(i).addAll(s); 52 | } 53 | } 54 | } 55 | 56 | return res; 57 | } 58 | 59 | /** 60 | * Assign ranks to a connected component 61 | * @param graph the graph 62 | * @param comp the component 63 | * @return an ordered list of vertices with the same rank 64 | */ 65 | public List> rank(DirectedGraph graph, Collection comp) { 66 | this.graph = graph; 67 | this.rank = new HashedMap(); 68 | this.maxRank = 0; 69 | //Tree tree = feasibleTree(comp); 70 | //TODO 71 | return null; 72 | } 73 | 74 | protected Tree feasibleTree(Collection comp) { 75 | return null; 76 | } 77 | 78 | protected void init_rank(Collection comp) { 79 | for (V v : comp) { 80 | if (graph.getSuccessorCount(v)==0) 81 | setRoot(v); 82 | } 83 | } 84 | 85 | private void setRoot(V v) { 86 | rank.put(v, new Integer(0)); 87 | assignRank(v, 0); 88 | } 89 | 90 | private void assignRank(V v, int vRank) { 91 | for (V child : graph.getPredecessors(v)) { 92 | Integer o = rank.get(child); 93 | int newRank; 94 | if (o != null) { 95 | int oldRank = o.intValue(); 96 | newRank = Math.max(oldRank, vRank+1); 97 | } 98 | else 99 | newRank = vRank+1; 100 | 101 | rank.put(child, new Integer(newRank)); 102 | if (newRank > maxRank) 103 | maxRank = newRank; 104 | assignRank(child, newRank); 105 | } 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/data/Simpsons.ged: -------------------------------------------------------------------------------- 1 | 0 HEAD 2 | 1 SOUR GRAMPS 3 | 2 VERS 2.2.6-1 4 | 2 NAME GRAMPS 5 | 1 DEST GEDCOM 5.5 6 | 1 DATE 9 MAR 2007 7 | 1 CHAR UTF-8 8 | 1 SUBM @SUBM@ 9 | 1 FILE /home/bodon/dok/gramps_data/Untitled_1.ged 10 | 1 COPR Copyright (c) 2007 . 11 | 1 GEDC 12 | 2 VERS 5.5 13 | 2 FORM LINEAGE-LINKED 14 | 0 @SUBM@ SUBM 15 | 1 NAME Not Provided 16 | 1 ADDR Not Provided 17 | 2 CONT Not Provided 18 | 0 @Abraham_Simpson@ INDI 19 | 1 NAME Abraham /Simpson/ 20 | 2 GIVN Abraham 21 | 2 SURN Simpson 22 | 1 SEX M 23 | 1 FAMS @F0002@ 24 | 1 CHAN 25 | 2 DATE 11 FEB 2007 26 | 3 TIME 15:05:36 27 | 0 @Bart_Simpson@ INDI 28 | 1 NAME Bart /Simpson/ 29 | 2 GIVN Bart 30 | 2 SURN Simpson 31 | 1 SEX M 32 | 1 FAMC @F0000@ 33 | 1 OBJE 34 | 2 FORM URL 35 | 2 FILE http://en.wikipedia.org/wiki/Bart_Simpson 36 | 1 CHAN 37 | 2 DATE 11 FEB 2007 38 | 3 TIME 15:04:14 39 | 0 @Clancy_Bouvier@ INDI 40 | 1 NAME Clancy /Bouvier/ 41 | 2 GIVN Clancy 42 | 2 SURN Bouvier 43 | 1 SEX M 44 | 1 FAMS @F0003@ 45 | 1 CHAN 46 | 2 DATE 11 FEB 2007 47 | 3 TIME 15:13:45 48 | 0 @Homer_Simpson@ INDI 49 | 1 NAME Homer /Simpson/ 50 | 2 GIVN Homer 51 | 2 SURN Simpson 52 | 1 SEX M 53 | 1 FAMC @F0002@ 54 | 1 FAMS @F0000@ 55 | 1 OBJE 56 | 2 FORM URL 57 | 2 FILE http://en.wikipedia.org/wiki/Homer_Simpson 58 | 1 OBJE 59 | 2 FORM URL 60 | 2 FILE safety.officer@springfieldnuclear.com 61 | 1 CHAN 62 | 2 DATE 11 FEB 2007 63 | 3 TIME 15:05:36 64 | 0 @Jacqueline_Bouvier@ INDI 65 | 1 NAME Jacqueline /Bouvier/ 66 | 2 GIVN Jacqueline 67 | 2 SURN Bouvier 68 | 1 SEX F 69 | 1 FAMS @F0003@ 70 | 1 CHAN 71 | 2 DATE 11 FEB 2007 72 | 3 TIME 15:13:45 73 | 0 @Lisa_Simpson@ INDI 74 | 1 NAME Lisa /Simpson/ 75 | 2 GIVN Lisa 76 | 2 SURN Simpson 77 | 1 SEX F 78 | 1 FAMC @F0000@ 79 | 1 OBJE 80 | 2 FORM URL 81 | 2 FILE lisa@springfieldhigh.edu 82 | 1 CHAN 83 | 2 DATE 11 FEB 2007 84 | 3 TIME 15:04:14 85 | 0 @Maggie_Simpson@ INDI 86 | 1 NAME Maggie /Simpson/ 87 | 2 GIVN Maggie 88 | 2 SURN Simpson 89 | 1 SEX F 90 | 1 FAMC @F0000@ 91 | 1 OBJE 92 | 2 FORM URL 93 | 2 FILE http://en.wikipedia.org/wiki/Maggie_Simpson 94 | 1 CHAN 95 | 2 DATE 11 FEB 2007 96 | 3 TIME 15:04:14 97 | 0 @Marge_Simpson@ INDI 98 | 1 NAME Marge /Simpson/ 99 | 2 GIVN Marge 100 | 2 SURN Simpson 101 | 1 SEX F 102 | 1 FAMC @F0003@ 103 | 1 FAMS @F0000@ 104 | 1 OBJE 105 | 2 FORM URL 106 | 2 FILE http://en.wikipedia.org/wiki/Marge_Simpson 107 | 1 CHAN 108 | 2 DATE 11 FEB 2007 109 | 3 TIME 15:13:45 110 | 0 @Mona_Simpson@ INDI 111 | 1 NAME Mona /Simpson/ 112 | 2 GIVN Mona 113 | 2 SURN Simpson 114 | 1 SEX F 115 | 1 FAMS @F0002@ 116 | 1 CHAN 117 | 2 DATE 11 FEB 2007 118 | 3 TIME 15:05:36 119 | 0 @Patty_Bouvier@ INDI 120 | 1 NAME Patty /Bouvier/ 121 | 2 GIVN Patty 122 | 2 SURN Bouvier 123 | 1 SEX F 124 | 1 FAMC @F0003@ 125 | 1 OBJE 126 | 2 FORM URL 127 | 2 FILE http://en.wikipedia.org/wiki/Patty_and_Selma_Bouvier 128 | 1 CHAN 129 | 2 DATE 11 FEB 2007 130 | 3 TIME 15:13:45 131 | 0 @Selma_Bouvier@ INDI 132 | 1 NAME Selma /Bouvier/ 133 | 2 GIVN Selma 134 | 2 SURN Bouvier 135 | 1 SEX F 136 | 1 FAMC @F0003@ 137 | 1 OBJE 138 | 2 FORM URL 139 | 2 FILE http://en.wikipedia.org/wiki/Patty_and_Selma_Bouvier 140 | 1 CHAN 141 | 2 DATE 11 FEB 2007 142 | 3 TIME 15:13:45 143 | 0 @F0000@ FAM 144 | 1 REFN 0 145 | 1 HUSB @Homer_Simpson@ 146 | 1 WIFE @Marge_Simpson@ 147 | 1 CHIL @Bart_Simpson@ 148 | 1 CHIL @Maggie_Simpson@ 149 | 1 CHIL @Lisa_Simpson@ 150 | 1 CHAN 151 | 2 DATE 11 FEB 2007 152 | 3 TIME 15:04:14 153 | 0 @F0002@ FAM 154 | 1 REFN 2 155 | 1 HUSB @Abraham_Simpson@ 156 | 1 WIFE @Mona_Simpson@ 157 | 1 CHIL @Homer_Simpson@ 158 | 1 CHAN 159 | 2 DATE 11 FEB 2007 160 | 3 TIME 15:05:36 161 | 0 @F0003@ FAM 162 | 1 REFN 3 163 | 1 HUSB @Clancy_Bouvier@ 164 | 1 WIFE @Jacqueline_Bouvier@ 165 | 1 CHIL @Patty_Bouvier@ 166 | 1 CHIL @Selma_Bouvier@ 167 | 1 CHIL @Marge_Simpson@ 168 | 1 CHAN 169 | 2 DATE 11 FEB 2007 170 | 3 TIME 15:13:45 171 | 0 TRLR 172 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/data/Edge.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.data; 9 | 10 | import geneaquilt.nodes.PEdge; 11 | 12 | 13 | /** 14 | * Class Edge 15 | * 16 | * @author Jean-Daniel Fekete 17 | * @version $Revision$ 18 | */ 19 | public class Edge { 20 | private PEdge node; 21 | private String from; 22 | private String to; 23 | private Vertex fromVertex; 24 | private Vertex toVertex; 25 | 26 | /** 27 | * Creates an edge with the name of the from and to vertices. 28 | * @param from the from vertex name 29 | * @param to the to vertex name 30 | */ 31 | public Edge(String from, String to) { 32 | this.from = from; 33 | this.to = to; 34 | } 35 | 36 | 37 | /** 38 | * @return the node 39 | */ 40 | public PEdge getNode() { 41 | if (node == null) 42 | node = createNode(); 43 | return node; 44 | } 45 | 46 | protected PEdge createNode() { 47 | return new PEdge(this); 48 | } 49 | 50 | /** 51 | * Unreference the node 52 | */ 53 | public void deleteNode() { 54 | node = null; 55 | } 56 | 57 | /** 58 | * @return the from 59 | */ 60 | public String getFrom() { 61 | return from; 62 | } 63 | 64 | /** 65 | * @param from the from to set 66 | */ 67 | public void setFrom(String from) { 68 | this.from = from; 69 | } 70 | 71 | /** 72 | * @return the to 73 | */ 74 | public String getTo() { 75 | return to; 76 | } 77 | 78 | /** 79 | * @param to the to to set 80 | */ 81 | public void setTo(String to) { 82 | this.to = to; 83 | } 84 | 85 | 86 | /** 87 | * {@inheritDoc} 88 | */ 89 | @Override 90 | public boolean equals(Object obj) { 91 | return obj == this 92 | || (obj != null 93 | && obj instanceof Edge 94 | && ((Edge)obj).from.equals(from) 95 | && ((Edge)obj).to.equals(to)); 96 | } 97 | 98 | /** 99 | * @return the fromVertex 100 | */ 101 | public Vertex getFromVertex() { 102 | return fromVertex; 103 | } 104 | 105 | /** 106 | * @param fromVertex the fromVertex to set 107 | */ 108 | public void setFromVertex(Vertex fromVertex) { 109 | this.fromVertex = fromVertex; 110 | } 111 | 112 | /** 113 | * @return the sex of either of the vertices 114 | */ 115 | public String getSex() { 116 | String s = fromVertex.getStringProperty("SEX"); 117 | if (s != null) 118 | return s; 119 | return toVertex.getStringProperty("SEX"); 120 | } 121 | 122 | /** 123 | * @return the toVertex 124 | */ 125 | public Vertex getToVertex() { 126 | return toVertex; 127 | } 128 | 129 | /** 130 | * @param toVertex the toVertex to set 131 | */ 132 | public void setToVertex(Vertex toVertex) { 133 | this.toVertex = toVertex; 134 | } 135 | 136 | /** 137 | * {@inheritDoc} 138 | */ 139 | @Override 140 | public int hashCode() { 141 | return from.hashCode() + 31*to.hashCode(); 142 | } 143 | 144 | /** 145 | * {@inheritDoc} 146 | */ 147 | @Override 148 | public String toString() { 149 | return "Edge["+from+"->"+to+"]"; 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/selection/MouseSelectionController.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.selection; 9 | import edu.umd.cs.piccolo.PNode; 10 | import edu.umd.cs.piccolo.event.PBasicInputEventHandler; 11 | import edu.umd.cs.piccolo.event.PInputEvent; 12 | import edu.umd.cs.piccolo.util.PBounds; 13 | import edu.umd.cs.piccolo.util.PPickPath; 14 | import geneaquilt.event.DisabablePanEventHandler; 15 | import geneaquilt.nodes.QuiltManager; 16 | 17 | /** 18 | * MouseSelectionController implements the selection 19 | * and Pan. 20 | * 21 | * @author Pierre Dragicevic 22 | */ 23 | public class MouseSelectionController extends PBasicInputEventHandler { 24 | protected final QuiltManager manager; 25 | protected final DisabablePanEventHandler panEventHandler; 26 | protected Selection currentSelection = null; 27 | protected boolean selectionCreated = false; 28 | protected boolean selectionEnabled = true; 29 | 30 | /** 31 | * Creates a controller on a quilt manager and a pan event handler 32 | * @param manager the quilt manager 33 | * @param panEventHandler the pan event handler 34 | */ 35 | public MouseSelectionController(QuiltManager manager, DisabablePanEventHandler panEventHandler) { 36 | this.manager = manager; 37 | this.panEventHandler = panEventHandler; 38 | } 39 | 40 | /** 41 | * {@inheritDoc} 42 | */ 43 | @Override 44 | public void mousePressed(PInputEvent event) { 45 | currentSelection = null; 46 | selectionCreated = false; 47 | updateSelection(event); 48 | 49 | if (currentSelection != null) { 50 | panEventHandler.setEnabled(false); 51 | } 52 | } 53 | 54 | /** 55 | * {@inheritDoc} 56 | */ 57 | @Override 58 | public void mouseDragged(PInputEvent event) { 59 | if (currentSelection != null) { 60 | updateSelection(event); 61 | } 62 | } 63 | 64 | // Like event.getPickedNode() but does not grab objects during drags 65 | protected PNode getPickedNode(PInputEvent event) { 66 | PPickPath picked = new PPickPath(event.getCamera(), new PBounds(event.getPosition().getX(), event.getPosition().getY(), 1, 1)); 67 | manager.fullPick(picked); 68 | return picked.getPickedNode(); 69 | } 70 | 71 | protected void updateSelection(PInputEvent event) { 72 | if (event.isLeftMouseButton()) { 73 | 74 | PNode node = getPickedNode(event); 75 | 76 | SelectionManager selections = manager.getSelectionManager(); 77 | 78 | // Drag an existing selection? 79 | if (currentSelection == null && selections.isSelected(node)) { 80 | currentSelection = selections.getLastSelection(node); 81 | } 82 | 83 | if (selections.isSelectable(node)) { 84 | if (currentSelection == null) { 85 | if (!event.isShiftDown()) 86 | selections.clearSelections(); 87 | currentSelection = selections.select(node); 88 | selectionCreated = true; 89 | } else { 90 | currentSelection.setSelectedObject(node); 91 | } 92 | } 93 | } 94 | } 95 | 96 | /** 97 | * {@inheritDoc} 98 | */ 99 | @Override 100 | public void mouseReleased(PInputEvent event) { 101 | if (currentSelection != null) { 102 | panEventHandler.setEnabled(true); 103 | } 104 | } 105 | 106 | /** 107 | * {@inheritDoc} 108 | */ 109 | @Override 110 | public void mouseClicked(PInputEvent event) { 111 | if (currentSelection == null && event.isLeftMouseButton() && !event.isShiftDown()) { 112 | SelectionManager selections = manager.getSelectionManager(); 113 | selections.clearSelections(); 114 | } else if (currentSelection != null && !selectionCreated && !event.isShiftDown()) { 115 | currentSelection.toggleHighlightMode(); 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/nodes/IndiGeneration.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.nodes; 9 | 10 | import java.awt.Graphics2D; 11 | import java.util.List; 12 | 13 | import edu.umd.cs.piccolo.PNode; 14 | import edu.umd.cs.piccolo.util.PBounds; 15 | import edu.umd.cs.piccolo.util.PPaintContext; 16 | import geneaquilt.Printer; 17 | import geneaquilt.data.Indi; 18 | import geneaquilt.hull.HullBin; 19 | import geneaquilt.utils.PiccoloUtils; 20 | import geneaquilt.utils.PrintConstants; 21 | 22 | /** 23 | * An IndiGeneration is a container that lays out its 24 | * children (Indi) in a vertical fashion and draws an horizontal 25 | * grid that spans over all the families 26 | * 27 | * @author Jean-Daniel Fekete 28 | * @version $Revision$ 29 | */ 30 | public class IndiGeneration extends PNode implements HullBin { 31 | 32 | int hullBinIndex; 33 | 34 | /** 35 | * Creates a Generation from a layer of individuals 36 | * @param layer the layer 37 | */ 38 | public IndiGeneration(List layer) { 39 | for (Indi i : layer) { 40 | addChild(i.getNode()); 41 | } 42 | //setPaint(Color.LIGHT_GRAY); 43 | } 44 | /** 45 | * {@inheritDoc} 46 | */ 47 | @Override 48 | protected void layoutChildren() { 49 | final double lineSpacing = GraphicsConstants.INDI_LINE_SPACING; 50 | double h = 0; 51 | double w = 0; 52 | for (Object o : getChildrenReference()) { 53 | PNode child = (PNode)o; 54 | // child.setGlobalTranslation( 55 | // new Point2D.Double( 56 | // getX(), 57 | // getY()+h)); 58 | PiccoloUtils.setLocation(child, getX(), getY()+h, true); 59 | if (child.getVisible()) { 60 | PBounds b = child.getFullBoundsReference(); 61 | h += b.getHeight() * lineSpacing; 62 | w = Math.max(w, b.getWidth()); 63 | if (child instanceof PSemanticText) 64 | ((PSemanticText)child).recomputeLayout(); 65 | } 66 | } 67 | setBounds(getX(), getY(), w/2, h/2); 68 | } 69 | 70 | /** 71 | * {@inheritDoc} 72 | */ 73 | public void fullPaint(PPaintContext paintContext) { 74 | 75 | if (!getVisible() || !fullIntersects(paintContext.getLocalClip())) 76 | return; 77 | 78 | // -- Paint in normal scale 79 | 80 | if (Printer.isPrinting() || PrintConstants.instance.getScale(paintContext) > 0.1) { 81 | super.fullPaint(paintContext); 82 | return; 83 | } 84 | 85 | // -- Paint in small scale 86 | 87 | // paintContext.pushTransform(getTransform()); 88 | paintContext.pushTransparency(getTransparency()); 89 | 90 | Graphics2D g = paintContext.getGraphics(); 91 | g.setColor(GraphicsConstants.INDI_GENERATION_COLOR); 92 | g.fill(getBoundsReference()); 93 | 94 | // paint only highlighted labels (string search) 95 | for (Object o : getChildrenReference()) { 96 | PNode n = (PNode)o; 97 | if (n instanceof PSemanticText && ((PSemanticText)n).getPaint() != null) { 98 | n.fullPaint(paintContext); 99 | } 100 | } 101 | 102 | paintContext.popTransparency(getTransparency()); 103 | // paintContext.popTransform(getTransform()); 104 | } 105 | 106 | /** 107 | * {@inheritDoc} 108 | */ 109 | public int getHullBinIndex() { 110 | return hullBinIndex; 111 | } 112 | 113 | /** 114 | * {@inheritDoc} 115 | */ 116 | public void setHullBinIndex(int index) { 117 | hullBinIndex = index; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/nodes/PIsoShape.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.nodes; 9 | 10 | import java.awt.Graphics2D; 11 | import java.awt.Shape; 12 | import java.awt.geom.Ellipse2D; 13 | import java.awt.geom.Rectangle2D; 14 | 15 | import edu.umd.cs.piccolo.PNode; 16 | import edu.umd.cs.piccolo.util.PAffineTransform; 17 | import edu.umd.cs.piccolo.util.PBounds; 18 | import edu.umd.cs.piccolo.util.PPaintContext; 19 | import geneaquilt.Printer; 20 | import geneaquilt.utils.PrintConstants; 21 | 22 | /** 23 | * PIsoShape show a shape centered and scaled to 24 | * fit the specified bounds. The scale is the same in X and Y. 25 | * 26 | * @author Jean-Daniel Fekete 27 | * @version $Revision$ 28 | */ 29 | public class PIsoShape extends PNode { 30 | private Shape shape; 31 | 32 | /** 33 | * Circle shape 34 | */ 35 | public static final Shape CIRCLE = new Ellipse2D.Double(0, 0, 10, 10); 36 | 37 | /** 38 | * Creates a PIsoShape with the specified shape. 39 | * 40 | * IMPORTANT: The shape bounds must be within (0, 0, 10, 10). This allows to specify margins. 41 | * @param shape the shape 42 | */ 43 | public PIsoShape(Shape shape) { 44 | this.shape = shape; 45 | } 46 | 47 | /** 48 | * @return the shape 49 | */ 50 | public Shape getShape() { 51 | return shape; 52 | } 53 | 54 | /** 55 | * {@inheritDoc} 56 | */ 57 | @Override 58 | protected void paint(PPaintContext paintContext) { 59 | if (getPaint() != null) { 60 | final Graphics2D g2 = paintContext.getGraphics(); 61 | g2.setPaint(getPaint()); 62 | paintIsoShape(paintContext, shape, getBoundsReference()); 63 | } 64 | } 65 | 66 | /** 67 | * Temporary fix. This allows PathHighlight to render on top of this node using the same shape. 68 | * @param paintContext the paint context 69 | * @param shape the shape to paint 70 | * @param b the bounding box where the shape should be drawn 71 | */ 72 | public static void paintIsoShape(PPaintContext paintContext, Shape shape, PBounds b) { 73 | Rectangle2D sb = shape.getBounds2D(); 74 | double w = Math.min(b.getWidth(), b.getHeight()); 75 | double minSize 76 | = paintContext.getRenderQuality() == PPaintContext.LOW_QUALITY_RENDERING 77 | ? 1 : 0.25f; 78 | 79 | // don't draw edges in small scales. This will accelerate the rendering of 80 | // the overview. 81 | if (Printer.isPrinting() || PrintConstants.instance.getScale(paintContext) * w > minSize) { 82 | PAffineTransform at = new PAffineTransform(); 83 | 84 | double sx = b.getWidth() / 10;//sb.getWidth(); 85 | double sy = b.getHeight() / 10;//sb.getHeight(); 86 | double s = Math.min(sx, sy); 87 | // at.scaleAboutPoint(s, b.getCenterX(), b.getCenterY()); 88 | at.translate(b.getCenterX(), b.getCenterY()); 89 | at.scale(s, s); 90 | at.translate(-sb.getCenterX(), -sb.getCenterY()); 91 | 92 | if (Printer.isPrinting()) { 93 | Graphics2D g2 = (Graphics2D)paintContext.getGraphics(); 94 | g2 = (Graphics2D)g2.create(); 95 | g2.transform(at); 96 | g2.fill(shape); 97 | g2.dispose(); 98 | } 99 | else { 100 | paintContext.pushTransform(at); 101 | paintContext.getGraphics().fill(shape); 102 | paintContext.popTransform(at); 103 | } 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/io/LayersReader.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.io; 9 | 10 | import geneaquilt.data.Network; 11 | import geneaquilt.data.Vertex; 12 | 13 | import java.io.BufferedReader; 14 | import java.io.File; 15 | import java.io.FileNotFoundException; 16 | import java.io.FileReader; 17 | import java.util.Arrays; 18 | import java.util.HashMap; 19 | import java.util.TreeSet; 20 | import java.util.Map.Entry; 21 | 22 | import org.apache.log4j.Logger; 23 | 24 | /** 25 | * Class LayersReader 26 | * 27 | * @author Jean-Daniel Fekete 28 | * @version $Revision$ 29 | */ 30 | public class LayersReader { 31 | private static final Logger LOG = Logger.getLogger(LayersReader.class); 32 | 33 | /** 34 | * Returns true if the layer file exists. 35 | * @param basefile 36 | * @return true if the layer file exists 37 | */ 38 | public boolean layerFileExists(String basefile) { 39 | int index = basefile.lastIndexOf('.'); 40 | if (index == -1) { 41 | LOG.warn("No file extension"); 42 | return false; 43 | } 44 | String layername = basefile.substring(0, index)+".lyr"; 45 | return (new File(layername)).exists(); 46 | } 47 | 48 | /** 49 | * Reads a layer file when it exists, associating a layer 50 | * and X position to each vertex. 51 | * @param basefile the GED file name 52 | * @param network the network 53 | * @return true if it has been loaded, false otherwise 54 | */ 55 | public boolean load(String basefile, Network network) { 56 | int index = basefile.lastIndexOf('.'); 57 | if (index == -1) { 58 | LOG.warn("No file extension"); 59 | return false; 60 | } 61 | String layername = basefile.substring(0, index)+".lyr"; 62 | FileReader fin = null; 63 | BufferedReader in = null; 64 | try { 65 | fin = new FileReader(layername); 66 | in = new BufferedReader(fin); 67 | 68 | String line; 69 | TreeSet ranks = new TreeSet(); 70 | HashMap vertexY = new HashMap(); 71 | while ((line = in.readLine()) != null) { 72 | String fields[] = line.split(" "); 73 | String id = fields[0]; 74 | if (id.startsWith("\"") && id.endsWith("\"")) { 75 | id = id.substring(1, id.length()-1); 76 | } 77 | double x = Double.parseDouble(fields[1]); 78 | Double y = Double.valueOf(fields[2]); 79 | Vertex v = network.getVertex(id); 80 | if (v != null) { 81 | v.setX(x); 82 | vertexY.put(v, y); 83 | ranks.add(y); 84 | } 85 | else { 86 | LOG.warn("Cannot find vertex with id="+id); 87 | } 88 | } 89 | double[] r = new double[ranks.size()]; 90 | int i = 0; 91 | for (Double d : ranks) { 92 | r[i++] = d.doubleValue(); 93 | } 94 | for (Entry e : vertexY.entrySet()) { 95 | int l = Arrays.binarySearch(r, e.getValue().doubleValue()); 96 | if (l < 0) { 97 | LOG.error("Unexpected layer not found for "+e.getValue()); 98 | l = -l+1; 99 | } 100 | network.setVertexLayer(e.getKey(), l); 101 | } 102 | // network.fixLayers(); 103 | in.close(); 104 | fin.close(); 105 | } 106 | catch (FileNotFoundException e) { 107 | LOG.warn("Layer file does not exist"); 108 | return false; 109 | } catch(Exception e) { 110 | LOG.info("Cannot open layer file", e); 111 | return false; 112 | } 113 | return true; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/utils/Benchmark.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.utils; 9 | 10 | import edu.umd.cs.piccolo.PCanvas; 11 | import edu.umd.cs.piccolo.PNode; 12 | import geneaquilt.BirdsEyeView; 13 | import geneaquilt.GeneaQuilt; 14 | import geneaquilt.data.Vertex; 15 | import geneaquilt.nodes.QuiltManager; 16 | import geneaquilt.selection.SelectionManager; 17 | 18 | import java.util.Collection; 19 | import java.util.regex.Pattern; 20 | 21 | /** 22 | * Computes a Benchmark 23 | * 24 | * @author Pierre Dragicevic 25 | */ 26 | public class Benchmark { 27 | 28 | static String taskname = null; 29 | static long t0 = 0; 30 | 31 | /** 32 | * 33 | * This class is used to compare the performance of different rendering methods. 34 | * 35 | * @param args 36 | */ 37 | public static void main(String[] args) { 38 | 39 | // beginTask("load"); 40 | GeneaQuilt.main(new String[]{"data/royal92.ged"}); 41 | // endTask(); 42 | 43 | GeneaQuilt.getFrame().setLocation(GeneaQuilt.getFrame().getX(), 0); 44 | 45 | final Thread t = new Thread() { 46 | public void run() { 47 | doBenchmark(); 48 | } 49 | }; 50 | t.start(); 51 | } 52 | 53 | static void doBenchmark() { 54 | 55 | wait(2); 56 | 57 | // JFrame frame = GeneaQuilt.getFrame(); 58 | GeneaQuilt quilt = GeneaQuilt.getQuilt(); 59 | QuiltManager root = quilt.getManager(); 60 | SelectionManager selection = root.getSelectionManager(); 61 | PCanvas canvas = quilt.getCanvas(); 62 | BirdsEyeView bev = quilt.getBev(); 63 | 64 | // beginTask("search 1"); 65 | Collection v = quilt.search("I2018", "ID", Pattern.LITERAL | Pattern.CASE_INSENSITIVE); 66 | PNode n = v.iterator().next().getNode(); 67 | quilt.search("", "", Pattern.LITERAL); 68 | // endTask(); 69 | 70 | wait(6); 71 | 72 | beginTask("select 1"); 73 | // Selection s = selection.select(n); 74 | endTask(); 75 | 76 | wait(1); 77 | 78 | /*beginTask("highlight 1"); 79 | s.highlightPredecessors(); 80 | s.highlightSuccessors(); 81 | s.highlightSelection(); 82 | frame.repaint(); 83 | endTask();*/ 84 | 85 | wait(2); 86 | 87 | // beginTask("search 2"); 88 | v = quilt.search("I2961", "ID", Pattern.LITERAL | Pattern.CASE_INSENSITIVE); 89 | n = v.iterator().next().getNode(); 90 | quilt.search("", "", Pattern.LITERAL); 91 | // endTask(); 92 | 93 | wait(1); 94 | 95 | beginTask("select 2"); 96 | // s = 97 | selection.select(n); 98 | endTask(); 99 | 100 | wait(1); 101 | 102 | /*beginTask("highlight 2"); 103 | s.highlightPredecessors(); 104 | s.highlightSuccessors(); 105 | s.highlightSelection(); 106 | frame.repaint(); 107 | endTask();*/ 108 | 109 | wait(2); 110 | 111 | beginTask("paint canvas"); 112 | canvas.paintImmediately(canvas.getBounds()); 113 | endTask(); 114 | 115 | wait(1); 116 | 117 | beginTask("paint bev"); 118 | bev.paintImmediately(bev.getBounds()); 119 | endTask(); 120 | 121 | wait(1); 122 | 123 | beginTask("paint bev"); 124 | bev.paintImmediately(bev.getBounds()); 125 | endTask(); 126 | 127 | wait(1); 128 | 129 | //System.exit(0); 130 | } 131 | 132 | ///////////////////// 133 | 134 | private static long now() { 135 | return System.nanoTime(); 136 | } 137 | 138 | /** 139 | * Begin a long task 140 | * @param taskname_ the task name 141 | */ 142 | public static void beginTask(String taskname_) { 143 | t0 = now(); 144 | taskname = taskname_; 145 | } 146 | 147 | /** 148 | * Ends the current long task. 149 | */ 150 | public static void endTask() { 151 | if (t0 == 0 || taskname == null) { 152 | System.out.println("Error: attempt to end a task that has not started."); 153 | return; 154 | } 155 | int timems = (int)((now() - t0)/1000000); 156 | System.out.println("Task \"" + taskname + "\" done in " + timems + " ms."); 157 | t0 = 0; 158 | taskname = null; 159 | } 160 | 161 | /** 162 | * Sleeps for a specified amount os seconds. 163 | * @param s the number of seconds 164 | */ 165 | public static void wait(int s) { 166 | try { 167 | Thread.sleep(1000 * s); 168 | } catch (Exception e) { 169 | e.printStackTrace(); 170 | } 171 | } 172 | 173 | 174 | } 175 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/hull/Path.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.hull; 9 | 10 | import java.awt.BasicStroke; 11 | import java.awt.Color; 12 | import java.awt.Graphics2D; 13 | import java.awt.Stroke; 14 | import java.awt.geom.Point2D; 15 | 16 | import edu.umd.cs.piccolo.util.PPaintContext; 17 | 18 | /** 19 | * A 2D path approximated by a polyline. 20 | * 21 | * @author dragice 22 | * 23 | */ 24 | public class Path { 25 | 26 | // For debugging 27 | static Stroke pathstroke = new BasicStroke(2); 28 | 29 | double[] x; 30 | double[] y; 31 | double[] tmpx; 32 | double[] tmpy; 33 | 34 | public Path(int nbPoints) { 35 | x = new double[nbPoints]; 36 | y = new double[nbPoints]; 37 | tmpx = new double[nbPoints]; 38 | tmpy = new double[nbPoints]; 39 | } 40 | 41 | public void copy(Path to) { 42 | for (int i=0; i y[i]) 69 | y[i] = ymax; 70 | } 71 | } 72 | 73 | public void average(Path p2) { 74 | for (int i=0; i x.length - 1) 99 | index = x.length -1; 100 | return new Point2D.Double(x[index], y[index]); 101 | } 102 | 103 | public int getClosestPointIndex(Point2D p) { 104 | int closest_i = 0; 105 | double min_dist = Double.MAX_VALUE; 106 | double dist; 107 | for (int i=0; i j0) { 147 | for (int j = j0; jPSemanticText draws text as a rectangle when it is below readable size. 22 | * It also makes the rectangle big enough to be seen when its Paint attribute is not null. 23 | * 24 | * @author Pierre Dragicevic 25 | */ 26 | public class PSemanticText extends PText { 27 | 28 | private Rectangle2D.Float smallTextBounds = new Rectangle2D.Float(); 29 | private static final float effective_y0 = 0f;//0.4f; 30 | private static final float effective_h = 1f;//0.5f; 31 | 32 | private static float minimumScreenWidth = 3; 33 | private static float minimumScreenHeight = 3; 34 | protected double greekThreshold2; 35 | 36 | /** 37 | * Creates a ptext with tunable effective height 38 | * @param label 39 | */ 40 | public PSemanticText(String label) { 41 | super(label); 42 | greekThreshold2 = super.getGreekThreshold(); 43 | setGreekThreshold(0); 44 | } 45 | 46 | /** 47 | * {@inheritDoc} 48 | */ 49 | public void recomputeLayout() { 50 | super.recomputeLayout(); 51 | Rectangle2D b = getBoundsReference(); 52 | if (b != null && smallTextBounds != null) 53 | smallTextBounds.setFrame( 54 | b.getX(), 55 | b.getY() + (int)(effective_y0*b.getHeight()), 56 | b.getWidth(), 57 | (int)(effective_h*b.getHeight())); 58 | } 59 | 60 | protected void paint(PPaintContext paintContext) { 61 | float scale = (float) PrintConstants.instance.getScale(paintContext); 62 | float screenFontSize = getFont().getSize() * scale; 63 | if (getTextPaint() != null 64 | && (Printer.isPrinting() || screenFontSize > greekThreshold2)) { 65 | super.paint(paintContext); 66 | } else { 67 | Graphics2D g = paintContext.getGraphics(); 68 | if (getPaint() == null) { 69 | g.setColor(GraphicsConstants.SMALL_TEXT_COLOR); 70 | g.fill(smallTextBounds); 71 | } else { 72 | g.setPaint(getPaint()); 73 | float h = (float)(smallTextBounds.getHeight() * scale); 74 | float w = (float)(smallTextBounds.getWidth() * scale); 75 | if (h >= minimumScreenHeight && w > minimumScreenWidth) { 76 | g.fill(smallTextBounds); 77 | } else { 78 | // Grow the rectangle 79 | float h2 = Math.max((float)smallTextBounds.getHeight(), minimumScreenHeight / scale); 80 | float w2 = Math.max((float)smallTextBounds.getWidth(), minimumScreenWidth / scale); 81 | g.fillRect( 82 | (int)(smallTextBounds.getCenterX() - w2 / 2), 83 | (int)(smallTextBounds.getCenterY() - h2 / 2), 84 | (int)(w2 + 0.5f), 85 | (int)(h2 + 0.5f)); 86 | } 87 | 88 | } 89 | } 90 | } 91 | 92 | static Color bgColor = new Color(1, 1, 1, 0.1f); 93 | static Color outlineColor = new Color(1, 1, 1, 1f); 94 | // static PAffineTransform outlineShifts[][] = new PAffineTransform[3][3]; 95 | // static { 96 | // for (int x=0; x<3; x++) 97 | // for (int y=0; y<3; y++) { 98 | // outlineShifts[x][y] = new PAffineTransform(AffineTransform.getTranslateInstance((x-1)/1.0, (y-1)/1.0)); 99 | // } 100 | // } 101 | 102 | /** 103 | * 104 | * @param paintContext 105 | */ 106 | public void paintWithOutline(PPaintContext paintContext) { 107 | 108 | float scale = (float) PrintConstants.instance.getScale(paintContext); 109 | float screenFontSize = getFont().getSize() * scale; 110 | if (getTextPaint() != null && (Printer.isPrinting() || screenFontSize > greekThreshold)) { 111 | 112 | Graphics2D g = paintContext.getGraphics(); 113 | if (getPaint() == null) { 114 | g.setColor(bgColor); 115 | g.fillRect( 116 | (int)(smallTextBounds.getX()), 117 | (int)(smallTextBounds.getY() + 4), // FIXME 118 | (int)(smallTextBounds.getWidth()), 119 | (int)(smallTextBounds.getHeight() - 6)); // FIXME 120 | } 121 | 122 | Paint oldTextPaint = getTextPaint(); 123 | setTextPaint(outlineColor); 124 | 125 | for (int x=0; x<3; x++) 126 | for (int y=0; y<3; y++) { 127 | if (x != 0 || y != 0) { 128 | // paintContext.pushTransform(outlineShifts[x][y]); 129 | g.translate(x-1, y-1); 130 | super.paint(paintContext); 131 | g.translate(-x+1, -y+1); 132 | //paintContext.popTransform(outlineShifts[x][y]); 133 | } 134 | } 135 | 136 | super.paint(paintContext); 137 | setTextPaint(oldTextPaint); 138 | super.paint(paintContext); 139 | } 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/selection/highlight/PathHighlight.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.selection.highlight; 9 | 10 | import java.awt.Graphics2D; 11 | import java.awt.geom.Line2D; 12 | import java.awt.geom.Rectangle2D; 13 | 14 | import edu.umd.cs.piccolo.PNode; 15 | import edu.umd.cs.piccolo.util.PBounds; 16 | import edu.umd.cs.piccolo.util.PPaintContext; 17 | import geneaquilt.nodes.GraphicsConstants; 18 | import geneaquilt.nodes.PIsoShape; 19 | import geneaquilt.utils.MulticolorStroke; 20 | import geneaquilt.utils.PrintConstants; 21 | 22 | /** 23 | * The graphical object that represents a path highlight. 24 | * @author dragice 25 | * 26 | */ 27 | public class PathHighlight extends Highlight { 28 | 29 | float pathWidth; 30 | 31 | /** 32 | * Creates a highlight on one node, with no associated selection. 33 | * @param node 34 | */ 35 | public PathHighlight(PNode node) { 36 | super(node); 37 | } 38 | 39 | /** 40 | * Creates a highlight between two nodes, with no associated selection. 41 | * @param node 42 | */ 43 | public PathHighlight(PNode from, PNode to) { 44 | super(from, to); 45 | } 46 | 47 | /** 48 | * Computes the shape and bounds of the highlight according to the position of the object(s) to be highlighted. 49 | * Call this method whenever the bounds of the "from" or "to" node you passed to the constructor change. 50 | */ 51 | public void updateShape() { 52 | if (to == null) { 53 | 54 | PBounds b = from.getFullBoundsReference(); 55 | if (from instanceof PIsoShape) { 56 | // Special case: edges 57 | shape = ((PIsoShape)from).getShape(); 58 | } else { 59 | if (shape instanceof Rectangle2D) { 60 | ((Rectangle2D)shape).setRect(b); 61 | } else { 62 | shape = new Rectangle2D.Double(b.getX(), b.getY(), b.getWidth(), b.getHeight()); 63 | } 64 | } 65 | setBounds(b); 66 | } else { 67 | PBounds b1 = from.getFullBoundsReference(); 68 | PBounds b2 = to.getFullBoundsReference(); 69 | if (shape instanceof Line2D) { 70 | ((Line2D)shape).setLine(b1.getCenterX(), b1.getCenterY(), b2.getCenterX(), b2.getCenterY()); 71 | } else { 72 | shape = new Line2D.Double(b1.getCenterX(), b1.getCenterY(), b2.getCenterX(), b2.getCenterY()); 73 | } 74 | double x0 = Math.min(b1.getCenterX(), b2.getCenterX()); 75 | double y0 = Math.min(b1.getCenterY(), b2.getCenterY()); 76 | double x1 = Math.max(b1.getCenterX(), b2.getCenterX()); 77 | double y1 = Math.max(b1.getCenterY(), b2.getCenterY()); 78 | 79 | if (Math.abs(b1.getCenterX() - b2.getCenterX()) < Math.abs(b1.getCenterY() - b2.getCenterY())) { 80 | pathWidth = (float)Math.min(b1.getWidth(), b2.getWidth()); 81 | pathWidth = Math.max(3, pathWidth - 5); 82 | } else { 83 | pathWidth = (float)Math.min(b1.getHeight(), b2.getHeight()); 84 | pathWidth = Math.max(3, pathWidth - 8); 85 | } 86 | 87 | setBounds( 88 | x0 - pathWidth / 2 - 1, 89 | y0 - pathWidth / 2 - 1, 90 | x1 - x0 + pathWidth + 2, 91 | y1 - y0 + pathWidth + 2); 92 | } 93 | } 94 | 95 | /** 96 | * {@inheritDoc} 97 | */ 98 | @Override 99 | protected void paint(PPaintContext paintContext) { 100 | if (!isEmpty()) { 101 | final Graphics2D g2 = paintContext.getGraphics(); 102 | if (to == null && from instanceof PIsoShape) { 103 | // special case: edges 104 | g2.setColor(selections.getOpaqueCombinedColor()); 105 | PIsoShape.paintIsoShape(paintContext, shape, getFullBoundsReference()); // temporary fix 106 | } else if (to == null) { 107 | // Highlight a unique object 108 | g2.setColor(selections.getTranslucentCombinedColor()); 109 | g2.fill(shape); 110 | } else { 111 | // Highlight the line connecting two objects 112 | double scale = PrintConstants.instance.getScale(paintContext); 113 | if (scale < 1 / GraphicsConstants.PATH_HIGHLIGHT_WIDTH && paintContext.getRenderQuality() == PPaintContext.LOW_QUALITY_RENDERING) { 114 | g2.setColor(selections.getLightCombinedColor()); 115 | g2.setStroke(GraphicsConstants.NULL_WIDTH_STROKE); 116 | g2.draw(shape); 117 | } else { 118 | if (scale < GraphicsConstants.MULTICOLOR_STROKE_ZOOM_FACTOR) { 119 | g2.setColor(selections.getTranslucentCombinedColor()); 120 | g2.setStroke(GraphicsConstants.PATH_HIGHLIGHT_STROKE); 121 | g2.draw(shape); 122 | } else { 123 | MulticolorStroke mstroke = selections.getMulticolorStroke(pathWidth, selections.getSelectionCount() == 1); 124 | mstroke.draw(g2, shape); 125 | } 126 | } 127 | } 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/algorithms/VertexOrder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.algorithms; 9 | 10 | import geneaquilt.data.DateRange; 11 | import geneaquilt.data.Network; 12 | import geneaquilt.data.Vertex; 13 | 14 | import java.util.ArrayList; 15 | import java.util.Arrays; 16 | import java.util.Comparator; 17 | 18 | /** 19 | * Class VertexOrder 20 | * 21 | * @author Jean-Daniel Fekete 22 | * @version $Revision$ 23 | */ 24 | public class VertexOrder extends AbstractAlgorithm { 25 | private Vertex[][] layers; 26 | // private HashMap bestOrder; 27 | // private int bestCrossings; 28 | /** 29 | * Creates the vertex orderer 30 | * @param network the network 31 | */ 32 | public VertexOrder(Network network) { 33 | super(network); 34 | } 35 | 36 | /** 37 | * {@inheritDoc} 38 | */ 39 | @Override 40 | public void compute() { 41 | init(); 42 | // bestOrder = computeOrder(); 43 | // TODO Auto-generated method stub 44 | } 45 | 46 | private void init() { 47 | ArrayList[] lyrs = new ArrayList[network.getMaxLayer()+1]; 48 | for (int l = 0; l < lyrs.length; l++) { 49 | lyrs[l] = new ArrayList(); 50 | } 51 | for (Vertex v : network.getVertices()) { 52 | int l = v.getLayer(); 53 | lyrs[l].add(v); 54 | } 55 | layers = new Vertex[network.getMaxLayer()+1][]; 56 | for (int l = 0; l < lyrs.length; l++) { 57 | Vertex[] layer = lyrs[l].toArray(null); 58 | layers[l] = layer; 59 | } 60 | sortLayer(layers[0], birthOrder); 61 | for (int l = 1; l < layers.length; l++) { 62 | 63 | } 64 | } 65 | 66 | private static class BirthOrder implements Comparator { 67 | public int compare(Vertex a, Vertex b) { 68 | if (a.getComponent() != b.getComponent()) { 69 | return a.getComponent() - b.getComponent(); 70 | } 71 | DateRange da = a.getDateRange(); 72 | DateRange db = b.getDateRange(); 73 | return da.compareTo(db); 74 | } 75 | } 76 | private static final BirthOrder birthOrder = new BirthOrder(); 77 | 78 | private void sortLayer(Vertex[] layer, Comparator order) { 79 | Arrays.sort(layer, order); 80 | setOrder(layer); 81 | } 82 | 83 | private void setOrder(Vertex[] layer) { 84 | for (int i = 0; i < layer.length; i++) { 85 | layer[i].setX(i); 86 | } 87 | } 88 | // 89 | // private int getOrder(Vertex v) { 90 | // return (int)v.getX(); 91 | // } 92 | // 93 | // private HashMap computeOrder() { 94 | // HashMap order = new HashMap(network.getVertexCount()); 95 | // for (int l = 0; l < layers.length; l++) { 96 | // Vertex[] layer = layers[l]; 97 | // layers[l] = layer; 98 | // for (int i = 0; i < layer.length; i++) { 99 | // order.put(layer[i], new Integer(i)); 100 | // } 101 | // } 102 | // return order; 103 | // } 104 | 105 | // private int minimizeCrossings(int startpass, int endpass) { 106 | // int currentCross = Integer.MAX_VALUE; 107 | // int bestCross = currentCross; 108 | // HashMap savedOrder = null; 109 | // 110 | // if (startpass > 1) { 111 | // bestCross = currentCross = computeCrossings(); 112 | // savedOrder = computeOrder(); 113 | // } 114 | // 115 | // for (int pass = startpass; pass <= endpass; pass++) { 116 | // 117 | // } 118 | // 119 | // return bestCross; 120 | // } 121 | 122 | // private int computeCrossings() { 123 | // int count = 0; 124 | // for (int l = network.getMinLayer(); l <= network.getMaxLayer(); l++) { 125 | // count += computeCrossings(l); 126 | // } 127 | // return count; 128 | // } 129 | 130 | // private int computeCrossings(int l) { 131 | // int cross = 0; 132 | // int max = 0; 133 | // Vertex[] layer = layers[l]; 134 | // int[] count = new int[layer.length+1]; 135 | // 136 | // for (int top = 0; top < layer.length; top++) { 137 | // Vertex v = layer[top]; 138 | // if (max > 0) { 139 | // for (Vertex w : network.getDescendants(v)) { 140 | // for (int k = getOrder(w)+1; k <= max; k++) { 141 | // cross += count[k]; 142 | // } 143 | // } 144 | // } 145 | // for (Vertex w : network.getDescendants(v)) { 146 | // int inv = getOrder(w); 147 | // if (inv > max) max = inv; 148 | // count[inv]++; 149 | // } 150 | // } 151 | // 152 | // return cross; 153 | // } 154 | } 155 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/io/DOTWriter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.io; 9 | 10 | import geneaquilt.data.Edge; 11 | import geneaquilt.data.Indi; 12 | import geneaquilt.data.Network; 13 | import geneaquilt.data.Vertex; 14 | 15 | import java.io.IOException; 16 | import java.io.PrintWriter; 17 | import java.util.Collection; 18 | import java.util.HashSet; 19 | import java.util.TreeMap; 20 | 21 | /** 22 | * Class DOTWriter 23 | * 24 | * @author Jean-Daniel Fekete 25 | * @version $Revision$ 26 | */ 27 | public class DOTWriter { 28 | Network network; 29 | boolean bare; 30 | TreeMap> ranks; 31 | /** Key for generation data */ 32 | static public String GENERATION_KEY = "GENERATION"; 33 | 34 | /** 35 | * Creates a writer from a network 36 | * 37 | * @param network 38 | * the network 39 | */ 40 | public DOTWriter(Network network) { 41 | this.network = network; 42 | } 43 | 44 | /** 45 | * Writes in the specified file 46 | * @param filename the file name 47 | * @throws IOException 48 | */ 49 | public void write(String filename) throws IOException { 50 | PrintWriter out = new PrintWriter(filename); 51 | try { 52 | write(out); 53 | } 54 | finally { 55 | out.close(); 56 | } 57 | } 58 | 59 | /** 60 | * Writes in the specified printwriter. 61 | * @param out the print writer 62 | * @throws IOException 63 | */ 64 | public void write(PrintWriter out) throws IOException { 65 | ranks = new TreeMap>(); 66 | out.println("digraph genealogy {"); 67 | if (bare) { 68 | out.println("ranksep=1;"); 69 | out.println("node [shape=point,width=0,height=0,label=\"\"];"); 70 | out.println("edge [style=invis];"); 71 | } 72 | else { 73 | out.println("node [shape=box];"); 74 | } 75 | HashSet vertices = new HashSet(network.getVertices()); 76 | if (! bare) { 77 | for (Vertex v : vertices) { 78 | if (v instanceof Indi) { 79 | Indi indi = (Indi) v; 80 | out.println(v.getId() + " [label=\"" + indi.getName() + "\"];"); 81 | } 82 | } 83 | vertices.clear(); 84 | } 85 | for (Edge e : network.getEdges()) { 86 | Vertex source = network.getSource(e); 87 | Vertex dest = network.getDest(e); 88 | 89 | addRank(source); 90 | addRank(dest); 91 | vertices.remove(source); 92 | vertices.remove(dest); 93 | out.print('"'); 94 | out.print(source.getId()); 95 | out.print('"'); 96 | out.print("->"); 97 | out.print('"'); 98 | out.print(dest.getId()); 99 | out.print('"'); 100 | out.print(';'); 101 | out.println(); 102 | } 103 | for (Vertex v : vertices) { 104 | addRank(v); 105 | out.print('"'); 106 | out.print(v.getId()); 107 | out.print('"'); 108 | out.print(';'); 109 | out.println(); 110 | } 111 | 112 | for (Collection sameRank : ranks.values()) { 113 | if (sameRank.size() != 1) { 114 | out.print("{ rank=same;"); 115 | for (Vertex v : sameRank) { 116 | out.print(' '); 117 | out.print('"'); 118 | out.print(v.getId()); 119 | out.print('"'); 120 | } 121 | out.println(";}"); 122 | } 123 | } 124 | 125 | out.println('}'); 126 | out.close(); 127 | ranks = null; 128 | } 129 | 130 | private void addRank(Vertex source) { 131 | Object gen = source.getProperty(GENERATION_KEY); 132 | if (gen != null) { 133 | Collection sameRank = ranks.get(gen); 134 | if (sameRank == null) { 135 | sameRank = new HashSet(); 136 | ranks.put(gen, sameRank); 137 | } 138 | sameRank.add(source); 139 | } 140 | } 141 | 142 | /** 143 | * @return the bare 144 | */ 145 | public boolean isBare() { 146 | return bare; 147 | } 148 | 149 | /** 150 | * @param bare the bare to set 151 | */ 152 | public void setBare(boolean bare) { 153 | this.bare = bare; 154 | } 155 | 156 | /** 157 | * @return the generationKey 158 | */ 159 | public String getGenerationKey() { 160 | return GENERATION_KEY; 161 | } 162 | 163 | /** 164 | * @param generationKey the generationKey to set 165 | */ 166 | public static void setGenerationKey(String generationKey) { 167 | GENERATION_KEY = generationKey; 168 | } 169 | 170 | } 171 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/io/DOTLayersReader.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.io; 9 | 10 | import geneaquilt.data.Network; 11 | import geneaquilt.data.Vertex; 12 | import geneaquilt.utils.GUIUtils; 13 | 14 | import java.io.BufferedReader; 15 | import java.io.File; 16 | import java.io.IOException; 17 | import java.io.InputStreamReader; 18 | import java.io.PrintWriter; 19 | import java.util.Arrays; 20 | import java.util.HashMap; 21 | import java.util.TreeSet; 22 | import java.util.Map.Entry; 23 | 24 | import javax.swing.JOptionPane; 25 | 26 | import org.apache.log4j.Logger; 27 | 28 | /** 29 | * Class DOTLayersReader 30 | * 31 | * @author Jean-Daniel Fekete 32 | * @version $Revision$ 33 | */ 34 | public class DOTLayersReader { 35 | private static final Logger LOG = Logger.getLogger(DOTLayersReader.class); 36 | private static boolean debug = false; 37 | 38 | /** 39 | * Reads a layer file when it exists, associating a layer 40 | * and X position to each vertex. 41 | * @param network the network 42 | * @return true if it has been loaded, false otherwise 43 | */ 44 | public boolean load(Network network) { 45 | try { 46 | DOTWriter writer = new DOTWriter(network); 47 | String msg = "Computing layers..."; 48 | if (network.getEdgeCount() > 5000) 49 | msg += " (this may take a while!)"; 50 | GUIUtils.updateComputationMessage(msg); 51 | 52 | writer.setBare(true); 53 | String arg; 54 | File tmp = null; 55 | if (debug) { 56 | tmp = File.createTempFile("quilt", ".dot"); 57 | PrintWriter pw = new PrintWriter(tmp); 58 | writer.write(pw); 59 | pw.close(); 60 | arg = "dot -Tplain "+tmp.getAbsolutePath() ; 61 | } 62 | else { 63 | arg = "dot -Tplain"; 64 | } 65 | 66 | Process proc; 67 | try { 68 | proc = Runtime.getRuntime().exec(arg); 69 | } 70 | catch(IOException e) { 71 | JOptionPane.showMessageDialog(null, "Cannot find the 'dot' program. The generations will not be correctly assigned.\n\nTo fix this problem, make sure GraphViz is installed and the dot executable is accessible from " + System.getProperty("user.dir") + "\nThen, delete the .lyr file and relaunch GeneaQuilts.", "Error", JOptionPane.ERROR_MESSAGE); 72 | LOG.warn("Cannot find the dot program"); 73 | return false; 74 | } 75 | 76 | InputStreamReader isr = new InputStreamReader(proc.getInputStream()); 77 | BufferedReader in = new BufferedReader(isr); 78 | if (! debug) { 79 | writer.write(new PrintWriter(proc.getOutputStream())); 80 | } 81 | 82 | if (tmp != null) { 83 | LOG.info("Generated DOT file available at "+tmp.getAbsolutePath()); 84 | //tmp.delete(); 85 | } 86 | 87 | String line; 88 | TreeSet ranks = new TreeSet(); 89 | HashMap vertexY = new HashMap(); 90 | while ((line = in.readLine()) != null) { 91 | String fields[] = line.split(" "); 92 | if (fields.length < 5 || ! fields[0].equals("node")) 93 | continue; 94 | String id = fields[1]; 95 | if (id.startsWith("\"") && id.endsWith("\"")) { 96 | id = id.substring(1, id.length()-1); 97 | } 98 | double x = Double.parseDouble(fields[2]); 99 | Double y = Double.valueOf(fields[3]); 100 | Vertex v = network.getVertex(id); 101 | if (v != null) { 102 | v.setX(x); 103 | vertexY.put(v, y); 104 | ranks.add(y); 105 | } 106 | else { 107 | LOG.warn("Invalid node id="+id); 108 | } 109 | } 110 | double[] r = new double[ranks.size()]; 111 | int i = 0; 112 | for (Double d : ranks) { 113 | r[i++] = d.doubleValue(); 114 | } 115 | for (Entry e : vertexY.entrySet()) { 116 | int l = Arrays.binarySearch(r, e.getValue().doubleValue()); 117 | if (l < 0) { 118 | LOG.error("Unexpeced layer not found for "+e.getValue()); 119 | l = -l+1; 120 | } 121 | network.setVertexLayer(e.getKey(), l); 122 | } 123 | // network.fixLayers(); 124 | } 125 | catch(Exception e) { 126 | LOG.debug("Cannot read layers", e); 127 | return false; 128 | } 129 | return true; 130 | } 131 | 132 | 133 | 134 | /** 135 | * @return the debug 136 | */ 137 | public static boolean isDebug() { 138 | return debug; 139 | } 140 | 141 | 142 | 143 | /** 144 | * @param d the debug to set 145 | */ 146 | public static void setDebug(boolean d) { 147 | debug = d; 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/nodes/PSemanticPath.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.nodes; 9 | 10 | import java.awt.BasicStroke; 11 | import java.awt.Graphics2D; 12 | import java.awt.Paint; 13 | import java.awt.Shape; 14 | import java.awt.Stroke; 15 | 16 | import edu.umd.cs.piccolo.nodes.PPath; 17 | import edu.umd.cs.piccolo.util.PPaintContext; 18 | import geneaquilt.Printer; 19 | import geneaquilt.utils.PrintConstants; 20 | 21 | /** 22 | * 23 | * A PPath that allows to set a minimum stroke screen width and a different stroke color when 24 | * its screen width is below a certain size. 25 | * 26 | * @author dragice 27 | * 28 | */ 29 | public class PSemanticPath extends PPath { 30 | /** 31 | * 32 | * StrokeCache implements a cache of strokes 33 | */ 34 | /*public static class StrokeCache { 35 | private Hashtable cache = new Hashtable(); 36 | private final int widthAccurracy = 1; 37 | / ** 38 | * Returns a stroke with the specified width. 39 | * @param width the width 40 | * @return a stroke 41 | * / 42 | public Stroke getStrokeWithWidth(float width) { 43 | int key = (int)(width * widthAccurracy); 44 | Stroke s = cache.get(new Integer(key)); 45 | if (s != null) 46 | return s; 47 | s = new BasicStroke(width); 48 | cache.put(new Integer(key), s); 49 | return s; 50 | } 51 | }*/ 52 | 53 | //private static final StrokeCache strokeCache = new StrokeCache(); 54 | private static Stroke lastStroke = null; 55 | private static float lastStrokeWidth = -1; 56 | 57 | private float normalStrokeWidth = 1; 58 | private float minimumScreenStrokeWidth = 1; 59 | private Paint smallStrokePaint = null; 60 | private float smallStrokePaintScale = 0; 61 | 62 | /** 63 | * 64 | * @param minimumScreenStrokeWidth 65 | */ 66 | public void setMinimumScreenStrokeWidth(float minimumScreenStrokeWidth) { 67 | this.minimumScreenStrokeWidth = minimumScreenStrokeWidth; 68 | } 69 | 70 | /** 71 | * 72 | * @param p 73 | * @param scale 74 | */ 75 | public void setSmallStrokePaint(Paint p, float scale) { 76 | this.smallStrokePaint = p; 77 | this.smallStrokePaintScale = scale; 78 | } 79 | 80 | /** 81 | * @return the small stroke paint 82 | */ 83 | public Paint getSmallStrokePaint() { 84 | return smallStrokePaint; 85 | } 86 | 87 | /** 88 | * @return the small stroke paint scale 89 | */ 90 | public float getSmallStrokePaintScale() { 91 | return smallStrokePaintScale; 92 | } 93 | 94 | /** 95 | * Creates a PSemanticPath. 96 | */ 97 | public PSemanticPath() { 98 | super(); 99 | updateNormalStrokeWidth(); 100 | } 101 | 102 | /** 103 | * Creates a PSemanticPath with the specified shape. 104 | * @param ppath the shape 105 | */ 106 | public PSemanticPath(Shape ppath) { 107 | super(ppath); 108 | updateNormalStrokeWidth(); 109 | } 110 | 111 | /** 112 | * {@inheritDoc} 113 | */ 114 | public void setStroke(Stroke stroke) { 115 | super.setStroke(stroke); 116 | updateNormalStrokeWidth(); 117 | } 118 | 119 | protected void updateNormalStrokeWidth() { 120 | if (getStroke() instanceof BasicStroke) { 121 | normalStrokeWidth = ((BasicStroke)getStroke()).getLineWidth(); 122 | } 123 | } 124 | 125 | protected void paint(PPaintContext paintContext) { 126 | 127 | Paint p = getPaint(); 128 | Graphics2D g2 = paintContext.getGraphics(); 129 | 130 | if (p != null) { 131 | g2.setPaint(p); 132 | g2.fill(getPathReference()); 133 | } 134 | 135 | float scale = (float) PrintConstants.instance.getScale(paintContext); 136 | Paint strokePaint; 137 | if (smallStrokePaint == null || scale >= smallStrokePaintScale) 138 | strokePaint = getStrokePaint(); 139 | else 140 | strokePaint = getSmallStrokePaint(); 141 | 142 | if (getStroke() != null && strokePaint != null) { 143 | 144 | float screenStrokeWidth = normalStrokeWidth * scale; 145 | 146 | if (Printer.isPrinting() || screenStrokeWidth >= minimumScreenStrokeWidth) { 147 | g2.setPaint(strokePaint); 148 | if (Printer.isPrinting()) 149 | g2.setStroke(GraphicsConstants.NULL_WIDTH_STROKE); 150 | else 151 | g2.setStroke(getStroke()); 152 | g2.draw(getPathReference()); 153 | } else { 154 | if (Printer.isPrinting() || minimumScreenStrokeWidth == 1 && paintContext.getRenderQuality() == PPaintContext.LOW_QUALITY_RENDERING) { 155 | // Special case: if no antialiasing and stroke width of 1, we can use a stroke 156 | // width of zero. 157 | g2.setPaint(strokePaint); 158 | g2.setStroke(GraphicsConstants.NULL_WIDTH_STROKE); 159 | g2.draw(getPathReference()); 160 | } else { 161 | g2.setPaint(strokePaint); 162 | g2.setStroke(getStrokeWithWidth(minimumScreenStrokeWidth / scale)); 163 | g2.draw(getPathReference()); 164 | } 165 | } 166 | } 167 | } 168 | 169 | private static Stroke getStrokeWithWidth(float width) { 170 | if (lastStrokeWidth == width) 171 | return lastStroke; 172 | lastStroke = new BasicStroke(width); 173 | return lastStroke; 174 | } 175 | 176 | /** 177 | * 178 | * @return the scale for larger strokes 179 | */ 180 | public float getScaleForLargerStrokes() { 181 | return minimumScreenStrokeWidth / normalStrokeWidth; 182 | } 183 | 184 | } 185 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/io/GEDReader.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.io; 9 | 10 | import geneaquilt.data.Edge; 11 | import geneaquilt.data.Fam; 12 | import geneaquilt.data.Indi; 13 | import geneaquilt.data.Network; 14 | import geneaquilt.data.Vertex; 15 | import org.gedcom4j.model.Family; 16 | import org.gedcom4j.model.FamilyChild; 17 | import org.gedcom4j.model.FamilySpouse; 18 | import org.gedcom4j.model.Individual; 19 | import org.gedcom4j.model.IndividualAttribute; 20 | import org.gedcom4j.model.IndividualEvent; 21 | import org.gedcom4j.model.PersonalName; 22 | import org.gedcom4j.model.StringWithCustomTags; 23 | import org.gedcom4j.parser.GedcomParser; 24 | 25 | 26 | /** 27 | * Class GEDReader allows reading from a GEDCOM file. 28 | * 29 | * @author Jean-Daniel Fekete 30 | * @version $Revision$ 31 | */ 32 | public class GEDReader { 33 | private Network network; 34 | 35 | /** 36 | * Loads a GEDCOM file 37 | * @param filename the file name 38 | * @return a network or null 39 | */ 40 | public Network load(String filename) { 41 | GedcomParser gp = new GedcomParser(); 42 | network = new Network(); 43 | try { 44 | gp.load(filename); 45 | 46 | for (Individual i: gp.gedcom.individuals.values()) { 47 | Indi indi = new Indi(); 48 | indi.setId(i.xref); 49 | network.addVertex(indi); 50 | for (PersonalName p : i.names) { 51 | indi.setProperty("NAME", p.basic); 52 | if (p.givenName != null) 53 | indi.setProperty("NAME.GIVN", p.givenName); 54 | if (p.surname != null) 55 | indi.setProperty("NAME.SURN", p.surname); 56 | if (p.nickname != null) 57 | indi.setProperty("NAME.NICK", p.surname); 58 | } 59 | for (StringWithCustomTags s : i.aliases) { 60 | indi.setProperty("NAME.ALIAS", s.trim()); 61 | } 62 | for (FamilyChild fc : i.familiesWhereChild) { 63 | indi.setFamc(fc.family.xref); // should take the fist? 64 | } 65 | for (FamilySpouse fs : i.familiesWhereSpouse) { 66 | indi.addFams(fs.family.xref); 67 | } 68 | if (i.sex != null) 69 | indi.setSex(i.sex.trim()); 70 | for (IndividualAttribute ia : i.attributes) { 71 | indi.setProperty(ia.type.tag, ia.description.value); 72 | } 73 | for (IndividualEvent ee : i.events) { 74 | String tag = ee.type.tag; 75 | if (ee.address != null) { 76 | StringBuffer sb = new StringBuffer(); 77 | for (String s : ee.address.lines) sb.append(s); 78 | indi.setProperty(tag+".ADDR", sb.toString()); 79 | } 80 | if (ee.age != null) 81 | indi.setProperty(tag+".AGE", ee.age.trim()); 82 | 83 | if (ee.cause != null) 84 | indi.setProperty(tag+".CAUSE", ee.cause.trim()); 85 | 86 | if (ee.date != null) 87 | indi.setDate(tag+".DATE", ee.date.trim()); 88 | 89 | if (ee.description != null) 90 | indi.setProperty(tag+".DESC", ee.description.trim()); 91 | 92 | } 93 | } 94 | for (Family f : gp.gedcom.families.values()) { 95 | Fam fam = new Fam(); 96 | fam.setId(f.xref); 97 | network.addVertex(fam); 98 | if (f.husband != null) 99 | fam.setHusb(f.husband.xref); 100 | if (f.wife != null) 101 | fam.setWife(f.wife.xref); 102 | } 103 | } 104 | catch(Exception e) { 105 | e.printStackTrace(); 106 | return null; 107 | } 108 | 109 | for (Vertex v : network.getVertices()) { 110 | if (v instanceof Indi) { 111 | Indi indi = (Indi) v; 112 | if (indi.getFamc() != null) { 113 | Vertex fam = network.getVertex(indi.getFamc()); 114 | if (fam != null) { 115 | Edge edge = new Edge(v.getId(), indi.getFamc()); 116 | if (!network.containsEdge(edge)) { 117 | network.addEdge(edge, indi, fam); 118 | edge.setFromVertex(v); 119 | edge.setToVertex(fam); 120 | } 121 | } 122 | } 123 | if (indi.getFams() != null) { 124 | for (String fid : indi.getFams()) { 125 | Vertex fam = network.getVertex(fid); 126 | if (fam != null) { 127 | Edge edge = new Edge(fam.getId(), v.getId()); 128 | if (!network.containsEdge(edge)) { 129 | network.addEdge(edge, fam, indi); 130 | edge.setFromVertex(fam); 131 | edge.setToVertex(indi); 132 | } 133 | } 134 | } 135 | } 136 | } 137 | } 138 | return network; 139 | } 140 | 141 | } 142 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/selection/highlight/SelectionCombination.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.selection.highlight; 9 | 10 | import geneaquilt.nodes.GraphicsConstants; 11 | import geneaquilt.selection.Selection; 12 | import geneaquilt.utils.GUIUtils; 13 | import geneaquilt.utils.MulticolorStroke; 14 | 15 | import java.awt.Color; 16 | import java.util.HashSet; 17 | import java.util.Hashtable; 18 | import java.util.Set; 19 | 20 | /** 21 | * This class computes and stores graphic attributes associated to a specific set of selections, such as 22 | * their average color. It ensures that every combination of selections is represented by a unique object. 23 | * 24 | * @author dragice 25 | * 26 | */ 27 | public class SelectionCombination { 28 | 29 | private static final Set allCombinations = new HashSet(); 30 | private static final SelectionCombination EMPTY_SELECTION = new SelectionCombination(new HashSet()); 31 | private final Set selections; 32 | 33 | private final Color combinedColor; 34 | private final Color combinedColor_translucent; 35 | private final Color combinedColor_light; 36 | private final Hashtable multicolorStrokes = new Hashtable(); 37 | 38 | protected SelectionCombination(Set selections) { 39 | this.selections = selections; 40 | this.combinedColor = getAverageColor(selections); 41 | this.combinedColor_translucent = GUIUtils.multiplyAlpha(combinedColor, selections.size() > 1 ? 0.5f : 0.3f); 42 | this.combinedColor_light = GUIUtils.mix(combinedColor, Color.white, selections.size() > 1 ? 0f : 0.5f); 43 | } 44 | 45 | public boolean equals(Object o) { 46 | if (!(o instanceof SelectionCombination)) 47 | return false; 48 | return selections.equals(((SelectionCombination)o).selections); 49 | } 50 | 51 | public boolean contains(Selection selection) { 52 | return selections.contains(selection); 53 | } 54 | 55 | public static SelectionCombination getInstance(Set selections) { 56 | for (SelectionCombination sc : allCombinations) { 57 | if (sc.selections.equals(selections)) 58 | return sc; 59 | } 60 | SelectionCombination sc = new SelectionCombination(selections); 61 | allCombinations.add(sc); 62 | return sc; 63 | } 64 | 65 | public static SelectionCombination getInstance(Selection selection) { 66 | Set sels = new HashSet(); 67 | sels.add(selection); 68 | return getInstance(sels); 69 | } 70 | 71 | public static SelectionCombination getEmptyInstance() { 72 | if (!allCombinations.contains(EMPTY_SELECTION)) 73 | allCombinations.add(EMPTY_SELECTION); 74 | return EMPTY_SELECTION; 75 | } 76 | 77 | public SelectionCombination getInstanceWithSelection(Selection newSelection) { 78 | Set sels = new HashSet(selections); 79 | sels.add(newSelection); 80 | return getInstance(sels); 81 | } 82 | 83 | public SelectionCombination getInstanceWithoutSelection(Selection selection) { 84 | Set sels = new HashSet(selections); 85 | sels.remove(selection); 86 | return getInstance(sels); 87 | } 88 | 89 | static void cleanupDeletedSelection(Selection selection) { 90 | Set combinations_tmp = new HashSet(allCombinations); 91 | for (SelectionCombination sc : combinations_tmp) { 92 | if (sc.contains(selection)) { 93 | allCombinations.remove(sc); 94 | } 95 | } 96 | } 97 | 98 | public Set getSelections() { 99 | return selections; 100 | } 101 | 102 | public static void clear() { 103 | allCombinations.clear(); 104 | } 105 | 106 | protected static Color getAverageColor(Set selections) { 107 | 108 | if (selections.size() == 0) 109 | return Color.white; 110 | 111 | int r = 0, g = 0, b = 0, a = 0; 112 | int count = 0; 113 | for (Selection s : selections) { 114 | Color c = s.getOpaqueColor(); 115 | r += c.getRed(); 116 | g += c.getGreen(); 117 | b += c.getBlue(); 118 | a += c.getAlpha(); 119 | count++; 120 | } 121 | return new Color(r/count, g/count, b/count, a/count); 122 | } 123 | 124 | 125 | protected static MulticolorStroke createMulticolorStroke(Set selections, float width, boolean light) { 126 | Color[] colors = new Color[selections.size()]; 127 | int i = 0; 128 | for (Selection s: selections) { 129 | colors[i] = light ? s.getLightColor() : s.getStrongColor(); 130 | i++; 131 | } 132 | return new MulticolorStroke(width, colors, GraphicsConstants.PATH_HIGHLIGHT_MULTICOLOR_SPACING); 133 | } 134 | 135 | /** 136 | * @return Returns the color obtained by blending all selection colors. 137 | * 138 | */ 139 | public Color getOpaqueCombinedColor() { 140 | return combinedColor; 141 | } 142 | 143 | public Color getTranslucentCombinedColor() { 144 | return combinedColor_translucent; 145 | } 146 | 147 | public Color getLightCombinedColor() { 148 | return combinedColor_light; 149 | } 150 | 151 | public MulticolorStroke getMulticolorStroke(float width, boolean light) { 152 | MulticolorStroke s = multicolorStrokes.get(width); 153 | if (s == null) { 154 | s = createMulticolorStroke(selections, width, light); 155 | multicolorStrokes.put(width, s); 156 | } 157 | return s; 158 | } 159 | 160 | public boolean isEmpty() { 161 | return selections.size() == 0; 162 | } 163 | 164 | public int getSelectionCount() { 165 | return selections.size(); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/selection/DOIManager.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.selection; 9 | 10 | import edu.uci.ics.jung.algorithms.util.MapBinaryHeap; 11 | import edu.umd.cs.piccolo.PNode; 12 | import geneaquilt.data.Edge; 13 | import geneaquilt.data.Network; 14 | import geneaquilt.data.Vertex; 15 | import geneaquilt.nodes.PEdge; 16 | import geneaquilt.nodes.PVertex; 17 | import geneaquilt.nodes.QuiltManager; 18 | 19 | import java.util.Comparator; 20 | import java.util.HashMap; 21 | import java.util.Map; 22 | 23 | /** 24 | * DOIManager manages the degree of interest 25 | * of nodes. 26 | * 27 | * @author Jean-Daniel Fekete 28 | */ 29 | public class DOIManager { 30 | private SelectionManager selectionManager; 31 | private QuiltManager quiltManager; 32 | private Map distance = new HashMap(); 33 | private MapBinaryHeap heap; 34 | private double highlightDistance = 0; 35 | private double maxDistance = 10; 36 | 37 | /** 38 | * Creates a DOIManager for a specified quiltManager and selectionManager. 39 | * @param quiltManager the QuiltManager 40 | * @param selectionManager the SelectionManager 41 | */ 42 | public DOIManager( 43 | QuiltManager quiltManager) { 44 | this.quiltManager = quiltManager; 45 | this.selectionManager = quiltManager.getSelectionManager(); 46 | } 47 | 48 | /** 49 | * @return the selectionManager 50 | */ 51 | public SelectionManager getSelectionManager() { 52 | return selectionManager; 53 | } 54 | 55 | /** 56 | * @return the quiltManager 57 | */ 58 | public QuiltManager getQuiltManager() { 59 | return quiltManager; 60 | } 61 | 62 | /** 63 | * @return the network 64 | */ 65 | public Network getNetwork() { 66 | return quiltManager.getNetwork(); 67 | } 68 | 69 | private final Comparator nodeComparator = 70 | new Comparator() { 71 | public int compare(PNode o1, PNode o2) { 72 | Double d1 = distance.get(o1); 73 | Double d2 = distance.get(o2); 74 | return (int)Math.signum(d1.doubleValue() - d2.doubleValue()); 75 | } 76 | }; 77 | 78 | /** 79 | * Computes the DOI in the network. 80 | */ 81 | public void computeDOI() { 82 | distance.clear(); 83 | heap = new MapBinaryHeap(nodeComparator); 84 | // Set> components = new HashSet>(); 85 | 86 | for (Selection sel : selectionManager.getSelections()) { 87 | PNode n = sel.getSelectedObject(); 88 | updateDistance(n, 0); 89 | // if (n instanceof PVertex) { 90 | // PVertex pv = (PVertex) n; 91 | // components.add( 92 | // getNetwork().getComponentSet(pv.getVertex())); 93 | // } 94 | // else { 95 | // PEdge pe = (PEdge)n; 96 | // Vertex v = getNetwork().getSource(pe.getEdge()); 97 | // components.add( 98 | // getNetwork().getComponentSet(v)); 99 | // } 100 | 101 | for (PNode h : sel.getHighlightedObjects()) { 102 | updateDistance(h, highlightDistance); 103 | } 104 | } 105 | while (! heap.isEmpty()) { 106 | PNode n = heap.remove(); 107 | double newD = nextDistFrom(n); 108 | // if (newD > maxDistance) 109 | // continue; 110 | if (n instanceof PVertex) { 111 | PVertex pv = (PVertex)n; 112 | Vertex v = pv.getVertex(); 113 | for (Edge edge : getNetwork().getOutEdges(v)) { 114 | updateDistance(edge.getNode(), newD); 115 | } 116 | for (Edge edge : getNetwork().getInEdges(v)) { 117 | updateDistance(edge.getNode(), newD); 118 | } 119 | } 120 | else if (n instanceof PEdge) { 121 | PEdge pedge = (PEdge) n; 122 | Edge edge = pedge.getEdge(); 123 | updateDistance(getNetwork().getSource(edge).getNode(), newD); 124 | updateDistance(getNetwork().getDest(edge).getNode(), newD); 125 | } 126 | else { 127 | System.err.println("Unexpected node "+n); 128 | } 129 | } 130 | for (Vertex v : getNetwork().getVertices()) { 131 | Double d = distance.get(v.getNode()); 132 | if (d == null) { 133 | // assert(!components.contains(getNetwork().getComponentSet(v))); 134 | v.setDOI(maxDistance); 135 | } 136 | else { 137 | v.setDOI(d.doubleValue()); 138 | } 139 | } 140 | } 141 | 142 | private boolean updateDistance(PNode node, double newD) { 143 | Double d = distance.get(node); 144 | if (d == null) { 145 | distance.put(node, new Double(newD)); 146 | heap.add(node); 147 | return true; 148 | } 149 | else if (d.doubleValue() > newD) { 150 | distance.put(node, new Double(newD)); 151 | heap.update(node); 152 | return true; 153 | } 154 | return false; 155 | } 156 | 157 | protected double nextDistFrom(PNode n) { 158 | Double d = distance.get(n); 159 | return d.doubleValue()+1; // can be log 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/io/JSONWriter.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.io; 9 | 10 | import geneaquilt.data.Edge; 11 | import geneaquilt.data.Indi; 12 | import geneaquilt.data.Network; 13 | import geneaquilt.data.Vertex; 14 | 15 | import java.io.IOException; 16 | import java.io.PrintWriter; 17 | import java.util.HashMap; 18 | import java.util.Map; 19 | import java.util.regex.Matcher; 20 | import java.util.regex.Pattern; 21 | 22 | /** 23 | * Class JSONWriter 24 | * 25 | * @author Jean-Daniel Fekete 26 | * @version $Revision$ 27 | */ 28 | public class JSONWriter { 29 | Network network; 30 | /** Key for generation data */ 31 | static public String GENERATION_KEY = "GENERATION"; 32 | static private Pattern INTEGER = Pattern.compile("[0-9]+"); 33 | static private Pattern JSON_CONTROLS = Pattern.compile("[\"|\\|\b|\n|\r|\t]"); 34 | 35 | /** 36 | * Creates a writer from a network 37 | * 38 | * @param network 39 | * the network 40 | */ 41 | public JSONWriter(Network network) { 42 | this.network = network; 43 | } 44 | 45 | 46 | /** 47 | * Writes in the specified file 48 | * @param filename the file name 49 | * @throws IOException 50 | */ 51 | public void write(String filename) throws IOException { 52 | PrintWriter out = new PrintWriter(filename); 53 | try { 54 | write(out); 55 | } 56 | finally { 57 | out.close(); 58 | } 59 | } 60 | 61 | /** 62 | * Writes in the specified printwriter. 63 | * @param out the print writer 64 | * @throws IOException 65 | */ 66 | public void write(PrintWriter out) throws IOException { 67 | HashMap nodeIndex = new HashMap(network.getVertexCount()); 68 | out.print("{"); 69 | 70 | printTag(out, "nodes"); 71 | out.print('['); 72 | 73 | boolean first = true; 74 | for (Vertex v : network.getVertices()) { 75 | if (first) { 76 | first = false; 77 | } 78 | else { 79 | out.print(','); 80 | } 81 | out.print('{'); 82 | nodeIndex.put(v, new Integer(nodeIndex.size())); 83 | if (v instanceof Indi) { 84 | Indi indi = (Indi) v; 85 | printTag(out, "type"); 86 | printName(out, "indi"); 87 | String f = indi.getFamc(); 88 | if (f != null) { 89 | out.print(','); 90 | printTag(out, "famc"); 91 | printName(out, f); 92 | } 93 | if (indi.getFams() != null && ! indi.getFams().isEmpty()) { 94 | out.print(','); 95 | printTag(out, "fams"); 96 | out.print('['); 97 | boolean first2 = true; 98 | for (String o : indi.getFams()) { 99 | if (first2) first2 = false; 100 | else out.print(','); 101 | printName(out, o); 102 | } 103 | out.print(']'); 104 | } 105 | // printTag(out, "id"); 106 | // printName(out, indi.getId()); 107 | // out.print(','); 108 | // printTag(out, "name"); 109 | // printName(out, indi.getName()); 110 | } 111 | else { 112 | // Fam fam = (Fam) v; 113 | printTag(out, "type"); 114 | printName(out, "fam"); 115 | out.print(','); 116 | printTag(out, "ID"); 117 | printName(out, v.getId()); 118 | } 119 | out.print(','); 120 | printTag(out, "x"); 121 | out.print(v.getX()); 122 | out.print(','); 123 | printTag(out, "y"); 124 | out.print(v.getLayer()); 125 | 126 | for (Map.Entry e : v.getProps().entrySet()) { 127 | out.print(','); 128 | printTag(out, e.getKey()); 129 | print(out, e.getValue()); 130 | } 131 | out.print('}'); 132 | } 133 | out.print(']'); 134 | out.print(','); 135 | printTag(out, "links"); 136 | out.print('['); 137 | 138 | first = true; 139 | for (Edge e : network.getEdges()) { 140 | Vertex source = network.getSource(e); 141 | Vertex dest = network.getDest(e); 142 | 143 | if (first) { 144 | first = false; 145 | } 146 | else { 147 | out.print(','); 148 | } 149 | out.print('{'); 150 | printTag(out, "source"); 151 | out.print(nodeIndex.get(source)); 152 | out.print(','); 153 | printTag(out, "target"); 154 | out.print(nodeIndex.get(dest)); 155 | out.print('}'); 156 | } 157 | 158 | out.println("]}"); 159 | out.close(); 160 | } 161 | 162 | private void print(PrintWriter out, Object o) throws IOException { 163 | if (o == null) { 164 | out.print("null"); 165 | } 166 | else if (o instanceof String) { 167 | String s = (String)o; 168 | Matcher m = INTEGER.matcher(s); 169 | if (m.matches()) { 170 | out.print(s); 171 | } 172 | else { 173 | printName(out, s); 174 | } 175 | } 176 | else { 177 | out.print(o.toString()); 178 | } 179 | } 180 | 181 | private void printName(PrintWriter out, String name) throws IOException { 182 | int last = 0; 183 | out.print('"'); 184 | Matcher m = JSON_CONTROLS.matcher(name); 185 | 186 | while (m.find()) { 187 | String pre = name.substring(last, m.end()-1); 188 | out.print(pre); 189 | out.print('\\'); 190 | last = m.end()-1; 191 | } 192 | out.print(name.substring(last)); 193 | out.print('"'); 194 | } 195 | 196 | private void printTag(PrintWriter out, String name) throws IOException { 197 | printName(out, name); 198 | out.print(':'); 199 | } 200 | 201 | } 202 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/nodes/PFlatRect.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | /* 9 | * Copyright (c) 2008-2010, Piccolo2D project, http://piccolo2d.org 10 | * Copyright (c) 1998-2008, University of Maryland 11 | * All rights reserved. 12 | * 13 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided 14 | * that the following conditions are met: 15 | * 16 | * Redistributions of source code must retain the above copyright notice, this list of conditions 17 | * and the following disclaimer. 18 | * 19 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions 20 | * and the following disclaimer in the documentation and/or other materials provided with the 21 | * distribution. 22 | * 23 | * None of the name of the University of Maryland, the name of the Piccolo2D project, or the names of its 24 | * contributors may be used to endorse or promote products derived from this software without specific 25 | * prior written permission. 26 | * 27 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 28 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 29 | * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 30 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 31 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 32 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 33 | * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 34 | * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35 | */ 36 | package geneaquilt.nodes; 37 | 38 | import java.awt.BasicStroke; 39 | import java.awt.Color; 40 | import java.awt.Graphics2D; 41 | import java.awt.Paint; 42 | import java.awt.Stroke; 43 | import java.awt.geom.GeneralPath; 44 | import java.awt.geom.Rectangle2D; 45 | 46 | import edu.umd.cs.piccolo.PNode; 47 | import edu.umd.cs.piccolo.util.PBounds; 48 | import edu.umd.cs.piccolo.util.PPaintContext; 49 | 50 | /** 51 | * This is a simple node that draws a "3D" rectangle within the bounds of the 52 | * node. Drawing a 3D rectangle in a zooming environment is a little tricky 53 | * because if you just use the regular (Java2D) 3D rectangle, the 3D borders get 54 | * scaled, and that is ugly. This version always draws the 3D border at fixed 2 55 | * pixel width. 56 | * 57 | * @author Ben Bederson 58 | */ 59 | public class PFlatRect extends PNode { 60 | private static final long serialVersionUID = 1L; 61 | private Color borderColor; 62 | private transient GeneralPath path = null; 63 | private transient Stroke stroke = new BasicStroke(1); 64 | 65 | /** 66 | * Constructs a simple P3DRect with empty bounds and a black stroke. 67 | */ 68 | public PFlatRect() { 69 | } 70 | 71 | /** 72 | * Constructs a P3DRect with the provided bounds. 73 | * 74 | * @param bounds bounds to assigned to the P3DRect 75 | */ 76 | public PFlatRect(final Rectangle2D bounds) { 77 | this(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight()); 78 | } 79 | 80 | /** 81 | * Constructs a P3DRect with the bounds provided. 82 | * 83 | * @param x left of bounds 84 | * @param y top of bounds 85 | * @param width width of bounds 86 | * @param height height of bounds 87 | */ 88 | public PFlatRect(final double x, final double y, final double width, final double height) { 89 | this(); 90 | setBounds(x, y, width, height); 91 | } 92 | 93 | /** 94 | * Paints this rectangle with shaded edges. Making it appear to stand out of 95 | * the page as normal 3D buttons do. 96 | * 97 | * @param paintContext context in which the paiting should occur 98 | */ 99 | protected void paint(final PPaintContext paintContext) { 100 | 101 | int old_quality = paintContext.getRenderQuality(); 102 | paintContext.setRenderQuality(PPaintContext.HIGH_QUALITY_RENDERING); 103 | 104 | 105 | // lazy init: 106 | if (stroke == null) { 107 | stroke = new BasicStroke(0); 108 | } 109 | if (path == null) { 110 | path = new GeneralPath(); 111 | } 112 | 113 | final Graphics2D g2 = paintContext.getGraphics(); 114 | 115 | final double x = getX(); 116 | final double y = getY(); 117 | final double width = getWidth()-1; 118 | final double height = getHeight()-1; 119 | final double scaleX = g2.getTransform().getScaleX(); 120 | final double scaleY = g2.getTransform().getScaleY(); 121 | final double dx = (float) (1.0 / scaleX); 122 | final double dy = (float) (1.0 / scaleY); 123 | final PBounds bounds = getBounds(); 124 | 125 | g2.setPaint(getPaint()); 126 | g2.fill(bounds); 127 | g2.setStroke(stroke); 128 | 129 | path.reset(); 130 | path.moveTo((float) (x + width), (float) y); 131 | path.lineTo((float) x, (float) y); 132 | path.lineTo((float) x, (float) (y + height)); 133 | g2.setPaint(borderColor); 134 | g2.draw(path); 135 | 136 | path.reset(); 137 | path.moveTo((float) (x + width), (float) (y + dy)); 138 | path.lineTo((float) (x + dx), (float) (y + dy)); 139 | path.lineTo((float) (x + dx), (float) (y + height)); 140 | g2.setPaint(borderColor); 141 | g2.draw(path); 142 | 143 | path.reset(); 144 | path.moveTo((float) (x + width), (float) y); 145 | path.lineTo((float) (x + width), (float) (y + height)); 146 | path.lineTo((float) x, (float) (y + height)); 147 | g2.setPaint(borderColor); 148 | g2.draw(path); 149 | 150 | path.reset(); 151 | path.moveTo((float) (x + width - dx), (float) (y + dy)); 152 | path.lineTo((float) (x + width - dx), (float) (y + height - dy)); 153 | path.lineTo((float) x, (float) (y + height - dy)); 154 | g2.setPaint(borderColor); 155 | g2.draw(path); 156 | 157 | paintContext.setRenderQuality(old_quality); 158 | 159 | } 160 | 161 | /** 162 | * Changes the paint that will be used to draw this rectangle. This paint is 163 | * used to shade the edges of the rectangle. 164 | * 165 | * @param newPaint the color to use for painting this rectangle 166 | */ 167 | public void setPaint(final Paint newPaint) { 168 | super.setPaint(newPaint); 169 | 170 | if (newPaint instanceof Color) { 171 | final Color color = (Color) newPaint; 172 | borderColor = color.darker().darker(); 173 | } 174 | 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/io/PEDReader.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.io; 9 | 10 | import geneaquilt.data.Edge; 11 | import geneaquilt.data.Fam; 12 | import geneaquilt.data.Indi; 13 | import geneaquilt.data.Network; 14 | import geneaquilt.data.Vertex; 15 | 16 | import java.io.BufferedReader; 17 | import java.io.FileReader; 18 | 19 | /** 20 | * Class PEDReader 21 | * 22 | * See http://pngu.mgh.harvard.edu/~purcell/plink/data.shtml 23 | * 24 | * @author Jean-Daniel Fekete 25 | */ 26 | public class PEDReader { 27 | private Network network; 28 | private String[] tmpLine = new String[7]; 29 | private static final String DELIMS = " \t"; 30 | 31 | /** 32 | * Creates a PED Reader. 33 | */ 34 | public PEDReader() { } 35 | 36 | private Indi findIndi(String fam, String id) { 37 | if (id.equals("0") || id.equals(".")) 38 | return null; 39 | id = "I"+fam+"_"+id; 40 | Indi indi = (Indi)network.getVertex(id); 41 | if (indi == null) { 42 | indi = new Indi(); 43 | indi.setId(id); 44 | network.addVertex(indi); 45 | } 46 | return indi; 47 | } 48 | 49 | private Fam findFam(String f, String wife, String husb) { 50 | Indi w = null; 51 | Indi h = null; 52 | 53 | if (wife.equals("0") || wife.equals(".")) { 54 | wife = null; 55 | } 56 | else { 57 | w = findIndi(f, wife); 58 | } 59 | if (husb.equals("0") || husb.equals(".")) { 60 | husb = null; 61 | } 62 | else { 63 | h = findIndi(f, husb); 64 | } 65 | 66 | if (wife == null && husb == null) 67 | return null; 68 | 69 | String id = "F"+f+"_"+(wife==null?"unknown":wife)+"_"+(husb==null?"unkown":husb); 70 | Fam fam = (Fam)network.getVertex(id); 71 | if (fam == null) { 72 | fam = new Fam(); 73 | fam.setId(id); 74 | fam.setHusb(h.getId()); 75 | h.addFams(fam.getId()); 76 | fam.setWife(w.getId()); 77 | w.addFams(fam.getId()); 78 | network.addVertex(fam); 79 | } 80 | return fam; 81 | } 82 | 83 | private String getSex(String s) { 84 | if (s == null) return null; 85 | if (s.equals("1")) return "M"; 86 | if (s.equals("2")) return "F"; 87 | return null; 88 | } 89 | 90 | private static int nextDelim(String line, int offset) { 91 | int len = line.length(); 92 | for (int i = offset; i < len; i++) { 93 | char c = line.charAt(i); 94 | if (DELIMS.indexOf(c) != -1) 95 | return i; 96 | } 97 | return -1; 98 | } 99 | 100 | private static int skipDelims(String line, int offset) { 101 | while (DELIMS.indexOf(line.charAt(offset)) != -1) 102 | offset++; 103 | return offset; 104 | } 105 | 106 | private String[] split(String line) { 107 | line = line.trim(); 108 | if (line.length() == 0 || line.startsWith("#") || line.equals("end")) 109 | return null; 110 | int last = skipDelims(line, 0); 111 | for (int i = 0; i < 6; i++) { 112 | int next = nextDelim(line, last); 113 | if (next == -1) { 114 | tmpLine[i++] = line.substring(last); 115 | while (i < 7) { 116 | tmpLine[i++] = null; 117 | } 118 | return tmpLine; 119 | } 120 | else 121 | tmpLine[i] = line.substring(last, next); 122 | last = skipDelims(line, next+1); 123 | } 124 | tmpLine[6] = line.substring(last); 125 | return tmpLine; 126 | } 127 | 128 | /** 129 | * Loads a network. 130 | * @param filename the file name 131 | * @return a network 132 | */ 133 | public Network load(String filename) { 134 | try { 135 | FileReader fin = new FileReader(filename); 136 | BufferedReader in = new BufferedReader(fin); 137 | network = new Network(); 138 | 139 | String line; 140 | int lineNum = 0; 141 | while ((line = in.readLine()) != null) { 142 | lineNum++; 143 | String[] field = split(line); 144 | if (field == null) 145 | continue; 146 | String f = field[0]; 147 | Indi indi = findIndi(f, field[1]); 148 | assert(indi.getFamc() == null); 149 | indi.setProperty("FAMILY", field[0]); 150 | indi.setName(field[1]); 151 | indi.setSex(getSex(field[4])); 152 | if (field[5] != null) { 153 | indi.setProperty("PHENOTYPE", field[5]); 154 | } 155 | if (field[6] != null) { 156 | indi.setProperty("DATA", field[6]); 157 | } 158 | Fam fam = findFam(f, field[3], field[2]); 159 | if (fam != null) { 160 | indi.setFamc(fam.getId()); 161 | fam.addChil(indi.getId()); 162 | } 163 | } 164 | in.close(); 165 | fin.close(); 166 | } 167 | catch(Exception e) { 168 | e.printStackTrace(); 169 | } 170 | for (Vertex v : network.getVertices()) { 171 | if (v instanceof Indi) { 172 | Indi indi = (Indi) v; 173 | if (indi.getFamc() != null) { 174 | Vertex fam = network.getVertex(indi.getFamc()); 175 | if (fam != null) { 176 | Edge edge = new Edge(v.getId(), indi.getFamc()); 177 | if (!network.containsEdge(edge)) { 178 | network.addEdge(edge, indi, fam); 179 | edge.setFromVertex(v); 180 | edge.setToVertex(fam); 181 | } 182 | } 183 | } 184 | if (indi.getFams() != null) { 185 | for (String fid : indi.getFams()) { 186 | Vertex fam = network.getVertex(fid); 187 | if (fam != null) { 188 | Edge edge = new Edge(fam.getId(), v.getId()); 189 | if (!network.containsEdge(edge)) { 190 | network.addEdge(edge, fam, indi); 191 | edge.setFromVertex(fam); 192 | edge.setToVertex(indi); 193 | } 194 | } 195 | } 196 | } 197 | } 198 | } 199 | return network; 200 | } 201 | } 202 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/algorithms/LayerRank.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.algorithms; 9 | 10 | import geneaquilt.data.Indi; 11 | import geneaquilt.data.Network; 12 | import geneaquilt.data.Vertex; 13 | 14 | import java.util.Collection; 15 | import java.util.HashMap; 16 | import java.util.HashSet; 17 | import java.util.Map; 18 | import java.util.Set; 19 | 20 | /** 21 | * 22 | * Class LayerRank 23 | * 24 | * @author Jean-Daniel Fekete 25 | * @version $Revision$ 26 | */ 27 | public class LayerRank extends AbstractAlgorithm { 28 | /** Name of the algorithm */ 29 | public static final String LAYER = "layer"; 30 | 31 | private Map rank = new HashMap(); 32 | private Set unprocessed = new HashSet(); 33 | private int maxRank = -1; 34 | // private Set movedup = new HashSet(); 35 | 36 | /** 37 | * Constructor which initializes the algorithm 38 | * @param g the graph whose nodes are to be analyzed 39 | */ 40 | public LayerRank(Network g) { 41 | super(g); 42 | } 43 | 44 | /** 45 | * {@inheritDoc} 46 | */ 47 | @Override 48 | public void compute() { 49 | computeLayers(); 50 | } 51 | 52 | private void computeLayers() { 53 | Collection vertices = network.getVertices(); 54 | rank.clear(); 55 | unprocessed.clear(); 56 | unprocessed.addAll(vertices); 57 | int maxLayers = 0; 58 | //Vertex longestRoot = null; 59 | 60 | for (Vertex v : vertices) { 61 | if (network.getSuccessorCount(v)==0) { 62 | int layers = setRoot(v); 63 | if (layers > maxLayers) { 64 | maxLayers = layers; 65 | //longestRoot = v; 66 | } 67 | } 68 | } 69 | if (! unprocessed.isEmpty()) { 70 | // loops 71 | System.err.println("Loops found in graph"); 72 | Vertex best = null; 73 | int lowest = maxRank+1; 74 | for (Vertex v : unprocessed) { 75 | // Look for the lowest ranked neighbor to cut 76 | for (Vertex other : network.getPredecessors(v)) { 77 | Integer i = rank.get(other); 78 | if (i != null && i.intValue() < lowest) { 79 | best = other; 80 | lowest = i.intValue(); 81 | } 82 | } 83 | // cut at V 84 | if (best != null) 85 | rank.put(v, new Integer(lowest-1)); 86 | else 87 | rank.put(v, new Integer(0)); 88 | } 89 | } 90 | for (Vertex v : vertices) { 91 | if (network.getPredecessorCount(v)==0) 92 | setTop(v); 93 | } 94 | //bumpUp(vertices); 95 | } 96 | 97 | private int setRoot(Vertex v) { 98 | int r = (v instanceof Indi) ? 0 : 1; 99 | 100 | rank.put(v, new Integer(r)); 101 | unprocessed.remove(v); 102 | System.out.println("Root: "+v); 103 | return assignRank(v, r); 104 | } 105 | 106 | private int assignRank(Vertex v, int level) { 107 | int maxRank = level; 108 | for (Vertex child : network.getPredecessors(v)) { 109 | Integer o = rank.get(child); 110 | int newRank; 111 | if (o != null) { 112 | int oldRank = o.intValue(); 113 | newRank = Math.max(oldRank, level+1); 114 | } 115 | else 116 | newRank = level+1; 117 | rank.put(child, new Integer(newRank)); 118 | unprocessed.remove(child); 119 | if (newRank > maxRank) 120 | maxRank = newRank; 121 | assignRank(child, newRank); 122 | } 123 | if (maxRank > this.maxRank) 124 | this.maxRank = maxRank; 125 | return maxRank; 126 | } 127 | 128 | // private int maxPred(Vertex v) { 129 | // int m = -1; 130 | // for (Vertex child : graph.getPredecessors(v)) { 131 | // m = Math.max(m, rank.get(child).intValue()); 132 | // } 133 | // return m; 134 | // } 135 | 136 | private int minPred(Vertex v) { 137 | int m = Integer.MAX_VALUE; 138 | for (Vertex child : network.getPredecessors(v)) { 139 | m = Math.min(m, rank.get(child).intValue()); 140 | } 141 | if (m == Integer.MAX_VALUE) 142 | return -1; 143 | return m; 144 | } 145 | 146 | // private void bumpUp(Collection vertices) { 147 | // Vertex[] sorted = new Vertex[vertices.size()]; 148 | // Comparator comparator = new Comparator() { 149 | // public int compare(Vertex o1, Vertex o2) { 150 | // return rank.get(o2).intValue()-rank.get(o1).intValue(); 151 | // } 152 | // }; 153 | // 154 | // vertices.toArray(sorted); 155 | // //TreeSet heap = new TreeSet( 156 | // //MapBinaryHeap heap = new MapBinaryHeap( 157 | // Arrays.sort(sorted, comparator); 158 | // for (int n = 0; n < sorted.length; n++) { 159 | // Vertex child = sorted[n]; 160 | // int oldRank = rank.get(child).intValue(); 161 | // int newRank = minPred(child)-1; 162 | // if (newRank > 0 && oldRank != newRank) { 163 | // if (newRank < oldRank) 164 | // assert(false); 165 | // System.out.println("Moved "+child+" from "+oldRank+" to "+newRank); 166 | // rank.put(child, new Integer(newRank)); 167 | // int index = Arrays.binarySearch(sorted, child, comparator); 168 | // if (index < 0) { 169 | // index = -index-1; 170 | // } 171 | // System.arraycopy(sorted, index, sorted, index+1, n-index); 172 | // sorted[index] = child; 173 | // n = index; 174 | // } 175 | // } 176 | // } 177 | 178 | private void setTop(Vertex v) { 179 | for (Vertex parent : network.getSuccessors(v)) { 180 | int oldRank = rank.get(parent).intValue(); 181 | int newRank = minPred(parent)-1; 182 | if (newRank > oldRank) { 183 | rank.put(parent, new Integer(newRank)); 184 | } 185 | setTop(parent); 186 | } 187 | } 188 | 189 | 190 | /** 191 | * @return the graph 192 | */ 193 | public Network getGraph() { 194 | return network; 195 | } 196 | 197 | /** 198 | * Returns the rank of the specified vertex 199 | * @param v the vertex 200 | * @return the rank or -1 201 | */ 202 | public int getRank(Vertex v) { 203 | Integer i = rank.get(v); 204 | if (i != null) 205 | return i.intValue(); 206 | return -1; 207 | } 208 | 209 | /** 210 | * @return the maxRank 211 | */ 212 | public int getMaxRank() { 213 | return maxRank; 214 | } 215 | } 216 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | geneaquilt 4 | geneaquilt 5 | jar 6 | Genealogy Quilt 7 | 1.0.13-SNAPSHOT 8 | http://geneaquilt.gforge.inria.fr 9 | 10 | 11 | BSD License 12 | http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5 13 | repo 14 | 15 | 16 | 17 | scm:git:git@github.com:jdfekete/geneaquilt.git 18 | scm:git:git@github.com:jdfekete/geneaquilt.git 19 | https://github.com/jdfekete/geneaquilt/ 20 | geneaquilt-1.0.11-SNAPSHOT 21 | 22 | 23 | 24 | 25 | internal.repo 26 | Temporary Staging Repository 27 | file://${project.build.directory}/mvn-repo 28 | 29 | 30 | 31 | 32 | UTF-8 33 | 34 | 35 | 36 | 37 | freehep-maven 38 | Maven FreeHEP 39 | http://java.freehep.org/maven2 40 | 41 | 42 | 43 | 44 | 45 | org.piccolo2d 46 | piccolo2d-core 47 | 1.2.1 48 | 49 | 50 | org.freehep 51 | freehep-graphicsio-pdf 52 | 2.3 53 | 54 | 55 | net.sf.jung 56 | jung-api 57 | 2.0 58 | 59 | 60 | net.sf.jung 61 | jung-algorithms 62 | 2.0 63 | 64 | 65 | net.sf.jung 66 | jung-graph-impl 67 | 2.0 68 | 69 | 70 | log4j 71 | log4j 72 | 1.2.12 73 | 74 | 75 | xmlenc 76 | xmlenc 77 | 0.52 78 | 79 | 80 | org.gedcom4j 81 | gedcom4j 82 | 2.1.8 83 | 84 | 85 | 86 | 87 | 88 | src/main/resources 89 | 90 | 91 | src/site/resources 92 | true 93 | 94 | 95 | 96 | 97 | org.apache.maven.plugins 98 | maven-release-plugin 99 | 2.5 100 | 101 | 102 | maven-resources-plugin 103 | 2.4.3 104 | 105 | 106 | copy-resources 107 | 108 | validate 109 | 110 | copy-resources 111 | 112 | 113 | UTF-8 114 | ${basedir}/target/data 115 | 116 | 117 | src/data 118 | false 119 | 120 | 121 | 122 | 123 | 124 | copy-scripts 125 | 126 | validate 127 | 128 | copy-resources 129 | 130 | 131 | ASCII 132 | ${basedir}/target 133 | 134 | 135 | . 136 | 137 | run.sh 138 | run.bat 139 | README.* 140 | LICENCE 141 | 142 | true 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | org.apache.maven.plugins 151 | maven-shade-plugin 152 | 2.3 153 | 154 | 155 | package 156 | 157 | shade 158 | 159 | 160 | 161 | 162 | geneaquilt.GeneaQuilt 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | true 171 | org.apache.maven.plugins 172 | maven-source-plugin 173 | 2.1.2 174 | 175 | 176 | attach-sources 177 | 178 | jar 179 | 180 | 181 | 182 | 183 | 184 | org.apache.maven.plugins 185 | maven-compiler-plugin 186 | 2.3.2 187 | 188 | 1.5 189 | 1.5 190 | true 191 | 192 | 193 | 194 | org.apache.maven.plugins 195 | maven-eclipse-plugin 196 | 2.8 197 | 198 | true 199 | 200 | 201 | 202 | maven-assembly-plugin 203 | 2.2 204 | 205 | 206 | src/assemble/distribution.xml 207 | 208 | 209 | 210 | 211 | make-assembly 212 | 213 | package 214 | 215 | 216 | single 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/data/Indi.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.data; 9 | 10 | import edu.umd.cs.piccolo.PNode; 11 | import geneaquilt.nodes.PIndi; 12 | import geneaquilt.nodes.PSemanticText; 13 | 14 | import java.util.ArrayList; 15 | import java.util.Collections; 16 | import java.util.List; 17 | 18 | /** 19 | * Class Indi 20 | * 21 | * @author Jean-Daniel Fekete 22 | * @version $Revision$ 23 | */ 24 | public class Indi extends Vertex { 25 | private ArrayList fams; 26 | private String famc; 27 | /** Use that attribute for names */ 28 | private transient String label; 29 | 30 | 31 | /** 32 | * Creates a new Indi. 33 | */ 34 | public Indi() { 35 | } 36 | 37 | /** 38 | * {@inheritDoc} 39 | */ 40 | public void setId(String id) { 41 | super.setId(id); 42 | setProperty("ID", id); 43 | } 44 | 45 | protected PNode createNode() { 46 | return new PIndi(this); 47 | } 48 | 49 | /** 50 | * @return a suitable label 51 | */ 52 | public String getLabel() { 53 | if (label == null) { 54 | label = getSexString()+getName(); 55 | } 56 | return label; 57 | } 58 | 59 | /** 60 | * Change the label 61 | * @param prop 62 | */ 63 | public void setLabelBy(String prop) { 64 | String old = label; 65 | 66 | Object l = getProperty(prop); 67 | if (l == null) { 68 | l = getName(); 69 | } 70 | if (l == null) { 71 | label = getSexString(); 72 | } 73 | else { 74 | label = getSexString() + l.toString(); 75 | } 76 | if (getNode() != null 77 | && (old == null || ! old.equals(label))) { 78 | ((PSemanticText)getNode()).setText(label); 79 | } 80 | } 81 | 82 | /** 83 | * @return the unicode string related to the sex 84 | */ 85 | public String getSexString() { 86 | if ("M".equalsIgnoreCase(getSex())) { 87 | return UNICODE_MALE + " "; 88 | } 89 | else if ("F".equalsIgnoreCase(getSex())) { 90 | return UNICODE_FEMALE + " "; 91 | } 92 | else 93 | return " "; 94 | } 95 | 96 | /** 97 | * {@inheritDoc} 98 | */ 99 | @Override 100 | public String toString() { 101 | return "Indi["+getId()+":"+getName()+"]"; 102 | } 103 | /** 104 | * @return the name 105 | */ 106 | public String getName() { 107 | return getStringProperty("NAME"); 108 | } 109 | 110 | /** 111 | * @param name the name to set 112 | */ 113 | public void setName(String name) { 114 | setProperty("NAME", name); 115 | } 116 | 117 | /** 118 | * @return the surname 119 | */ 120 | public String getSurname() { 121 | return getStringProperty("NAME.SURN"); 122 | } 123 | 124 | /** 125 | * @return the given name 126 | */ 127 | public String getGiven() { 128 | return getStringProperty("NAME.GIVN"); 129 | } 130 | 131 | 132 | /** 133 | * @return the fams 134 | */ 135 | public List getFams() { 136 | if (fams == null) 137 | return Collections.EMPTY_LIST; 138 | return fams; 139 | } 140 | 141 | /** 142 | * @param fams the fams to set 143 | */ 144 | public void setFams(ArrayList fams) { 145 | this.fams = fams; 146 | } 147 | 148 | /** 149 | * Adds a fams 150 | * @param f the fams 151 | */ 152 | public void addFams(String f) { 153 | if (fams == null) { 154 | fams = new ArrayList(); 155 | } 156 | else { 157 | if (fams.contains(f)) 158 | return; 159 | } 160 | fams.add(f); 161 | } 162 | 163 | /** 164 | * @return the famc 165 | */ 166 | public String getFamc() { 167 | return famc; 168 | } 169 | 170 | /** 171 | * @param famc the famc to set 172 | */ 173 | public void setFamc(String famc) { 174 | this.famc = famc; 175 | } 176 | 177 | /** 178 | * @return the sex 179 | */ 180 | public String getSex() { 181 | return getStringProperty("SEX"); 182 | } 183 | 184 | /** 185 | * @param sex the sex to set 186 | */ 187 | public void setSex(String sex) { 188 | setProperty("SEX", sex); 189 | } 190 | 191 | /** 192 | * @return the birth 193 | */ 194 | public DateRange getBirth() { 195 | Object d = getProperty("BIRT.DATE"); 196 | if (d == null) 197 | return null; 198 | return (DateRange)d; 199 | } 200 | 201 | /** 202 | * @return a birth date, maybe invalid, never null 203 | */ 204 | public DateRange findBirth() { 205 | DateRange d = getBirth(); 206 | if (d == null) { 207 | d = new DateRange(); 208 | d.clear(); 209 | setDate("BIRT.DATE", d); 210 | } 211 | return d; 212 | } 213 | 214 | /** 215 | * @param birth the birth to set 216 | */ 217 | public void setBirth(String birth) { 218 | setDate("BIRT.DATE", birth); 219 | } 220 | 221 | /** 222 | * @return the death 223 | */ 224 | public DateRange getDeath() { 225 | Object d = getProperty("DEAT.DATE"); 226 | if (d == null) 227 | return null; 228 | return (DateRange)d; 229 | } 230 | 231 | /** 232 | * @return a death date, maybe invalid, never null 233 | */ 234 | public DateRange findDeath() { 235 | DateRange d = getDeath(); 236 | if (d == null) { 237 | d = new DateRange(); 238 | d.clear(); 239 | setDate("DEAT.DATE", d); 240 | } 241 | return d; 242 | } 243 | 244 | /** 245 | * @param death the death to set 246 | */ 247 | public void setDeath(String death) { 248 | setDate("DEAT.DATE", death); 249 | } 250 | 251 | /** 252 | * @return the christening date 253 | */ 254 | public DateRange getChr() { 255 | Object d = getProperty("CHR.DATE"); 256 | if (d == null) 257 | return null; 258 | return (DateRange)d; 259 | } 260 | 261 | /** 262 | * @return a christening date, maybe invalid, never null 263 | */ 264 | public DateRange findChr() { 265 | DateRange d = getChr(); 266 | if (d == null) { 267 | d = new DateRange(); 268 | d.clear(); 269 | setDate("CHR.DATE", d); 270 | } 271 | return d; 272 | } 273 | 274 | /** 275 | * @return the burial date 276 | */ 277 | public DateRange getBurial() { 278 | Object d = getProperty("BURI.DATE"); 279 | if (d == null) 280 | return null; 281 | return (DateRange)d; 282 | } 283 | 284 | /** 285 | * @return a burial date, maybe invalid, never null 286 | */ 287 | public DateRange findBurial() { 288 | DateRange d = getBurial(); 289 | if (d == null) { 290 | d = new DateRange(); 291 | d.clear(); 292 | setDate("BURI.DATE", d); 293 | } 294 | return d; 295 | } 296 | } 297 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/DetailsTable.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt; 9 | 10 | import edu.umd.cs.piccolo.PNode; 11 | import geneaquilt.data.Edge; 12 | import geneaquilt.data.Fam; 13 | import geneaquilt.data.Indi; 14 | import geneaquilt.data.Vertex; 15 | import geneaquilt.nodes.PEdge; 16 | import geneaquilt.nodes.PFam; 17 | import geneaquilt.nodes.PIndi; 18 | import geneaquilt.selection.Selection; 19 | import geneaquilt.selection.SelectionManager; 20 | 21 | import java.awt.Color; 22 | import java.awt.Component; 23 | import java.awt.Dimension; 24 | import java.awt.Font; 25 | import java.util.ArrayList; 26 | import java.util.Vector; 27 | import java.util.Map.Entry; 28 | 29 | import javax.swing.JScrollPane; 30 | import javax.swing.JTable; 31 | import javax.swing.SwingConstants; 32 | import javax.swing.event.ChangeEvent; 33 | import javax.swing.event.ChangeListener; 34 | import javax.swing.table.DefaultTableCellRenderer; 35 | import javax.swing.table.DefaultTableModel; 36 | import javax.swing.table.JTableHeader; 37 | import javax.swing.table.TableCellRenderer; 38 | import javax.swing.table.TableColumnModel; 39 | 40 | /** 41 | * DetailsTable Visualize the details of selected items. 42 | * 43 | * @author Jean-Daniel Fekete 44 | */ 45 | public class DetailsTable extends JTable implements ChangeListener { 46 | SelectionManager selManager; 47 | DefaultTableModel model; 48 | JScrollPane scroll; 49 | ArrayList cellColor = new ArrayList(); 50 | 51 | static Vector emptyLine = new Vector(); 52 | static { 53 | emptyLine.add(""); 54 | emptyLine.add(""); 55 | } 56 | 57 | /** 58 | * Creates a details table looking at the selection. 59 | * 60 | * @param selManager 61 | * the selection manager 62 | */ 63 | public DetailsTable(SelectionManager selManager) { 64 | this.selManager = selManager; 65 | setFont(new Font("Helvetica", 0, 11)); 66 | setRowHeight(13); 67 | selManager.addChangeListener(this); 68 | model = (DefaultTableModel) getModel(); 69 | model.addColumn("Attribute"); 70 | model.addColumn("Value"); 71 | setAutoResizeMode(JTable.AUTO_RESIZE_OFF); 72 | TableColumnModel tcm = getColumnModel(); 73 | tcm.getColumn(0).setPreferredWidth(75); 74 | tcm.getColumn(0).setMinWidth(75); 75 | tcm.getColumn(0).setCellRenderer(new DefaultTableCellRenderer() { 76 | public Component getTableCellRendererComponent( 77 | JTable table, Object value, boolean isSelected, 78 | boolean hasFocus, int row, int column) { 79 | super.getTableCellRendererComponent( 80 | table, 81 | value, 82 | isSelected, 83 | hasFocus, 84 | row, 85 | column); 86 | setBackground(cellColor.get(row)); 87 | return this; 88 | } 89 | }); 90 | tcm.getColumn(1).setPreferredWidth(150); 91 | tcm.getColumn(1).setMinWidth(150); 92 | tcm.getColumn(1).setCellRenderer(new DefaultTableCellRenderer() { 93 | public Component getTableCellRendererComponent( 94 | JTable table, Object value, boolean isSelected, 95 | boolean hasFocus, int row, int column) { 96 | super.getTableCellRendererComponent( 97 | table, value, isSelected, 98 | hasFocus, row, column); 99 | String v = value == null ? "" : value.toString(); 100 | if (v.indexOf('\n') != -1) { 101 | v = ""+v.replace("\n", "
") + ""; 102 | } 103 | setToolTipText(v); 104 | return this; 105 | } 106 | }); 107 | 108 | setPreferredScrollableViewportSize(getPreferredSize()); 109 | } 110 | 111 | /** 112 | * {@inheritDoc} 113 | */ 114 | public boolean isCellEditable(int row, int column) { 115 | return false; 116 | } 117 | 118 | /** 119 | * @return a scroll pane on that table 120 | */ 121 | public JScrollPane getScrollPane() { 122 | if (scroll == null) { 123 | scroll = new JScrollPane(this); 124 | JTableHeader h = getTableHeader(); 125 | DefaultTableCellRenderer dcr = (DefaultTableCellRenderer) h 126 | .getDefaultRenderer(); 127 | dcr.setHorizontalAlignment(SwingConstants.LEFT); 128 | h.setReorderingAllowed(false); 129 | 130 | scroll.setColumnHeaderView(h); 131 | scroll.setPreferredSize(new Dimension(200, 300)); 132 | } 133 | return scroll; 134 | } 135 | 136 | /** 137 | * {@inheritDoc} 138 | */ 139 | public void stateChanged(ChangeEvent e) { 140 | updateSelection(); 141 | } 142 | 143 | protected void updateSelection() { 144 | model.setNumRows(0); 145 | cellColor.clear(); 146 | for (Selection s : selManager.getSelections()) { 147 | PNode n = s.getSelectedObject(); 148 | Color c = s.getStrongColor(); 149 | if (n instanceof PIndi) { 150 | PIndi pindi = (PIndi) n; 151 | Indi indi = pindi.getIndi(); 152 | update(indi, c); 153 | } 154 | else if (n instanceof PFam) { 155 | PFam pfam = (PFam) n; 156 | Fam fam = pfam.getFam(); 157 | update(fam, c); 158 | } 159 | if (n instanceof PEdge) { 160 | PEdge pedge = (PEdge) n; 161 | Edge edge = pedge.getEdge(); 162 | updateEdge(edge, c); 163 | } 164 | } 165 | } 166 | 167 | protected void update(Vertex v, Color c) { 168 | 169 | if (model.getRowCount() > 0) { 170 | model.addRow(emptyLine); 171 | cellColor.add(Color.white); 172 | setRowHeight(model.getRowCount() - 1, 6); 173 | } 174 | 175 | for (Entry entry : v.getProps().entrySet()) { 176 | Vector row = new Vector(); 177 | row.add(entry.getKey()); 178 | row.add(entry.getValue()); 179 | model.addRow(row); 180 | setRowHeight(model.getRowCount() - 1, getRowHeight()); 181 | cellColor.add(c); 182 | } 183 | 184 | } 185 | 186 | protected void updateEdge(Edge edge, Color c) { 187 | } 188 | 189 | static class TextAreaRenderer extends DefaultTableCellRenderer 190 | implements TableCellRenderer { 191 | public Component getTableCellRendererComponent( 192 | JTable jTable, 193 | Object obj, 194 | boolean isSelected, 195 | boolean hasFocus, 196 | int row, 197 | int column) { 198 | String v = obj.toString(); 199 | super.getTableCellRendererComponent( 200 | jTable, 201 | v, 202 | isSelected, 203 | hasFocus, 204 | row, 205 | column); 206 | if (v.indexOf('\n') != -1) { 207 | v = ""+v.replace("\n", "
") + ""; 208 | } 209 | setToolTipText(v); 210 | return this; 211 | } 212 | } 213 | 214 | } 215 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/selection/SelectionManager.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.selection; 9 | 10 | import edu.umd.cs.piccolo.PNode; 11 | import geneaquilt.data.Edge; 12 | import geneaquilt.data.Network; 13 | import geneaquilt.data.Vertex; 14 | import geneaquilt.nodes.GraphicsConstants; 15 | import geneaquilt.nodes.PEdge; 16 | import geneaquilt.nodes.PFam; 17 | import geneaquilt.nodes.PIndi; 18 | import geneaquilt.nodes.PVertex; 19 | import geneaquilt.nodes.QuiltManager; 20 | import geneaquilt.selection.highlight.HighlightManager; 21 | import geneaquilt.selection.highlight.SelectionCombination; 22 | import geneaquilt.selection.highlight.SelectionHighlight; 23 | 24 | import java.awt.Color; 25 | import java.util.ArrayList; 26 | import java.util.Collection; 27 | import java.util.HashSet; 28 | import java.util.Set; 29 | 30 | import javax.swing.JMenuItem; 31 | import javax.swing.event.ChangeEvent; 32 | import javax.swing.event.ChangeListener; 33 | 34 | /** 35 | * SelectionManager manages selections. 36 | * 37 | * @author Pierre Dragicevic 38 | */ 39 | public class SelectionManager { 40 | 41 | private QuiltManager quiltManager; 42 | private HighlightManager highlightManager; 43 | private ArrayList selections; 44 | private int nextSelectionColorIndex = 0; 45 | private ArrayList changeListeners = new ArrayList(); 46 | private int inhibitNotify = 0; 47 | JMenuItem exportSelectionItem = null; // to disable and unable selection export 48 | 49 | /** 50 | * Creates a selection manager managing a quilt manager. 51 | * @param quiltManager the quilt manager 52 | * @param backgroundLayer the background layer node 53 | * @param foregroundLayer the foreground layer node 54 | */ 55 | public SelectionManager( 56 | QuiltManager quiltManager, 57 | PNode selectionLayer) { 58 | this.quiltManager = quiltManager; 59 | this.highlightManager = new HighlightManager(quiltManager.getFullBoundsReference()); 60 | selectionLayer.addChild(highlightManager); 61 | selections = new ArrayList(); 62 | } 63 | 64 | /** 65 | * @return the network 66 | */ 67 | public Network getNetwork() { 68 | return quiltManager.getNetwork(); 69 | } 70 | 71 | /** 72 | * @return the selected network 73 | */ 74 | public Network getSelectedNetwork() { 75 | 76 | // Built the list of selected vertices and edges 77 | Set vertices = new HashSet(); 78 | Set edges = new HashSet(); 79 | for (Selection s : selections) { 80 | Set nodes = s.getHighlightedObjects(); 81 | for (PNode n : nodes) 82 | if (n instanceof PVertex) 83 | vertices.add(((PVertex)n).getVertex()); 84 | else if (n instanceof PEdge) 85 | edges.add(((PEdge)n).getEdge()); 86 | } 87 | 88 | // Build the network 89 | Network selNetwork = new Network(); 90 | for (Vertex v : vertices) 91 | selNetwork.addVertex(v); 92 | for (Edge e : edges) 93 | selNetwork.addEdge(e, e.getFromVertex(), e.getToVertex()); 94 | 95 | return selNetwork; 96 | } 97 | 98 | 99 | /** 100 | * @return the foreground layer 101 | */ 102 | public HighlightManager getHighlightManager() { 103 | return highlightManager; 104 | } 105 | 106 | /** 107 | * Removes all selections 108 | */ 109 | public void clearSelections() { 110 | highlightManager.clear(); 111 | SelectionCombination.clear(); 112 | selections.clear(); 113 | nextSelectionColorIndex = 0; 114 | fireChangeListeners(); 115 | highlightManager.setTestOverlap(false); 116 | } 117 | 118 | /** 119 | * @return if the selection is empty 120 | */ 121 | public boolean isEmpty() { 122 | return selections.isEmpty(); 123 | } 124 | 125 | /** 126 | * Returns true if the specified node is selectable 127 | * @param node the node 128 | * @return true if it can be selected 129 | */ 130 | public boolean isSelectable(PNode node) { 131 | if (node == null) 132 | return false; 133 | // if (isSelected(node)) 134 | // return false; 135 | return node instanceof PEdge 136 | || node instanceof PFam 137 | || node instanceof PIndi; 138 | } 139 | 140 | /** 141 | * Selects the specified node. 142 | * @param node the node 143 | * @return the new selection object or null 144 | */ 145 | public Selection select(PNode node) { 146 | if (!isSelectable(node)) 147 | return null; 148 | highlightManager.setTestOverlap(selections.size() > 0); 149 | Selection newSelection = new Selection(this, node, getNextSelectionColor()); 150 | selections.add(newSelection); 151 | nextSelectionColorIndex++; 152 | fireChangeListeners(); 153 | return newSelection; 154 | } 155 | 156 | /** 157 | * Returns true if the specified node is selected. 158 | * @param node the node 159 | * @return true/false 160 | */ 161 | public boolean isSelected(PNode node) { 162 | for (Selection s : selections) 163 | if (s.getSelectedObject() == node) 164 | return true; 165 | return false; 166 | } 167 | 168 | /** 169 | * Returns the selection associated with the node 170 | * @param node the node 171 | * @return the selection or null 172 | */ 173 | public Selection getLastSelection(PNode node) { 174 | // for (Selection s : selections) 175 | // if (s.getSelectedObject() == node) 176 | // return s; 177 | SelectionHighlight h = highlightManager.getSelectionHighlight(node); 178 | if (h == null) 179 | return null; 180 | return h.getLastSelection(); 181 | } 182 | 183 | /** 184 | * Sets the selection color index for the next selection 185 | * @param index the new index 186 | */ 187 | public void setNextSelectionColorIndex(int index) { 188 | nextSelectionColorIndex = index; 189 | } 190 | 191 | /** 192 | * @return the selection color index for the next selection 193 | */ 194 | public int getNextSelectionColorIndex() { 195 | return nextSelectionColorIndex; 196 | } 197 | 198 | /** 199 | * @return the selection color for the next selection 200 | */ 201 | public Color getNextSelectionColor() { 202 | return GraphicsConstants.SELECTION_COLORS[nextSelectionColorIndex % GraphicsConstants.SELECTION_COLORS.length]; 203 | } 204 | 205 | /** 206 | * @return the selections 207 | */ 208 | public Collection getSelections() { 209 | return selections; 210 | } 211 | 212 | /** 213 | * Fires a change 214 | */ 215 | public void fireChangeListeners() { 216 | if (inhibitNotify != 0) 217 | return; 218 | if (changeListeners.isEmpty()) 219 | return; 220 | ChangeEvent ev = new ChangeEvent(this); 221 | for (ChangeListener l : changeListeners) { 222 | l.stateChanged(ev); 223 | } 224 | if (exportSelectionItem != null) 225 | exportSelectionItem.setEnabled(selections.size() > 0); 226 | } 227 | 228 | /** 229 | * 230 | * Register a new change listener 231 | * @param l the listener 232 | */ 233 | public void addChangeListener(ChangeListener l) { 234 | changeListeners.add(l); 235 | } 236 | 237 | /** 238 | * Removes the specified change listener 239 | * @param l the listener 240 | */ 241 | public void removeChangeListener(ChangeListener l) { 242 | changeListeners.remove(l); 243 | } 244 | 245 | /** 246 | * Removes all the listeners. 247 | */ 248 | public void removeAllChangerListeners() { 249 | changeListeners.clear(); 250 | } 251 | 252 | public void setExportSelectionItem(JMenuItem exportSelectionItem) { 253 | this.exportSelectionItem = exportSelectionItem; 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /src/main/java/geneaquilt/algorithms/GenerationRank.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2010-2014, Jean-Daniel Fekete, Pierre Dragicevic, and INRIA. 3 | * All rights reserved. 4 | * 5 | * Use of this source code is governed by a BSD-style license that can be 6 | * found in the LICENSE file. 7 | */ 8 | package geneaquilt.algorithms; 9 | 10 | import geneaquilt.data.Edge; 11 | import geneaquilt.data.Fam; 12 | import geneaquilt.data.Network; 13 | import geneaquilt.data.Vertex; 14 | 15 | import java.util.HashSet; 16 | import java.util.LinkedList; 17 | import java.util.Set; 18 | 19 | import org.apache.log4j.Logger; 20 | 21 | /** 22 | * Class GenerationRank 23 | * 24 | * @author Jean-Daniel Fekete 25 | * @version $Revision$ 26 | */ 27 | public class GenerationRank extends AbstractAlgorithm { 28 | private static final Logger LOG = Logger.getLogger(GenerationRank.class); 29 | private Set treeNode = new HashSet(); 30 | private Set treeEdge = new HashSet(); 31 | 32 | /** 33 | * Creates a GenerationRank. 34 | * @param network the network 35 | */ 36 | public GenerationRank(Network network) { 37 | super(network); 38 | } 39 | 40 | /** 41 | * {@inheritDoc} 42 | */ 43 | @Override 44 | public void compute() { 45 | assignLayers(); 46 | } 47 | 48 | private void assignLayers() { 49 | Set cycles = network.getCycles(); 50 | LOG.debug("Cyclic edges: "+cycles.size()); 51 | try { 52 | // for (Edge e : network.getEdges()) 53 | // e.setInverted(cycles.contains(e)); 54 | for (Edge e : cycles) { 55 | network.removeEdge(e); 56 | network.addEdge(e, e.getToVertex(), e.getFromVertex()); 57 | } 58 | 59 | network.resetLayers(); 60 | for (Set comp : network.getComponents()) { 61 | assignLayers(comp); 62 | } 63 | int min = network.getMinLayer(); 64 | if (min != 0) 65 | network.offsetLayer(-min, network.getVertices()); 66 | treeNode.clear(); 67 | treeEdge.clear(); 68 | } 69 | finally { 70 | for (Edge e : cycles) { 71 | network.removeEdge(e); 72 | network.addEdge(e, e.getFromVertex(), e.getToVertex()); 73 | } 74 | } 75 | } 76 | 77 | private void initRank(Set comp) { 78 | LinkedList queue = new LinkedList(); 79 | HashSet processed = new HashSet(); 80 | int ctr = 0; 81 | 82 | for (Vertex v : comp) { 83 | if (network.isOrphan(v)) { 84 | queue.add(v); 85 | } 86 | } 87 | 88 | while (! queue.isEmpty()) { 89 | Vertex v = queue.removeFirst(); 90 | processed.add(v); 91 | ctr++; 92 | int layer = (v instanceof Fam) ? 1 : 0; 93 | for (Vertex p : network.getAscendants(v)) { 94 | layer = Math.max(layer, p.getLayer()+1); 95 | } 96 | v.setLayer(layer); 97 | for (Vertex d : network.getDescendants(v)) { 98 | if (processed.containsAll(network.getAscendants(d))) { 99 | queue.addLast(d); 100 | } 101 | } 102 | } 103 | assert(ctr == comp.size()); 104 | } 105 | 106 | private void assignLayers(Set comp) { 107 | initRank(comp); 108 | feasibleTree(comp); 109 | int min = comp.size(); 110 | for (Vertex v : comp) { 111 | min = Math.min(min, v.getLayer()); 112 | } 113 | if ((min % 2) == 1) // family is always odd 114 | min--; 115 | network.offsetLayer(-min, comp); 116 | } 117 | 118 | private void feasibleTree(Set comp) { 119 | if (comp.size() <= 1) 120 | return; 121 | while (tightTree(comp) < comp.size()) { 122 | Edge e = null; 123 | for (Vertex v : comp) { 124 | for (Edge f : network.getInEdges(v)) { 125 | if (! treeEdge.contains(f) 126 | && incident(f)!=null 127 | && ((e == null) 128 | || (slack(f) < slack(e)))) { 129 | e = f; 130 | } 131 | } 132 | } 133 | if (e != null) { 134 | int delta = slack(e); 135 | if (delta != 0) { 136 | if (incident(e)==network.getDescendant(e)) 137 | delta = -delta;//CHECK 138 | network.offsetLayer(delta, treeNode); 139 | } 140 | else 141 | LOG.error("Unexpected tight node"); 142 | } 143 | } 144 | } 145 | 146 | private Vertex incident(Edge e) { 147 | Vertex source = network.getSource(e); 148 | Vertex dest = network.getDest(e); 149 | if (treeNode.contains(source)) { 150 | if (!treeNode.contains(dest)) 151 | return source; 152 | } 153 | else if (treeNode.contains(dest)) { 154 | return dest; 155 | } 156 | return null; 157 | } 158 | 159 | private int slack(Edge e) { 160 | return network.getSource(e).getLayer()-network.getDest(e).getLayer()-1; 161 | } 162 | 163 | private int tightTree(Set comp) { 164 | treeNode.clear(); 165 | treeEdge.clear(); 166 | for (Vertex v : comp) { 167 | treeSearch(v, comp.size()); 168 | if (! treeEdge.isEmpty()) 169 | break; 170 | } 171 | 172 | return treeNode.size(); 173 | } 174 | 175 | private boolean treeSearch(Vertex v, int n) { 176 | for (Edge e : network.getOutEdges(v)) { 177 | Vertex head = network.getDest(e); 178 | if (!treeNode.contains(head) && slack(e)==0) { 179 | addTreeEdge(e); 180 | if (treeEdge.size()==n-1 || treeSearch(head, n)) { 181 | return true; 182 | } 183 | } 184 | } 185 | for (Edge e : network.getInEdges(v)) { 186 | Vertex tail = network.getSource(e); 187 | if (!treeNode.contains(tail) && slack(e)==0) { 188 | addTreeEdge(e); 189 | if (treeEdge.size()==n-1 || treeSearch(tail, n)) 190 | return true; 191 | } 192 | } 193 | return false; 194 | } 195 | 196 | private void addTreeEdge(Edge e) { 197 | assert(! treeEdge.contains(e)); 198 | 199 | treeEdge.add(e); 200 | treeNode.add(network.getSource(e)); 201 | treeNode.add(network.getDest(e)); 202 | } 203 | 204 | // private int depth(Vertex v) { 205 | // int max = 0; 206 | // for (Vertex d : network.getDescendants(v)) { 207 | // max = Math.max(max, depth(d)); 208 | // } 209 | // return max + 1; 210 | // } 211 | // 212 | // private int assignLayer(Vertex first) { 213 | // int min = network.getVertexCount(); 214 | // first.setLayer(min); 215 | // LinkedList queue = new LinkedList(); 216 | // queue.addLast(first); 217 | // 218 | // while (! queue.isEmpty()) { 219 | // Vertex v = queue.removeFirst(); 220 | // int l = v.getLayer(); 221 | // for (Vertex d : network.getDescendants(v)) { 222 | // if (d.getLayer()!=-1) {// already assigned 223 | // if (d.getLayer() > l) 224 | // continue; 225 | // else 226 | // LOG.debug("Fixing up layer of "+d 227 | // +" from "+d.getLayer()+" to "+(l+1)); 228 | // } 229 | // d.setLayer(l+1); 230 | // LOG.debug("Vertex "+d+"="+Integer.toString(l+1)); 231 | // queue.addLast(d); 232 | // } 233 | // } 234 | // return min; 235 | // } 236 | } 237 | --------------------------------------------------------------------------------