2 * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3 * Copyright (C) 2009 - DIGITEO - Antoine ELIAS
5 * This file must be used under the terms of the CeCILL.
6 * This source file is licensed as described in the file COPYING, which
7 * you should have received as part of this distribution. The terms
8 * are also available at
9 * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
13 package org.scilab.modules.xcos.utils;
15 import java.util.ArrayList;
16 import java.util.Collections;
17 import java.util.Comparator;
18 import java.util.List;
21 import org.scilab.modules.graph.utils.StyleMap;
22 import org.scilab.modules.xcos.block.BasicBlock;
23 import org.scilab.modules.xcos.io.scicos.BasicBlockInfo;
24 import org.scilab.modules.xcos.port.BasicPort;
25 import org.scilab.modules.xcos.port.Orientation;
27 import com.mxgraph.model.mxGeometry;
28 import com.mxgraph.model.mxIGraphModel;
29 import com.mxgraph.util.mxUtils;
32 * Helpers to place port on a block.
34 public final class BlockPositioning {
37 * The default grid size. This value is used when the grid size isn't
38 * accessible (on the palette).
40 public static final double DEFAULT_GRIDSIZE = Double.MIN_NORMAL;
41 /** The rotation step of the clockwise and anticlockwise rotation */
42 public static final int ROTATION_STEP = 90;
43 /** The max valid rotation value (always 360 degres) */
44 public static final int MAX_ROTATION = 360;
46 /** This class is a static singleton, thus it must not be instantiated */
47 private BlockPositioning() {
53 public static class PortComparator implements Comparator<BasicPort> {
55 public int compare(BasicPort arg0, BasicPort arg1) {
56 int order_0 = arg0.getOrdering();
57 int order_1 = arg1.getOrdering();
59 if (order_0 < order_1) {
61 } else if (order_0 > order_1) {
70 * Dispatch ports on Block's _WEST_ side.
73 * The block we have to work on.
75 * The ports we have to move on the side.
77 public static void updateWestPortsPosition(BasicBlock block, List <? extends BasicPort > ports) {
80 if (block.getParentDiagram() == null) {
81 gridSize = DEFAULT_GRIDSIZE;
83 gridSize = block.getParentDiagram().getGridSize();
86 // BasicBlock.sortsort(List<?> children) takes into account different
87 // parameters to order the ports list. We only need to order the ports
88 // given their ordering.
89 Collections.sort(ports, new PortComparator());
91 final mxGeometry blockGeom = block.getGeometry();
92 assert blockGeom != null;
93 final int portsSize = ports.size();
94 final double blockLength = blockGeom.getHeight();
95 final double segLength = blockLength / (portsSize + 1);
98 for (int i = 0; i < portsSize; ++i) {
99 final BasicPort port = (ports.get(i));
100 final mxGeometry portGeom = port.getGeometry();
102 double nonVariantPosition = -portGeom.getWidth();
104 double alignedPosition = calculateAlignedPosition(gridSize, segLength, order);
106 portGeom.setX(nonVariantPosition);
107 portGeom.setY(alignedPosition);
109 port.setLabelPosition(Orientation.WEST);
115 * Calculate an aligned port position.
122 * the current working index
123 * @return the aligned position on the grid.
125 private static double calculateAlignedPosition(final double gridSize, final double segLength, int i) {
127 * The base position is the origin of the port geometry. It is the
128 * upper-left corner position.
130 final double basePosition = (i + 1) * segLength;
133 * The aligned base position is the base position aligned on the grid.
135 final double alignedBasePosition = basePosition - Math.IEEEremainder(basePosition, gridSize);
138 * The aligned position is the base position translated from origin to
139 * the middle of the port.
141 final double alignedPosition = alignedBasePosition - (BasicPort.DEFAULT_PORTSIZE / 2.0);
143 return alignedPosition;
147 * Dispatch ports on Block's _NORTH_ side.
150 * The block we have to work on.
152 * The ports we have to move on the side.
154 public static void updateNorthPortsPosition(BasicBlock block, List <? extends BasicPort > ports) {
156 if (block.getParentDiagram() == null) {
157 gridSize = DEFAULT_GRIDSIZE;
159 gridSize = block.getParentDiagram().getGridSize();
162 // BasicBlock.sortsort(List<?> children) takes into account different
163 // parameters to order the ports list. We only need to order the ports
164 // given their ordering.
165 Collections.sort(ports, new PortComparator());
167 final mxGeometry blockGeom = block.getGeometry();
168 assert blockGeom != null;
169 final int portsSize = ports.size();
170 final double blockLength = blockGeom.getWidth();
171 final double segLength = blockLength / (portsSize + 1);
174 for (int i = 0; i < portsSize; ++i) {
175 final BasicPort port = (ports.get(i));
176 final mxGeometry portGeom = port.getGeometry();
178 double nonVariantPosition = -portGeom.getHeight();
180 double alignedPosition = calculateAlignedPosition(gridSize, segLength, order);
182 portGeom.setX(alignedPosition);
183 portGeom.setY(nonVariantPosition);
185 port.setLabelPosition(Orientation.NORTH);
191 * Dispatch ports on Block's _EAST_ side.
194 * The block we have to work on.
196 * The ports we have to move on the side.
198 public static void updateEastPortsPosition(BasicBlock block, List <? extends BasicPort > ports) {
200 if (block.getParentDiagram() == null) {
201 gridSize = DEFAULT_GRIDSIZE;
203 gridSize = block.getParentDiagram().getGridSize();
206 // BasicBlock.sortsort(List<?> children) takes into account different
207 // parameters to order the ports list. We only need to order the ports
208 // given their ordering.
209 Collections.sort(ports, new PortComparator());
211 final mxGeometry blockGeom = block.getGeometry();
212 assert blockGeom != null;
213 final int portsSize = ports.size();
214 final double blockLength = blockGeom.getHeight();
215 final double segLength = blockLength / (portsSize + 1);
218 for (int i = 0; i < portsSize; ++i) {
219 final BasicPort port = (ports.get(i));
220 final mxGeometry portGeom = port.getGeometry();
222 double nonVariantPosition = blockGeom.getWidth();
224 double alignedPosition = calculateAlignedPosition(gridSize, segLength, order);
226 portGeom.setX(nonVariantPosition);
227 portGeom.setY(alignedPosition);
229 port.setLabelPosition(Orientation.EAST);
235 * Dispatch ports on Block's _SOUTH_ side.
238 * The block we have to work on.
240 * The ports we have to move on the side.
242 public static void updateSouthPortsPosition(BasicBlock block, List <? extends BasicPort > ports) {
244 if (block.getParentDiagram() == null) {
245 gridSize = DEFAULT_GRIDSIZE;
247 gridSize = block.getParentDiagram().getGridSize();
250 // BasicBlock.sortsort(List<?> children) takes into account different
251 // parameters to order the ports list. We only need to order the ports
252 // given their ordering.
253 Collections.sort(ports, new PortComparator());
255 final mxGeometry blockGeom = block.getGeometry();
256 assert blockGeom != null;
257 final int portsSize = ports.size();
258 final double blockLength = blockGeom.getWidth();
259 final double segLength = blockLength / (portsSize + 1);
262 for (int i = 0; i < portsSize; ++i) {
263 final BasicPort port = (ports.get(i));
264 final mxGeometry portGeom = port.getGeometry();
266 double nonVariantPosition = blockGeom.getHeight();
268 double alignedPosition = calculateAlignedPosition(gridSize, segLength, order);
270 portGeom.setX(alignedPosition);
271 portGeom.setY(nonVariantPosition);
273 port.setLabelPosition(Orientation.SOUTH);
279 * Update all the port position of the block.
282 * The block we have to work on.
284 public static void updatePortsPosition(BasicBlock block) {
285 final Map<Orientation, List<BasicPort>> ports = BasicBlockInfo.getAllOrientedPorts(block);
288 for (Orientation iter : Orientation.values()) {
289 List<BasicPort> orientedPorts = ports.get(iter);
290 if (orientedPorts != null && !orientedPorts.isEmpty()) {
291 updatePortsPositions(block, orientedPorts, iter);
298 * Update the port position for the specified orientation. This function
299 * manage the flip and mirror properties.
302 * The block we are working on
304 * The ports at with the specified orientation
308 private static void updatePortsPositions(BasicBlock block, List<BasicPort> ports, Orientation iter) {
309 @SuppressWarnings("serial")
310 final List<BasicPort> invertedPorts = new ArrayList<BasicPort>(ports) {
312 Collections.reverse(this);
315 final boolean mirrored = block.getMirror();
316 final boolean flipped = block.getFlip();
317 final int angle = block.getAngle();
318 List<BasicPort> working = ports;
320 /* List order modification with the flip flag */
322 if (iter == Orientation.NORTH || iter == Orientation.SOUTH) {
323 working = invertedPorts;
327 /* List order modification with the mirror flag */
329 if (iter == Orientation.EAST || iter == Orientation.WEST) {
330 working = invertedPorts;
335 * Ugly modification of the iter to update at the right position Works
336 * only for 0 - 90 - 180 - 270 angles.
338 Orientation rotated = rotateOrientation(iter, mirrored, flipped);
340 updatePortsPosition(block, rotated, angle, working);
344 * Ugly modification of the iter to update at the right position. Works only
345 * for 0 - 90 - 180 - 270 angles.
348 * the real orientation
350 * is the block mirrored
352 * is the block flipped
353 * @return The modified orientation
355 private static Orientation rotateOrientation(final Orientation iter, final boolean mirrored, final boolean flipped) {
356 final int nbOfOrientations = Orientation.values().length; // 4
357 Orientation rotated = iter;
359 /* Flip & Mirror management */
361 if (rotated == Orientation.EAST || rotated == Orientation.WEST) {
362 rotated = Orientation.values()[(rotated.ordinal() + 2) % nbOfOrientations];
366 if (rotated == Orientation.NORTH || rotated == Orientation.SOUTH) {
367 rotated = Orientation.values()[(rotated.ordinal() + 2) % nbOfOrientations];
374 * Update the ports positions according to the angle. This function doesn't
375 * handle order inversion.
378 * The block we are working on
380 * The current port orientation
382 * The angle we have to rotate
384 * The ordered ports we are working on.
386 private static void updatePortsPosition(BasicBlock block, Orientation iter, final int angle, List<BasicPort> working) {
388 * Ugly modification of the iter to update at the right position Works
389 * only for 0 - 90 - 180 - 270 angles.
391 final int nbOfOrientations = Orientation.values().length; // 4
392 Orientation rotated = iter;
394 /* Angle management */
395 int rotationIndex = angle / ROTATION_STEP;
396 rotated = Orientation.values()[(rotated.ordinal() + rotationIndex) % nbOfOrientations];
398 /* Call the associated function */
401 updateNorthPortsPosition(block, working);
404 updateSouthPortsPosition(block, working);
407 updateEastPortsPosition(block, working);
410 updateWestPortsPosition(block, working);
419 * Rotate all the port of the block.
422 * The block to work on.
424 public static void rotateAllPorts(BasicBlock block) {
425 final int angle = block.getAngle();
426 final boolean flipped = block.getFlip();
427 final boolean mirrored = block.getMirror();
429 final int childrenCount = block.getChildCount();
430 for (int i = 0; i < childrenCount; ++i) {
431 if (block.getChildAt(i) instanceof BasicPort) {
432 final BasicPort port = (BasicPort) block.getChildAt(i);
433 final Orientation orientation = port.getOrientation();
438 if (block.getParentDiagram() != null) {
439 final mxIGraphModel model = block.getParentDiagram().getModel();
440 final String rot = Integer.toString(orientation.getRelativeAngle(angle, port.getClass(), flipped, mirrored));
441 mxUtils.setCellStyles(model, new Object[] { port }, XcosConstants.STYLE_ROTATION, rot);
443 final StyleMap m = new StyleMap(port.getStyle());
444 final int rot = orientation.getRelativeAngle(angle, port.getClass(), flipped, mirrored);
445 m.put(XcosConstants.STYLE_ROTATION, Integer.toString(rot));
446 port.setStyle(m.toString());
455 * Update the geometry of the block's ports.
458 * The block to work on
460 public static void updateBlockView(BasicBlock block) {
462 updatePortsPosition(block);
463 rotateAllPorts(block);
467 * FIXME: #6705; This placement trick doesn't work on the first block
468 * Dnd as the view is not revalidated.
470 * On block loading, parentDiagram is null thus placement is not
476 * Flip a block (horizontal inversion).
479 * The block to work on
481 public static void toggleFlip(BasicBlock block) {
483 block.setFlip(!block.getFlip());
484 updateBlockView(block);
488 * Mirror a block (vertical inversion).
491 * The block to work on
493 public static void toggleMirror(BasicBlock block) {
495 block.setMirror(!block.getMirror());
496 updateBlockView(block);
500 * Rotate a block with an anti-clockwise next value
503 * The block to work on
505 public static void toggleAntiClockwiseRotation(BasicBlock block) {
506 block.setAngle(getNextAntiClockwiseAngle(block));
507 updateBlockView(block);
511 * Get the next anti-clockwise rotation value
514 * The block to work on
515 * @return The angle value
517 public static int getNextAntiClockwiseAngle(BasicBlock block) {
518 int angle = (block.getAngle() - ROTATION_STEP + MAX_ROTATION) % MAX_ROTATION;
523 * Get the next clockwise rotation value
526 * The block to work on
527 * @return The angle value
529 public static int getNextClockwiseAngle(BasicBlock block) {
530 int angle = (block.getAngle() + ROTATION_STEP) % MAX_ROTATION;
535 * Convert any angle value to a valid block value
538 * the non valid value
539 * @return the nearest graph valid value
541 public static int roundAngle(int angle) {
543 if (angle < 0 || angle > MAX_ROTATION) {
544 ret = (angle + MAX_ROTATION) % MAX_ROTATION;
547 for (int i = 0; i < (MAX_ROTATION / ROTATION_STEP); i++) {
548 int min = i * ROTATION_STEP;
549 int max = (i + 1) * ROTATION_STEP;
551 if (ret < (min + max) / 2) {
560 * Helper function that protect the block model.
563 * The block to protect
565 private static void beginUpdate(BasicBlock block) {
566 if (block != null && block.getParentDiagram() != null) {
567 block.getParentDiagram().getModel().beginUpdate();
572 * Helper function that end the protection of the block model.
575 * The block previously protected
577 private static void endUpdate(BasicBlock block) {
578 if (block != null && block.getParentDiagram() != null) {
579 block.getParentDiagram().getModel().endUpdate();