001/* 002 003Copyright (c) 2011-2016 The Regents of the University of California. 004All rights reserved. 005 006Permission is hereby granted, without written agreement and without 007license or royalty fees, to use, copy, modify, and distribute this 008software and its documentation for any purpose, provided that the above 009copyright notice and the following two paragraphs appear in all copies 010of this software. 011 012IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA LIABLE TO ANY PARTY 013FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 014ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF 015THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF 016SUCH DAMAGE. 017 018THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, 019INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 020MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE 021PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF 022CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, 023ENHANCEMENTS, OR MODIFICATIONS. 024 025PT_COPYRIGHT_VERSION_2 026COPYRIGHTENDKEY 027 */ 028package ptolemy.vergil.basic.layout.kieler; 029 030import java.util.List; 031 032import de.cau.cs.kieler.core.properties.IProperty; 033import de.cau.cs.kieler.core.properties.Property; 034import de.cau.cs.kieler.kiml.klayoutdata.KLayoutData; 035import de.cau.cs.kieler.kiml.klayoutdata.KShapeLayout; 036import de.cau.cs.kieler.kiml.options.Direction; 037import de.cau.cs.kieler.kiml.options.EdgeRouting; 038import de.cau.cs.kieler.kiml.options.LayoutOptions; 039import de.cau.cs.kieler.klay.layered.p1cycles.CycleBreakingStrategy; 040import de.cau.cs.kieler.klay.layered.p2layers.LayeringStrategy; 041import de.cau.cs.kieler.klay.layered.p3order.CrossingMinimizationStrategy; 042import de.cau.cs.kieler.klay.layered.p4nodes.NodePlacementStrategy; 043import de.cau.cs.kieler.klay.layered.properties.FixedAlignment; 044import de.cau.cs.kieler.klay.layered.properties.Properties; 045import diva.graph.GraphModel; 046import ptolemy.data.BooleanToken; 047import ptolemy.data.DoubleToken; 048import ptolemy.kernel.CompositeEntity; 049import ptolemy.kernel.util.IllegalActionException; 050import ptolemy.vergil.actor.ActorGraphModel; 051import ptolemy.vergil.basic.layout.AbstractLayoutConfiguration; 052import ptolemy.vergil.basic.layout.AbstractLayoutConfiguration.InteractionMode; 053import ptolemy.vergil.basic.layout.ActorLayoutConfiguration; 054import ptolemy.vergil.basic.layout.ModalLayoutConfiguration; 055import ptolemy.vergil.modal.FSMGraphModel; 056 057/** 058 * Responsible for translating layout configuration parameters into the KIELER format. 059 * Parameters are read from an instance of the {@link AbstractLayoutConfiguration} attribute, 060 * which is attached to composite entities when the configuration dialog is opened. 061 * 062 * @see AbstractLayoutConfiguration 063 * @author Miro Spoenemann, Christoph Daniel Schulze, Ulf Rueegg 064 * @version $Id$ 065 * @since Ptolemy II 10.0 066 * @Pt.ProposedRating Red (msp) 067 * @Pt.AcceptedRating Red (msp) 068 */ 069public class Parameters { 070 071 /** 072 * Create a parameters instance. 073 * 074 * @param compositeEntity the composite entity for which parameters are retrieved. 075 */ 076 public Parameters(CompositeEntity compositeEntity) { 077 _compositeEntity = compositeEntity; 078 } 079 080 /** 081 * Configure the KIELER layout using a property holder. 082 * 083 * @param parentLayout 084 * the layout of the parent node 085 * @param graphModel 086 * the graph model of the current diagram 087 * @exception IllegalActionException 088 * if one of the parameters has the wrong type 089 */ 090 public void configureLayout(KShapeLayout parentLayout, 091 GraphModel graphModel) throws IllegalActionException { 092 093 // Configuration values specified by user. 094 List<AbstractLayoutConfiguration> configAttributes = _compositeEntity 095 .attributeList(AbstractLayoutConfiguration.class); 096 097 AbstractLayoutConfiguration layoutConfiguration = null; 098 if (!configAttributes.isEmpty()) { 099 layoutConfiguration = configAttributes.get(0); 100 } 101 102 // Note that when the layout configuration dialog of a model has never been opened, 103 // there is no configuration element 104 // Otherwise subsequently applied layout options override each other 105 // in the following calls, with the user-specified, diagram-specific 106 // options being the strongest 107 108 // 1. default values 109 _applyDefault(parentLayout); 110 111 // 2. common configuration values 112 _applyAbstractConfiguration(parentLayout, layoutConfiguration); 113 114 // 3. model specific configuration values 115 if (graphModel instanceof ActorGraphModel) { 116 _applyActorConfiguration(parentLayout, 117 (ActorLayoutConfiguration) layoutConfiguration); 118 } else if (graphModel instanceof FSMGraphModel) { 119 _applyModalConfiguration(parentLayout, 120 (ModalLayoutConfiguration) layoutConfiguration); 121 } 122 123 } 124 125 /////////////////////////////////////////////////////////////////// 126 //// private methods //// 127 128 /** 129 * An initial, default layout configuration applied to every graph. 130 * 131 * @param parentLayout 132 * the layout of the parent node 133 */ 134 private void _applyDefault(KLayoutData parentLayout) 135 throws IllegalActionException { 136 137 // Set general default values. 138 parentLayout.setProperty(LayoutOptions.DIRECTION, Direction.RIGHT); 139 parentLayout.setProperty(LayoutOptions.BORDER_SPACING, 5.0f); 140 parentLayout.setProperty(Properties.EDGE_SPACING_FACTOR, 1.5f); 141 142 parentLayout.setProperty(LayoutOptions.SPACING, SPACING.getDefault()); 143 parentLayout.setProperty(LayoutOptions.ASPECT_RATIO, 144 ASPECT_RATIO.getDefault()); 145 146 } 147 148 /** 149 * This applies user-specified layout options the graph, 150 * if they exist. 151 * 152 * @param parentLayout 153 * the layout of the parent node 154 * @param abstractConfiguration 155 * the container with user-specified options, may be null 156 * @exception IllegalActionException 157 * thrown if one of the parameters has the wrong type 158 */ 159 private void _applyAbstractConfiguration(KLayoutData parentLayout, 160 AbstractLayoutConfiguration abstractConfiguration) 161 throws IllegalActionException { 162 163 if (abstractConfiguration != null) { 164 // Whether decorations are to be laid out or left as they are 165 BooleanToken decorationsToken = BooleanToken.convert( 166 abstractConfiguration.includeDecorations.getToken()); 167 parentLayout.setProperty(DECORATIONS, 168 decorationsToken.booleanValue()); 169 170 // Spacing between diagram elements 171 DoubleToken spacingToken = DoubleToken 172 .convert(abstractConfiguration.spacing.getToken()); 173 parentLayout.setProperty(SPACING, 174 (float) spacingToken.doubleValue()); 175 176 // Target aspect ratio for the diagram 177 DoubleToken logAspectToken = DoubleToken 178 .convert(abstractConfiguration.logAspectRatio.getToken()); 179 parentLayout.setProperty(ASPECT_RATIO, 180 (float) Math.pow(10, logAspectToken.doubleValue())); 181 182 // The interaction mode (constraints the layout according to what the 183 // diagram currently looks like) 184 InteractionMode interactionMode = (InteractionMode) abstractConfiguration.interactionMode 185 .getChosenValue(); 186 if (interactionMode != null) { 187 // The switch cases fall through on purpose! 188 switch (interactionMode) { 189 case Full: 190 parentLayout.setProperty(Properties.CROSS_MIN, 191 CrossingMinimizationStrategy.INTERACTIVE); 192 case Columns: 193 parentLayout.setProperty(Properties.NODE_LAYERING, 194 LayeringStrategy.INTERACTIVE); 195 case Cycles: 196 parentLayout.setProperty(Properties.CYCLE_BREAKING, 197 CycleBreakingStrategy.INTERACTIVE); 198 default: 199 // Don't change the configuration in all other cases 200 } 201 } 202 } 203 204 } 205 206 /** 207 * This applies default layout options for actor models 208 * as well as user-specified layout options specific to actor models, 209 * if they exist. 210 * 211 * @param parentLayout 212 * the layout of the parent node 213 * @param actorConfiguration 214 * the container with user-specified options, may be null 215 * @exception IllegalActionException 216 * thrown if one of the parameters has the wrong type 217 */ 218 private void _applyActorConfiguration(KLayoutData parentLayout, 219 ActorLayoutConfiguration actorConfiguration) 220 throws IllegalActionException { 221 222 // Set default values for actor models. 223 parentLayout.setProperty(LayoutOptions.EDGE_ROUTING, 224 EdgeRouting.ORTHOGONAL); 225 226 // User-specified properties 227 if (actorConfiguration != null) { 228 // The node placement algorithm to use 229 BooleanToken minimizeBendsToken = BooleanToken 230 .convert(actorConfiguration.minimizeBends.getToken()); 231 if (minimizeBendsToken.booleanValue()) { 232 parentLayout.setProperty(Properties.NODE_PLACER, 233 NodePlacementStrategy.BRANDES_KOEPF); 234 } else { 235 parentLayout.setProperty(Properties.NODE_PLACER, 236 NodePlacementStrategy.LINEAR_SEGMENTS); 237 } 238 } 239 } 240 241 /** 242 * This applies default layout options for FSM models 243 * as well as user-specified layout options specific to FSM models, 244 * if they exist. 245 * 246 * @param parentLayout 247 * the layout of the parent node 248 * @param modalConfiguration 249 * the container with user-specified options, may be null 250 * @exception IllegalActionException 251 * thrown if one of the parameters has the wrong type 252 */ 253 private void _applyModalConfiguration(KLayoutData parentLayout, 254 ModalLayoutConfiguration modalConfiguration) 255 throws IllegalActionException { 256 257 // Set default values for modal models. 258 parentLayout.setProperty(LayoutOptions.EDGE_ROUTING, 259 EdgeRouting.SPLINES); 260 parentLayout.setProperty(LayoutOptions.DIRECTION, 261 ModalLayoutConfiguration.DEF_DIRECTION); 262 263 float spacing = parentLayout.getProperty(SPACING); 264 parentLayout.setProperty(SPACING, spacing / 2f); 265 parentLayout.setProperty(Properties.OBJ_SPACING_IN_LAYER_FACTOR, 8f); 266 267 // The node placement algorithm to use 268 parentLayout.setProperty(Properties.NODE_PLACER, 269 NodePlacementStrategy.BRANDES_KOEPF); 270 parentLayout.setProperty(Properties.FIXED_ALIGNMENT, 271 FixedAlignment.BALANCED); 272 273 // User-specified 274 if (modalConfiguration != null) { 275 276 // For FSMs the user can choose whether he wants to use splines 277 // or not. Depending on the choice we have to adapt the layout options 278 BooleanToken useSplines = BooleanToken 279 .convert(modalConfiguration.drawSplines.getToken()); 280 parentLayout.setProperty(SPLINES, useSplines.booleanValue()); 281 282 if (useSplines.booleanValue()) { 283 // spline routing 284 parentLayout.setProperty(LayoutOptions.EDGE_ROUTING, 285 EdgeRouting.SPLINES); 286 } else { 287 // arc-style routing 288 parentLayout.setProperty(SPACING, spacing / 4f); 289 parentLayout.setProperty(Properties.OBJ_SPACING_IN_LAYER_FACTOR, 290 20f); 291 } 292 293 // direction 294 Direction dir = (Direction) modalConfiguration.direction 295 .getChosenValue(); 296 parentLayout.setProperty(LayoutOptions.DIRECTION, dir); 297 } 298 } 299 300 /** Layout option that determines whether decoration nodes are included in layout. */ 301 public static final IProperty<Boolean> DECORATIONS = new Property<Boolean>( 302 "ptolemy.vergil.basic.layout.decorations", 303 AbstractLayoutConfiguration.DEF_DECORATIONS); 304 305 /** Layout option for the overall spacing between elements. */ 306 public static final IProperty<Float> SPACING = new Property<Float>( 307 LayoutOptions.SPACING, 308 (float) AbstractLayoutConfiguration.DEF_SPACING); 309 310 /** Layout option for the aspect ratio of connected components. */ 311 public static final IProperty<Float> ASPECT_RATIO = new Property<Float>( 312 LayoutOptions.ASPECT_RATIO, 313 (float) AbstractLayoutConfiguration.DEF_ASPECT_RATIO); 314 315 /** Layout option that determines whether splines should be used for FSMs. */ 316 public static final IProperty<Boolean> SPLINES = new Property<Boolean>( 317 "ptolemy.vergil.basic.layout.splines", 318 ModalLayoutConfiguration.DEF_USE_SPLINES); 319 320 /////////////////////////////////////////////////////////////////// 321 //// private variables //// 322 323 /** the parent entity from which the layout configuration is read. */ 324 private CompositeEntity _compositeEntity; 325 326}