| /****************************************************************************** |
| * Copyright (c) 2009, 2010 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| ****************************************************************************/ |
| package org.eclipse.gmf.runtime.diagram.ui.render.util; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.draw2d.IFigure; |
| import org.eclipse.draw2d.PolylineConnection; |
| import org.eclipse.draw2d.geometry.Point; |
| import org.eclipse.draw2d.geometry.PointList; |
| import org.eclipse.draw2d.geometry.PrecisionPoint; |
| import org.eclipse.draw2d.geometry.PrecisionRectangle; |
| import org.eclipse.gef.ConnectionEditPart; |
| import org.eclipse.gef.LayerConstants; |
| import org.eclipse.gef.editparts.LayerManager; |
| import org.eclipse.gmf.runtime.diagram.core.util.ViewUtil; |
| import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart; |
| import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; |
| import org.eclipse.gmf.runtime.diagram.ui.editparts.LabelEditPart; |
| import org.eclipse.gmf.runtime.diagram.ui.editparts.ShapeCompartmentEditPart; |
| import org.eclipse.gmf.runtime.diagram.ui.editparts.ShapeEditPart; |
| import org.eclipse.gmf.runtime.diagram.ui.image.PartPositionInfo; |
| import org.eclipse.gmf.runtime.draw2d.ui.geometry.LineSeg; |
| import org.eclipse.gmf.runtime.draw2d.ui.geometry.PointListUtilities; |
| import org.eclipse.gmf.runtime.draw2d.ui.geometry.PrecisionPointList; |
| import org.eclipse.gmf.runtime.draw2d.ui.geometry.LineSeg.Sign; |
| import org.eclipse.gmf.runtime.draw2d.ui.mapmode.IMapMode; |
| import org.eclipse.gmf.runtime.draw2d.ui.mapmode.MapModeUtil; |
| import org.eclipse.gmf.runtime.notation.View; |
| |
| /** |
| * A Utility class to generate the info for images of diagrams |
| * |
| * @author aboyko |
| * @since 1.3 |
| * |
| */ |
| public final class PartPositionInfoGenerator { |
| |
| /** |
| * Margin around the connection. <code>Double</code> value is expected. The |
| * generator picks the maximum between this value and the line width of the |
| * connection. The value must be in logical units. |
| * <p>Default value of 0 is taken if options is not provided</p> |
| */ |
| public static final String CONNECTION_MARGIN = "connectionMargin"; //$NON-NLS-1$ |
| /** |
| * Point of the origin of the diagram, this is expected to be a |
| * {@link org.eclipse.draw2d.geometry.Point} in logical units, relative to |
| * printable layer. |
| * <p>Default value of (0,0) is taken if this option is not provided</p> |
| */ |
| public static final String DIAGRAM_ORIGIN = "diagramOrigin"; //$NON-NLS-1$ |
| /** |
| * Scaling factor for generating parts info for scaled down or up diagram. |
| * Double is expected. |
| * <p>Default value of 1.0 will be taken if this option is |
| * not provided</p> |
| */ |
| public static final String SCALE_FACTOR = "scaleFactor"; //$NON-NLS-1$ |
| |
| /** |
| * Generates the info for a diagram |
| * |
| * @param diagramEditPart the diagram |
| * @param options options affecting positional info |
| * @return a list of <code>PartPositionInfo</code> |
| */ |
| public static final List<PartPositionInfo> getDiagramPartInfo( |
| DiagramEditPart diagramEditPart, Map<String, Object> options) { |
| List<PartPositionInfo> result = new ArrayList<PartPositionInfo>(); |
| List<IGraphicalEditPart> editParts = new ArrayList<IGraphicalEditPart>(); |
| |
| List<IGraphicalEditPart> children = (List<IGraphicalEditPart>) diagramEditPart.getPrimaryEditParts(); |
| IMapMode mm = MapModeUtil.getMapMode(diagramEditPart.getFigure()); |
| |
| Object optionConnectionMargin = options.get(PartPositionInfoGenerator.CONNECTION_MARGIN); |
| double connectionMargin = optionConnectionMargin != null ? ((Double)optionConnectionMargin).doubleValue() : 0; |
| Object optionDiagramOrigin = options.get(PartPositionInfoGenerator.DIAGRAM_ORIGIN); |
| Point origin = optionDiagramOrigin != null ? (Point)optionDiagramOrigin : new Point(); |
| Object optionScaleFactor = options.get(PartPositionInfoGenerator.SCALE_FACTOR); |
| double scale = optionScaleFactor != null ? ((Double)optionScaleFactor).doubleValue() : 1.0; |
| if (scale <= 0) { |
| throw new IllegalArgumentException(); |
| } |
| |
| for (IGraphicalEditPart part : children) { |
| editParts.add(part); |
| getNestedEditParts(part, editParts); |
| } |
| |
| IFigure printableLayer = LayerManager.Helper.find(diagramEditPart) |
| .getLayer(LayerConstants.PRINTABLE_LAYERS); |
| |
| for (IGraphicalEditPart part : editParts) { |
| IFigure figure = part.getFigure(); |
| |
| // Need to support any kind of shape edit part |
| // and shape compartments, too, because these sometimes |
| // correspond to distinct semantic elements |
| View view = part.getNotationView(); |
| if (part instanceof ConnectionEditPart |
| && figure instanceof PolylineConnection) { |
| // find a way to get (P1, P2, ... PN) for connection edit part |
| // add MARGIN and calculate "stripe" for the polyline instead of |
| // bounding box. |
| PartPositionInfo position = new PartPositionInfo(); |
| |
| position.setView(view); |
| position.setSemanticElement(ViewUtil |
| .resolveSemanticElement(view)); |
| |
| PolylineConnection mainPoly = (PolylineConnection) figure; |
| if (mainPoly.isVisible() && !mainPoly.getBounds().isEmpty()) { |
| PointList mainPts = mainPoly.getPoints().getCopy(); |
| |
| DiagramImageUtils.translateTo(mainPts, figure, printableLayer); |
| PointList envelopingPts = calculateEnvelopingPolyline(mainPts, |
| (int) Math.max(connectionMargin, mainPoly |
| .getLineWidth() >> 1)); |
| envelopingPts.translate(new PrecisionPoint(-origin.preciseX(), |
| -origin.preciseY())); |
| mm.LPtoDP(envelopingPts); |
| envelopingPts.performScale(scale); |
| |
| List<Point> pts = new ArrayList(envelopingPts.size()); |
| for (int i = 0; i < envelopingPts.size(); i++) { |
| pts.add(envelopingPts.getPoint(i)); |
| } |
| |
| position.setPolyline(pts); |
| } |
| result.add(0, position); |
| } else if ((view != null && view.isSetElement()) |
| || (part instanceof ShapeEditPart |
| || part instanceof ShapeCompartmentEditPart || part instanceof LabelEditPart)) { |
| PartPositionInfo position = new PartPositionInfo(); |
| |
| position.setView(view); |
| position.setSemanticElement(ViewUtil |
| .resolveSemanticElement(view)); |
| |
| if (figure.isShowing() && !figure.getBounds().isEmpty()) { |
| PrecisionRectangle bounds = new PrecisionRectangle(figure |
| .getBounds()); |
| DiagramImageUtils.translateTo(bounds, figure, printableLayer); |
| bounds.translate(new PrecisionPoint(-origin.preciseX(), -origin |
| .preciseY())); |
| mm.LPtoDP(bounds); |
| |
| bounds.performScale(scale); |
| |
| position.setPartHeight(bounds.height); |
| position.setPartWidth(bounds.width); |
| position.setPartX(bounds.x); |
| position.setPartY(bounds.y); |
| } |
| result.add(0, position); |
| } |
| } |
| return result; |
| } |
| |
| private static void getNestedEditParts(IGraphicalEditPart childEditPart, |
| Collection editParts) { |
| |
| for (Iterator iter = childEditPart.getChildren().iterator(); iter |
| .hasNext();) { |
| |
| IGraphicalEditPart child = (IGraphicalEditPart) iter.next(); |
| editParts.add(child); |
| getNestedEditParts(child, editParts); |
| } |
| } |
| |
| /** |
| * Calculates enveloping polyline for a given polyline with margin MARGIN |
| * |
| * E1 E2 |
| * +----------------+ |
| * | |<------- MARGIN |
| * A *----------------* B |
| * | | |
| * +----------------+ |
| * E4 E3 |
| * |
| * On the figure above: AB is a given polyline. E1E2E3E4 is enveloping |
| * polyline built around AB perimeter using margin MARGIN. |
| * |
| * |
| * @param polyPts |
| * @param origin |
| * location of the main diagram bounding box used to shift |
| * coordinates to be relative against diagram |
| * |
| * @return List of Point type objects (that carry X and Y coordinate pair) |
| * representing the polyline |
| */ |
| private static PointList calculateEnvelopingPolyline(PointList polyPts, int margin) { |
| PointList result = new PrecisionPointList(polyPts.size() << 1); |
| List<LineSeg> mainSegs = (List<LineSeg>) PointListUtilities.getLineSegments(polyPts); |
| removeRedundantSegments(mainSegs); |
| if (mainSegs.size() > 0) { |
| result = calculateParallelPolyline(mainSegs, margin); |
| PointList pts = calculateParallelPolyline(mainSegs, -margin); |
| for (int i = pts.size() - 1; i >= 0; i--) { |
| result.addPoint(pts.getPoint(i)); |
| } |
| result.addPoint(result.getFirstPoint()); |
| } |
| return result; |
| } |
| |
| private static void removeRedundantSegments(List<LineSeg> polyPts) { |
| for (Iterator<LineSeg> itr = polyPts.listIterator(); itr.hasNext();) { |
| LineSeg lineSeg = itr.next(); |
| if (lineSeg.getOrigin().equals(lineSeg.getTerminus())) { |
| itr.remove(); |
| } |
| } |
| } |
| |
| /** |
| * Calculates polyline offset from the given polyline by the margin value |
| * |
| * ResultA ResultB |
| * +----------------+ |
| * | |<------- MARGIN |
| * A *----------------* B |
| * |
| * On the figure above: AB is a given polyline. ResultA-ResultB is the result |
| * @param polySegs given polyline |
| * @param margin offset from given poly-line, can be negative. |
| * @return offset or parallel polyline. |
| */ |
| private static PointList calculateParallelPolyline(List<LineSeg> polySegs, int margin) { |
| PointList result = new PrecisionPointList(polySegs.size() << 2); |
| int index = 0; |
| int absMargin = Math.abs(margin); |
| Sign sign = margin < 0 ? Sign.NEGATIVE : Sign.POSITIVE; |
| LineSeg parallel_1, parallel_2; |
| result.addPoint(polySegs.get(index++).locatePoint(0, absMargin, sign)); |
| parallel_1 = polySegs.get(index - 1).getParallelLineSegThroughPoint(result.getLastPoint()); |
| for (; index < polySegs.size(); index++) { |
| parallel_2 = polySegs.get(index).getParallelLineSegThroughPoint( |
| polySegs.get(index).locatePoint(0, absMargin, sign)); |
| PointList intersections = parallel_1.getLinesIntersections(parallel_2); |
| if (intersections.size() > 0) { |
| result.addPoint(intersections.getFirstPoint()); |
| } else { |
| result.addPoint(parallel_1.getTerminus()); |
| result.addPoint(parallel_2.getOrigin()); |
| } |
| parallel_1 = parallel_2; |
| } |
| result.addPoint(polySegs.get(index - 1).locatePoint(1.0, absMargin, sign)); |
| return result; |
| } |
| |
| } |
| |