All Downloads are FREE. Search and download functionalities are using the official Maven repository.

edu.cmu.tetradapp.workbench.LagWorkbench Maven / Gradle / Ivy

The newest version!
///////////////////////////////////////////////////////////////////////////////
// For information as to what this class does, see the Javadoc, below.       //
// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,       //
// 2007, 2008, 2009, 2010, 2014, 2015, 2022 by Peter Spirtes, Richard        //
// Scheines, Joseph Ramsey, and Clark Glymour.                               //
//                                                                           //
// This program is free software; you can redistribute it and/or modify      //
// it under the terms of the GNU General Public License as published by      //
// the Free Software Foundation; either version 2 of the License, or         //
// (at your option) any later version.                                       //
//                                                                           //
// This program is distributed in the hope that it will be useful,           //
// but WITHOUT ANY WARRANTY; without even the implied warranty of            //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             //
// GNU General Public License for more details.                              //
//                                                                           //
// You should have received a copy of the GNU General Public License         //
// along with this program; if not, write to the Free Software               //
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA //
///////////////////////////////////////////////////////////////////////////////

package edu.cmu.tetradapp.workbench;

import edu.cmu.tetrad.graph.*;
import edu.cmu.tetradapp.model.EditorUtils;

import java.awt.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.List;


/**
 * Extends AbstractWorkbench in the ways needed to display tetrad-style graphs.
 *
 * @author josephramsey
 * @author Willie Wheeler
 * @version $Id: $Id
 * @see edu.cmu.tetradapp.workbench.AbstractWorkbench
 */
public class LagWorkbench extends AbstractWorkbench {

    //=================PUBLIC STATIC FINAL FIELDS=========================//
    private static final int MEASURED_NODE = 0;
    private static final int LATENT_NODE = 1;
    private static final int DIRECTED_EDGE = 0;
    private static final int NONDIRECTED_EDGE = 2;
    private static final int PARTIALLY_ORIENTED_EDGE = 3;
    private static final int BIDIRECTED_EDGE = 4;

    //====================PRIVATE FIELDS=================================//

    /**
     * The type of node to be drawn next.
     */
    private int nodeType = LagWorkbench.MEASURED_NODE;

    /**
     * The type of edge to be drawn next.
     */
    private int edgeMode = LagWorkbench.DIRECTED_EDGE;

    //========================CONSTRUCTORS===============================//

    /**
     * Constructs a new workbench with an empty graph; useful if another graph will be set later.
     */
    public LagWorkbench() {
        this(new EdgeListGraph());
    }

    /**
     * Constructs a new workbench workbench for the given workbench model.
     */
    private LagWorkbench(Graph graph) {
        super(graph);
        setRightClickPopupAllowed(true);
    }

    //========================PUBLIC METHODS==============================//

    private static boolean containsName(List nodes, String name) {
        for (Node node : nodes) {
            if (name.equals(node.getName())) {
                return true;
            }
        }
        return false;
    }

    /**
     * The type of edge to be drawn next.
     *
     * @return the type of edge to be drawn.
     * @see #DIRECTED_EDGE
     * @see #NONDIRECTED_EDGE
     * @see #PARTIALLY_ORIENTED_EDGE
     * @see #BIDIRECTED_EDGE
     */
    public int getEdgeMode() {
        return this.edgeMode;
    }

    /**
     * Sets the edge mode to the given mode.
     *
     * @param edgeMode a int
     */
    public void setEdgeMode(int edgeMode) {
        switch (edgeMode) {
            case LagWorkbench.DIRECTED_EDGE:
                // Falls through!
            case LagWorkbench.NONDIRECTED_EDGE:
                // Falls through!
            case LagWorkbench.PARTIALLY_ORIENTED_EDGE:
                // Falls through!
            case LagWorkbench.BIDIRECTED_EDGE:
                this.edgeMode = edgeMode;
                break;
            default:
                throw new IllegalArgumentException();
        }
    }

    /**
     * Creates a new model node for the workbench.
     *
     * @return a {@link edu.cmu.tetrad.graph.Node} object
     */
    public Node getNewModelNode() {

        // select a name and create the model node
        String name;
        Node modelNode;

        switch (this.nodeType) {
            case LagWorkbench.MEASURED_NODE:
                name = nextVariableName("X");
                modelNode = new GraphNode(name);
                modelNode.setNodeType(NodeType.MEASURED);
                break;

            case LagWorkbench.LATENT_NODE:
                name = nextVariableName("L");
                modelNode = new GraphNode(name);
                modelNode.setNodeType(NodeType.LATENT);
                break;

            default:
                throw new IllegalStateException();
        }

        return modelNode;
    }

    /**
     * {@inheritDoc}
     * 

* Creates a new display node for the workbench based on the given model node. */ public DisplayNode getNewDisplayNode(Node modelNode) { DisplayNode displayNode; if (modelNode.getNodeType() == NodeType.MEASURED) { GraphNodeMeasured nodeMeasured = new GraphNodeMeasured(modelNode); nodeMeasured.setEditExitingMeasuredVarsAllowed(isEditExistingMeasuredVarsAllowed()); displayNode = nodeMeasured; } else if (modelNode.getNodeType() == NodeType.LATENT) { displayNode = new GraphNodeLatent(modelNode); } else if (modelNode.getNodeType() == NodeType.SELECTION) { displayNode = new GraphNodeSelection(modelNode); } else if (modelNode.getNodeType() == NodeType.ERROR) { displayNode = new GraphNodeError(modelNode); } else { throw new IllegalStateException(); } displayNode.addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { if ("resetGraph".equals(evt.getPropertyName())) { setGraph(getGraph()); } else if ("editingValueChanged".equals(evt.getPropertyName())) { firePropertyChange("modelChanged", null, null); } } }); return displayNode; } /** * {@inheritDoc} *

* Creates a new display edge for the workbench based on the given model edge. */ public IDisplayEdge getNewDisplayEdge(Edge modelEdge) { Node node1 = modelEdge.getNode1(); Node node2 = modelEdge.getNode2(); if (node1 == node2) { throw new IllegalArgumentException("Edges to self not supported."); } DisplayNode displayNodeA = (DisplayNode) getModelNodesToDisplay().get(node1); DisplayNode displayNodeB = (DisplayNode) getModelNodesToDisplay().get(node2); if ((displayNodeA == null) || (displayNodeB == null)) { return null; } return new DisplayEdge(modelEdge, displayNodeA, displayNodeB); } /** * {@inheritDoc} *

* Creates a new model edge for the workbench connecting the two given model nodes and using the edge type from * #getEdgeType(). */ public Edge getNewModelEdge(Node node1, Node node2) { switch (this.edgeMode) { case LagWorkbench.DIRECTED_EDGE: return Edges.directedEdge(node1, node2); case LagWorkbench.NONDIRECTED_EDGE: return Edges.nondirectedEdge(node1, node2); case LagWorkbench.PARTIALLY_ORIENTED_EDGE: return Edges.partiallyOrientedEdge(node1, node2); case LagWorkbench.BIDIRECTED_EDGE: return Edges.bidirectedEdge(node1, node2); default: throw new IllegalStateException(); } } /** * {@inheritDoc} *

* Gets a new "tracking edge"--that is, an edge which is anchored at one end to a node but tracks the mouse at the * other end. Used for drawing new edges. */ public IDisplayEdge getNewTrackingEdge(DisplayNode node, Point mouseLoc) { switch (this.edgeMode) { case LagWorkbench.DIRECTED_EDGE: return new DisplayEdge(node, mouseLoc, DisplayEdge.DIRECTED); case LagWorkbench.NONDIRECTED_EDGE: return new DisplayEdge(node, mouseLoc, DisplayEdge.NONDIRECTED); case LagWorkbench.PARTIALLY_ORIENTED_EDGE: return new DisplayEdge(node, mouseLoc, DisplayEdge.PARTIALLY_ORIENTED); case LagWorkbench.BIDIRECTED_EDGE: return new DisplayEdge(node, mouseLoc, DisplayEdge.BIDIRECTED); default: throw new IllegalStateException(); } } /** * Determines whether the next node to be constructed will be measured or latent. * * @return MEASURED_NODE or LATENT_NODE * @see #MEASURED_NODE * @see #LATENT_NODE */ public int getNodeMode() { return this.nodeType; } /** * Given base b (a String), returns the first node in the sequence "b1", "b2", "b3", etc., which is not already the * name of a node in the workbench. * * @param base the base string. * @return the first string in the sequence not already being used. */ private String nextVariableName(String base) { // Variable names should start with "1." int i = 0; loop: while (true) { String name = base + (++i); for (Node node1 : getGraph().getNodes()) { if (node1.getName().equals(name)) { continue loop; } } break; } return base + i; } /** * Sets the type of this node to the given type. * * @param nodeType a int */ public void setNodeType(int nodeType) { if (nodeType == LagWorkbench.MEASURED_NODE || nodeType == LagWorkbench.LATENT_NODE) { this.nodeType = nodeType; } else { throw new IllegalArgumentException("The type of the node must be " + "MEASURED_NODE or LATENT_NODE."); } } //===========================PRIVATE METHODS==========================// /** * Pastes a list of session elements (SessionNodeWrappers and SessionEdges) into the workbench. * * @param graphElements a {@link java.util.List} object * @param upperLeft a {@link java.awt.Point} object */ public void pasteSubgraph(List graphElements, Point upperLeft) { // Extract the SessionNodes from the SessionNodeWrappers // and pass the list of them to the Session. Choose a unique // name for each of the session wrappers. Point oldUpperLeft = EditorUtils.getTopLeftPoint(graphElements); int deltaX = upperLeft.x - oldUpperLeft.x; int deltaY = upperLeft.y - oldUpperLeft.y; for (Object graphElement : graphElements) { if (graphElement instanceof Node node) { adjustNameAndPosition(node, deltaX, deltaY); getWorkbench().getGraph().addNode(node); } else if (graphElement instanceof Edge) { getWorkbench().getGraph().addEdge((Edge) graphElement); } else { throw new IllegalArgumentException("The list of session " + "elements should contain only SessionNodeWrappers " + "and SessionEdges: " + graphElement); } } } /** * Adjusts the name to avoid name conflicts in the new session and, if the name is adjusted, adjusts the position so * the user can see the two nodes. * * @param node The node which is being adjusted * @param deltaX the shift in x * @param deltaY the shift in y. */ private void adjustNameAndPosition(Node node, int deltaX, int deltaY) { String originalName = node.getName(); //String base = extractBase(originalName); String uniqueName = nextUniqueName(originalName); if (!uniqueName.equals(originalName)) { node.setName(uniqueName); node.setCenterX(node.getCenterX() + deltaX); node.setCenterY(node.getCenterY() + deltaY); } } /** * @param base the string base of the name--for example, "Graph". * @return the next string in the sequence--for example, "Graph1". */ private String nextUniqueName(String base) { if (base == null) { throw new NullPointerException("Base name must be non-null."); } List currentNodes = this.getWorkbench().getGraph().getNodes(); if (!LagWorkbench.containsName(currentNodes, base)) { return base; } // otherwise fine new unique name. base += "_"; int i = 1; while (LagWorkbench.containsName(currentNodes, base + i)) { i++; } return base + i; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy