1 /* 2 * Copyright (c) 2012, 2014, Oracle and/or its affiliates. 3 * All rights reserved. Use is subject to license terms. 4 * 5 * This file is available and licensed under the following license: 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * - Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * - Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the distribution. 16 * - Neither the name of Oracle Corporation nor the names of its 17 * contributors may be used to endorse or promote products derived 18 * from this software without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 package com.oracle.javafx.scenebuilder.kit.editor.panel.content.guides; 34 35 import com.oracle.javafx.scenebuilder.kit.util.MathUtils; 36 import java.util.Collections; 37 import java.util.List; 38 import javafx.geometry.Bounds; 39 import javafx.scene.Group; 40 import javafx.scene.Node; 41 import javafx.scene.paint.Paint; 42 43 /** 44 * 45 */ 46 public class MovingGuideController { 47 48 private final double MATCH_DISTANCE = 6.0; 49 50 private final HorizontalLineIndex horizontalLineIndex = new HorizontalLineIndex(); 51 private final VerticalLineIndex verticalLineIndex = new VerticalLineIndex(); 52 private final MovingGuideRenderer renderer; 53 private double suggestedDX; 54 private double suggestedDY; 55 56 public MovingGuideController(Paint chromeColor, Bounds scopeInScene) { 57 this.renderer = new MovingGuideRenderer(chromeColor, scopeInScene); 58 } 59 60 public void addSampleBounds(Node node) { 61 assert node != null; 62 assert node.getScene() != null; 63 64 final Bounds layoutBounds = node.getLayoutBounds(); 65 final Bounds boundsInScene = node.localToScene(layoutBounds, true /* rootScene */); 66 addSampleBounds(boundsInScene, true /* addMiddle */); 67 } 68 69 public void addSampleBounds(Bounds boundsInScene, boolean addMiddle) { 70 final double minX = boundsInScene.getMinX(); 71 final double minY = boundsInScene.getMinY(); 72 final double maxX = boundsInScene.getMaxX(); 73 final double maxY = boundsInScene.getMaxY(); 74 final double midX = (minX + maxX) / 2.0; 75 final double midY = (minY + maxY) / 2.0; 76 77 horizontalLineIndex.addLine(new HorizontalSegment(minX, maxX, minY)); 78 horizontalLineIndex.addLine(new HorizontalSegment(minX, maxX, maxY)); 79 verticalLineIndex.addLine(new VerticalSegment(minX, minY, maxY)); 80 verticalLineIndex.addLine(new VerticalSegment(maxX, minY, maxY)); 81 82 if (addMiddle) { 83 horizontalLineIndex.addLine(new HorizontalSegment(minX, maxX, midY)); 84 verticalLineIndex.addLine(new VerticalSegment(midX, minY, maxY)); 85 } 86 } 87 88 public void clearSampleBounds() { 89 horizontalLineIndex.clear(); 90 verticalLineIndex.clear(); 91 clear(); 92 } 93 94 public boolean hasSampleBounds() { 95 return (horizontalLineIndex.isEmpty() == false) || (verticalLineIndex.isEmpty() == false); 96 } 97 98 public void clear() { 99 renderer.setLines(Collections.emptyList(), Collections.emptyList()); 100 } 101 102 public void match(Bounds targetBounds) { 103 List<HorizontalSegment> horizontalMatchingLines; 104 List<VerticalSegment> verticalMatchingLines; 105 boolean matchedHorizontally = false; 106 boolean matchedVertically = false; 107 108 // Match horizontal center line of targetBounds 109 horizontalMatchingLines 110 = horizontalLineIndex.matchCenter(targetBounds, MATCH_DISTANCE); 111 if (horizontalMatchingLines.isEmpty() == false) { 112 matchedHorizontally = true; 113 final HorizontalSegment line = horizontalMatchingLines.get(0); 114 assert MathUtils.equals(line.getY1(), line.getY2()); 115 final double targetMinY = targetBounds.getMinY(); 116 final double targetMaxY = targetBounds.getMaxY(); 117 final double targetMidY = (targetMinY + targetMaxY) / 2.0; 118 suggestedDY = line.getY1() - targetMidY; 119 } 120 121 // Match north boundary of targetBounds 122 if (matchedHorizontally == false) { 123 horizontalMatchingLines 124 = horizontalLineIndex.matchNorth(targetBounds, MATCH_DISTANCE); 125 if (horizontalMatchingLines.isEmpty() == false) { 126 matchedHorizontally = true; 127 final HorizontalSegment line = horizontalMatchingLines.get(0); 128 assert MathUtils.equals(line.getY1(), line.getY2()); 129 suggestedDY = line.getY1() - targetBounds.getMinY(); 130 } 131 } 132 133 // Match south boundary of targetBounds 134 if (matchedHorizontally == false) { 135 horizontalMatchingLines 136 = horizontalLineIndex.matchSouth(targetBounds, MATCH_DISTANCE); 137 if (horizontalMatchingLines.isEmpty() == false) { 138 matchedHorizontally = true; 139 final HorizontalSegment line = horizontalMatchingLines.get(0); 140 assert MathUtils.equals(line.getY1(), line.getY2()); 141 suggestedDY = line.getY1() - targetBounds.getMaxY(); 142 } 143 } 144 145 if (matchedHorizontally == false) { 146 suggestedDY = 0.0; 147 } 148 149 // Match vertical center line of targetBounds 150 verticalMatchingLines 151 = verticalLineIndex.matchCenter(targetBounds, MATCH_DISTANCE); 152 if (verticalMatchingLines.isEmpty() == false) { 153 matchedVertically = true; 154 final VerticalSegment line = verticalMatchingLines.get(0); 155 assert MathUtils.equals(line.getX1(), line.getX2()); 156 final double targetMinX = targetBounds.getMinX(); 157 final double targetMaxX = targetBounds.getMaxX(); 158 final double targetMidX = (targetMinX + targetMaxX) / 2.0; 159 suggestedDX = line.getX1() - targetMidX; 160 } 161 162 // Match west boundary of targetBounds 163 if (matchedVertically == false) { 164 verticalMatchingLines 165 = verticalLineIndex.matchWest(targetBounds, MATCH_DISTANCE); 166 if (verticalMatchingLines.isEmpty() == false) { 167 matchedVertically = true; 168 final VerticalSegment line = verticalMatchingLines.get(0); 169 assert MathUtils.equals(line.getX1(), line.getX2()); 170 suggestedDX = line.getX1() - targetBounds.getMinX(); 171 } 172 } 173 174 // Match east boundary of targetBounds 175 if (matchedVertically == false) { 176 verticalMatchingLines 177 = verticalLineIndex.matchEast(targetBounds, MATCH_DISTANCE); 178 if (verticalMatchingLines.isEmpty() == false) { 179 matchedVertically = true; 180 final VerticalSegment line = verticalMatchingLines.get(0); 181 assert MathUtils.equals(line.getX1(), line.getX2()); 182 suggestedDX = line.getX1() - targetBounds.getMaxX(); 183 } 184 } 185 186 if (matchedVertically == false) { 187 suggestedDX = 0.0; 188 } 189 190 renderer.setLines(horizontalMatchingLines, verticalMatchingLines); 191 } 192 193 194 public double getSuggestedDX() { 195 return suggestedDX; 196 } 197 198 199 public double getSuggestedDY() { 200 return suggestedDY; 201 } 202 203 public Group getGuideGroup() { 204 return renderer.getGuideGroup(); 205 } 206 }