/// <summary> /// Resolves references between shapes (indicated as lines on the diagram) /// </summary> /// <param name="list">Any list of <seealso cref="LayoutShape"/>s that need to have the shape references resolved</param> /// <returns>The shape with the most references in the list (typically treated as the root shape)</returns> /// <remarks>Any <see cref="LayoutShape"/> where the <see cref="LayoutShape.Placed"/> property is <see langword="true"/> /// should be ignored.</remarks> public override LayoutShape ResolveReferences(LayoutShapeList list) { LayoutShape mostChildren = null; LayoutShapeList allShapes = myLayoutShapes; foreach (LayoutShape layshape in list) { if (!layshape.Placed) { layshape.ResolveReferences(allShapes); if ((mostChildren == null || layshape.Count > mostChildren.Count) && !(layshape.Shape is FactTypeShape) && !(layshape.Shape is ExternalConstraintShape)) { mostChildren = layshape; } } } return mostChildren; }
/// <summary> /// Resolves references between shapes (indicated as lines on the diagram) /// </summary> /// <param name="list">Any list of <seealso cref="LayoutShape"/>s that need to have the shape references resolved</param> /// <returns>The shape with the most references in the list (typically treated as the root shape)</returns> /// <remarks>Any <see cref="LayoutShape"/> where the <see cref="LayoutShape.Placed"/> property is <see langword="true"/> /// should be ignored.</remarks> public override LayoutShape ResolveReferences(LayoutShapeList list) { LayoutShape mostChildren = null; LayoutShapeList allShapes = myLayoutShapes; foreach (LayoutShape layshape in list) { if (!layshape.Placed) { layshape.ResolveReferences(allShapes); if ((mostChildren == null || layshape.Count > mostChildren.Count) && !(layshape.Shape is FactTypeShape) && !(layshape.Shape is ExternalConstraintShape)) { mostChildren = layshape; } } } return(mostChildren); }
/// <summary> /// Adds the <paramref name="shape"/> with the specified <paramref name="pinned"/> value. /// </summary> /// <param name="shape">The shape to lay out on the diagram.</param> /// <param name="pinned">Indicates whether the shape is pinned in place or not. True means the shape is pinned to its current location.</param> public void AddShape(ShapeElement shape, bool pinned) { NodeShape ns = shape as NodeShape; if (ns == null || shape.ParentShape as ORMDiagram == null) { return; } LayoutShapeList list = myLayoutShapes; LayoutShape layshape; // If the shape doesn't exist, add it, otherwise simply modify the pinned value. if (!list.TryGetValue(ns, out layshape)) { layshape = new LayoutShape(ns, pinned); list.Add(layshape); } else { layshape.Pinned = pinned; } }
/// <summary> /// Organizes the shapes added with <see cref="AddShape(ShapeElement)"/> or similar methods. /// </summary> /// <param name="ignoreExistingShapes">Do not adjust for existing shapes</param> /// <param name="referenceShape">Layout shapes to the right of this shape.</param> /// <param name="layoutRightOfReferenceShape">Set to layout to the right of the <paramref name="referenceShape"/></param> /// <param name="layoutBelowReferenceShape">Set to layout below the <paramref name="referenceShape"/></param> public void Layout(bool ignoreExistingShapes, NodeShape referenceShape, bool layoutRightOfReferenceShape, bool layoutBelowReferenceShape) { LayoutShape backupRoot = null; LayoutShapeList allShapes = myLayoutShapes; switch (allShapes.Count) { case 0: return; default: backupRoot = allShapes[0]; break; } myLayoutEngine.LateBind(myDiagram, allShapes); SizeD margin = myDiagram.NestedShapesMargin; if (referenceShape != null && (layoutRightOfReferenceShape || layoutBelowReferenceShape)) { RectangleD referenceBounds = referenceShape.AbsoluteBounds; if (layoutRightOfReferenceShape) { margin.Width = Math.Max(margin.Width, referenceBounds.Right); } if (layoutBelowReferenceShape) { margin.Height = Math.Max(margin.Height, referenceBounds.Bottom); } } PointD minimumPoint = new PointD(margin.Width, margin.Height); RectangleD layoutRectangle = new RectangleD(minimumPoint, SizeD.Empty); bool firstPass = true; for (; ;) { LayoutShape mostRelatives = myLayoutEngine.ResolveReferences(allShapes); LayoutShape root = null; // If the root shape was set by the user, AND the shape exists in our shape list if (myRootShape == null || !allShapes.TryGetValue(myRootShape, out root)) { root = GetRoot(mostRelatives); } if (root == null) { if (backupRoot == null) { myLayoutEngine.PostLayout(minimumPoint); break; } root = backupRoot; } // run the layout of base shapes myLayoutEngine.PerformLayout(root, new PointD(margin.Width, layoutRectangle.Bottom + (firstPass ? 0 : root.Shape.Size.Height)), ref layoutRectangle); firstPass = false; backupRoot = null; myRootShape = null; } Reflow(ignoreExistingShapes); }
/// <summary> /// Places each external constraint shape at the point corresponding to the average of all of its referenced shapes. /// Frequency constraints are handled differently, since they apply to only one fact type (but 1 or more roles in that /// fact type) at any time. /// </summary> /// <param name="minimumPoint">The minimum location for new element placement</param> public override void PostLayout(PointD minimumPoint) { ResolveReferences(myConstraintShapes); foreach (LayoutShape shape in myConstraintShapes) { if (!shape.Pinned) { PointD avg = new PointD(0, 0); NodeShape nodeShape = shape.Shape; FrequencyConstraintShape freqShape; FrequencyConstraint constraint; LinkedElementCollection <FactType> relatedFactTypes; int count; if (null != (freqShape = nodeShape as FrequencyConstraintShape) && null != (constraint = freqShape.ModelElement as FrequencyConstraint) && 1 == (relatedFactTypes = constraint.FactTypeCollection).Count) { Diagram diagram = myDiagram; FactType factType = relatedFactTypes[0]; FactTypeShape factTypeShape = null; LayoutShape factTypeLayoutShape = null; foreach (PresentationElement pel in PresentationViewsSubject.GetPresentation(factType)) { FactTypeShape testShape = pel as FactTypeShape; if (testShape != null && testShape.Diagram == diagram) { if (factTypeShape == null) { factTypeShape = testShape; } if (myLayoutShapes.TryGetValue(testShape, out factTypeLayoutShape)) { factTypeShape = testShape; break; } } } LinkedElementCollection <Role> constraintRoles = constraint.RoleCollection; LinkedElementCollection <RoleBase> displayOrder = factTypeShape.DisplayedRoleOrder; DisplayOrientation orientation = factTypeShape.DisplayOrientation; RectangleD shapeBounds = factTypeShape.AbsoluteBounds; SizeD shapeSize = factTypeShape.Size; PointD location = (factTypeLayoutShape != null) ? factTypeLayoutShape.TargetLocation : shapeBounds.Location; count = constraintRoles.Count; double width = shapeSize.Width; double height = shapeSize.Height; for (int i = 0; i < count; i++) { int targetIndex = displayOrder.IndexOf(constraintRoles[i]); switch (orientation) { case DisplayOrientation.Horizontal: avg.Offset((width / (targetIndex + 1)) + location.X, location.Y - height); break; case DisplayOrientation.VerticalRotatedRight: avg.Offset(location.X + width, (height / (targetIndex + 1)) + location.Y); break; case DisplayOrientation.VerticalRotatedLeft: avg.Offset(location.X + width, height - (height / (targetIndex + 1)) + location.Y); break; } } avg.X /= count; avg.Y /= count; } else if (0 != (count = shape.Count)) { double minX = double.MaxValue; double minY = double.MaxValue; double maxX = 0; double maxY = 0; SizeD size; LayoutShapeList relatedShapes = shape.RelatedShapes; // Take the center of farthest bounds as the location. // This is the same as the average for two elements, but // balances more than two elements much better. for (int i = 0; i < count; ++i) { LayoutShape relatedShape = relatedShapes[i]; PointD location = relatedShape.TargetLocation; size = relatedShape.Shape.Size; double x = location.X + size.Width / 2; double y = location.Y + size.Height / 2; minX = Math.Min(minX, x); minY = Math.Min(minY, y); maxX = Math.Max(maxX, x); maxY = Math.Max(maxY, y); } size = nodeShape.Size; avg.X = (maxX + minX) / 2 - size.Width / 2; avg.Y = (maxY + minY) / 2 - size.Height / 2; // Constraints are frequently ending up directly on top of // an ObjectTypeShape, bump them up a bit double bumpAdjust = size.Height * 2; avg.Y -= bumpAdjust; if (avg.Y < minimumPoint.Y) { avg.Y += bumpAdjust + bumpAdjust; } } shape.TargetLocation = avg; } shape.Placed = true; } // Now add the shapes back into the main myLayoutShape list for reflow foreach (LayoutShape shape in myConstraintShapes) { myLayoutShapes.Add(shape); } myConstraintShapes.Clear(); }