SymPath PathBetweenControls(ControlPosition controlPosition1, ControlPosition controlPosition2, ForkPosition forkStart, out PointF dropTargetPosition) { float xStart = controlPosition1.x; float yStart = controlPosition1.y; float xEnd = controlPosition2.x; float yEnd = controlPosition2.y; if (forkStart != null) { if (forkStart.loopFallThru) { dropTargetPosition = LocationFromAbstractPosition(xEnd, yEnd - 0.5F); } else { // above end of fork start. dropTargetPosition = LocationFromAbstractPosition(forkStart.x, forkStart.y - 0.5F); } } else if (xEnd != xStart) { // Below start control (use when a join is going) dropTargetPosition = LocationFromAbstractPosition(xStart, yStart + 0.5F); } else { // Above end control (other cases). dropTargetPosition = LocationFromAbstractPosition(xEnd, yEnd - 0.5F); } bool startHorizontal = false; if (forkStart != null) startHorizontal = forkStart.loopStart; if (forkStart != null && forkStart.x != controlPosition2.x) { // The fork start in a different horizontal position than it ends. This is probably due to a fork with no controls on it. // Create the path in two pieces. float xMiddle = forkStart.x; float yMiddle = forkStart.y; SymPath path1 = PathFromStartToEnd(xStart, yStart, xMiddle, yMiddle, startHorizontal, 0); SymPath path3 = PathFromStartToEnd(xMiddle, yMiddle, xEnd, yEnd, false, controlPosition2.loopBottom); SymPath path2 = new SymPath(new[] { path1.LastPoint, path3.FirstPoint }); return SymPath.Join(SymPath.Join(path1, path2, PointKind.Normal), path3, PointKind.Normal); } else { return PathFromStartToEnd(xStart, yStart, xEnd, yEnd, startHorizontal, controlPosition2.loopBottom); } }
private void CreateLegBetweenControls(CourseView.ControlView controlView1, ControlPosition controlPosition1, CourseView.ControlView controlView2, ControlPosition controlPosition2, int splitLegIndex, ForkPosition forkStart) { PointF dropTargetPosition; SymPath path = PathBetweenControls(controlPosition1, controlPosition2, forkStart, out dropTargetPosition); CourseObj courseObj = new TopologyLegCourseObj(controlView1.controlId, controlView1.courseControlIds[splitLegIndex], controlView2.courseControlIds[0], scaleRatio, appearance, path); CourseLayer layer; if (LegInSpecificVariation(controlView1.courseControlIds[splitLegIndex], controlView2.courseControlIds[0])) layer = courseLayerSpecificVariation; else layer = courseLayerAllVariationsAndParts; courseObj.layer = layer; courseLayout.AddCourseObject(courseObj); // Add the drop target. courseObj = new TopologyDropTargetCourseObj(controlView1.controlId, controlView1.courseControlIds[splitLegIndex], controlView2.courseControlIds[0], scaleRatio, appearance, dropTargetPosition); courseObj.layer = layer; courseLayout.AddCourseObject(courseObj); if (forkStart != null && forkStart.variationCode != '\0') { // There is a variation fork. courseObj = CreateVariationCode(controlView1, controlPosition1, splitLegIndex, forkStart); courseLayout.AddCourseObject(courseObj); } }
private CourseObj CreateVariationCode(CourseView.ControlView controlView1, ControlPosition controlPosition1, int splitLegIndex, ForkPosition forkStart) { // Delta between position of fork start and position of code. float deltaX = (forkStart.x < controlPosition1.x) ? -0.4F : 0.4F; float deltaY = -0.4F; float x = forkStart.x + deltaX; float y = forkStart.y + deltaY; string text = "(" + forkStart.variationCode + ")"; CourseObj courseObj = new VariationCodeCourseObj(controlView1.controlId, controlView1.courseControlIds[splitLegIndex], scaleRatio, appearance, text, LocationFromAbstractPosition(x, y)); courseObj.layer = CourseLayer.AllVariations; return courseObj; }
// Assign positions/sizes from startIndex upto (not including) endIndex, starting at given position. // Return total size used. private SizeF AssignControlPositions(int startIndex, int endIndex, float startX, float startY) { int index = startIndex; float x = startX, y = startY; float totalWidth = 1, totalHeight = 0; // Always at least a width of 1. while (index != endIndex) { int numForks = (controlViewsAllVariationsAndParts[index].legTo == null) ? 0 : controlViewsAllVariationsAndParts[index].legTo.Length; // Simple case, no splitting. controlPositions[index] = new ControlPosition() { x = x, y = y, }; totalWidth = Math.Max(totalWidth, 1); totalHeight += 1; y += 1; if (numForks > 1) { bool loop = (controlViewsAllVariationsAndParts[index].joinIndex == index); // fork or loop subsequent. Two passes -- first determine totalWidth and maxHeight; float totalForkWidth = 0, maxForkHeight = 1; SizeF[] forkSize = new SizeF[numForks]; ForkPosition[] forkStart = new ForkPosition[numForks]; int startFork = 0; if (loop) { startFork = 1; totalForkWidth = 1; } // Get size of each fork. for (int i = startFork; i < numForks; ++i) { forkSize[i] = AssignControlPositions(controlViewsAllVariationsAndParts[index].legTo[i], controlViewsAllVariationsAndParts[index].joinIndex, 0, 0); totalForkWidth += forkSize[i].Width; maxForkHeight = Math.Max(maxForkHeight, forkSize[i].Height); } // Get position of each fork. if (loop) { float forkY = y; float forkX = x; forkStart[0] = new ForkPosition(forkX, forkY, false, true, '\0'); int halfForks = (numForks + 1) / 2; totalForkWidth = 0; forkX = x - 0.5F; for (int i = startFork; i < halfForks; ++i) { forkX -= forkSize[i].Width; } totalForkWidth = Math.Max(totalForkWidth, (x-forkX) * 2); for (int i = startFork; i < halfForks; ++i) { forkX += forkSize[i].Width / 2; forkStart[i] = new ForkPosition(forkX, forkY, loop, false, variationMap[controlViewsAllVariationsAndParts[index].courseControlIds[i]]); forkX += forkSize[i].Width / 2; } forkX = x + 0.5F; for (int i = halfForks; i < numForks; ++i) { forkX += forkSize[i].Width / 2; forkStart[i] = new ForkPosition(forkX, forkY, loop, false, variationMap[controlViewsAllVariationsAndParts[index].courseControlIds[i]]); forkX += forkSize[i].Width / 2; } totalForkWidth = Math.Max(totalForkWidth, (forkX-x) * 2); controlPositions[index].loopBottom = y + maxForkHeight - 0.5F; } else { float forkY = y + 0.5F; float forkX = x - totalForkWidth / 2; for (int i = startFork; i < numForks; ++i) { forkX += forkSize[i].Width / 2; forkStart[i] = new ForkPosition(forkX, forkY, loop, false, variationMap[controlViewsAllVariationsAndParts[index].courseControlIds[i]]); forkX += forkSize[i].Width / 2; } } controlPositions[index].forkStart = forkStart; // Assign control positions for each fork again, now that we know the start position. for (int i = startFork; i < numForks; ++i) { AssignControlPositions(controlViewsAllVariationsAndParts[index].legTo[i], controlViewsAllVariationsAndParts[index].joinIndex, forkStart[i].x, forkStart[i].y); } float height = maxForkHeight + 1; totalHeight += height; y += height; totalWidth = Math.Max(totalWidth, totalForkWidth); if (index == controlViewsAllVariationsAndParts[index].joinIndex) index = controlViewsAllVariationsAndParts[index].legTo[0]; else index = controlViewsAllVariationsAndParts[index].joinIndex; } else { if (controlViewsAllVariationsAndParts[index].legTo != null && controlViewsAllVariationsAndParts[index].legTo.Length > 0) index = controlViewsAllVariationsAndParts[index].legTo[0]; else break; // no more controls. } } return new SizeF(totalWidth, totalHeight); }
// Assign positions/sizes from startIndex upto (not including) endIndex, starting at given position. // Return total size used. private SizeF AssignControlPositions(int startIndex, int endIndex, float startX, float startY) { int index = startIndex; float x = startX, y = startY; float totalWidth = 1, totalHeight = 0; // Always at least a width of 1. while (index != endIndex) { int numForks = (controlViewsAllVariationsAndParts[index].legTo == null) ? 0 : controlViewsAllVariationsAndParts[index].legTo.Length; // Simple case, no splitting. controlPositions[index] = new ControlPosition() { x = x, y = y, }; totalWidth = Math.Max(totalWidth, 1); totalHeight += 1; y += 1; if (numForks > 1) { bool loop = (controlViewsAllVariationsAndParts[index].joinIndex == index); // fork or loop subsequent. Two passes -- first determine totalWidth and maxHeight; float totalForkWidth = 0, maxForkHeight = 1; SizeF[] forkSize = new SizeF[numForks]; ForkPosition[] forkStart = new ForkPosition[numForks]; int startFork = 0; if (loop) { startFork = 1; totalForkWidth = 1; } // Get size of each fork. for (int i = startFork; i < numForks; ++i) { forkSize[i] = AssignControlPositions(controlViewsAllVariationsAndParts[index].legTo[i], controlViewsAllVariationsAndParts[index].joinIndex, 0, 0); totalForkWidth += forkSize[i].Width; maxForkHeight = Math.Max(maxForkHeight, forkSize[i].Height); } // Get position of each fork. if (loop) { float forkY = y; float forkX = x; forkStart[0] = new ForkPosition(forkX, forkY, false, true, '\0'); int halfForks = (numForks + 1) / 2; totalForkWidth = 0; forkX = x - 0.5F; for (int i = startFork; i < halfForks; ++i) { forkX -= forkSize[i].Width; } totalForkWidth = Math.Max(totalForkWidth, (x - forkX) * 2); for (int i = startFork; i < halfForks; ++i) { forkX += forkSize[i].Width / 2; forkStart[i] = new ForkPosition(forkX, forkY, loop, false, variationMap[controlViewsAllVariationsAndParts[index].courseControlIds[i]]); forkX += forkSize[i].Width / 2; } forkX = x + 0.5F; for (int i = halfForks; i < numForks; ++i) { forkX += forkSize[i].Width / 2; forkStart[i] = new ForkPosition(forkX, forkY, loop, false, variationMap[controlViewsAllVariationsAndParts[index].courseControlIds[i]]); forkX += forkSize[i].Width / 2; } totalForkWidth = Math.Max(totalForkWidth, (forkX - x) * 2); controlPositions[index].loopBottom = y + maxForkHeight - 0.5F; } else { float forkY = y + 0.5F; float forkX = x - totalForkWidth / 2; for (int i = startFork; i < numForks; ++i) { forkX += forkSize[i].Width / 2; forkStart[i] = new ForkPosition(forkX, forkY, loop, false, variationMap[controlViewsAllVariationsAndParts[index].courseControlIds[i]]); forkX += forkSize[i].Width / 2; } } controlPositions[index].forkStart = forkStart; // Assign control positions for each fork again, now that we know the start position. for (int i = startFork; i < numForks; ++i) { AssignControlPositions(controlViewsAllVariationsAndParts[index].legTo[i], controlViewsAllVariationsAndParts[index].joinIndex, forkStart[i].x, forkStart[i].y); } float height = maxForkHeight + 1; totalHeight += height; y += height; totalWidth = Math.Max(totalWidth, totalForkWidth); if (index == controlViewsAllVariationsAndParts[index].joinIndex) { index = controlViewsAllVariationsAndParts[index].legTo[0]; } else { index = controlViewsAllVariationsAndParts[index].joinIndex; } } else { if (controlViewsAllVariationsAndParts[index].legTo != null && controlViewsAllVariationsAndParts[index].legTo.Length > 0) { index = controlViewsAllVariationsAndParts[index].legTo[0]; } else { break; // no more controls. } } } return(new SizeF(totalWidth, totalHeight)); }
private CourseObj CreateVariationCode(CourseView.ControlView controlView1, ControlPosition controlPosition1, int splitLegIndex, ForkPosition forkStart) { // Delta between position of fork start and position of code. float deltaX = (forkStart.x < controlPosition1.x) ? -0.4F : 0.4F; float deltaY = -0.4F; float x = forkStart.x + deltaX; float y = forkStart.y + deltaY; string text = "(" + forkStart.variationCode + ")"; CourseObj courseObj = new VariationCodeCourseObj(controlView1.controlId, controlView1.courseControlIds[splitLegIndex], courseObjRatio, appearance, text, LocationFromAbstractPosition(x, y)); courseObj.layer = CourseLayer.AllVariations; return(courseObj); }
// Determine the path between controls, and the drop targets. SymPath PathBetweenControls(ControlPosition controlPosition1, ControlPosition controlPosition2, ForkPosition forkStart, out List <DropTarget> dropTargets) { float xStart = controlPosition1.x; float yStart = controlPosition1.y; float xEnd = controlPosition2.x; float yEnd = controlPosition2.y; dropTargets = new List <DropTarget>(); if (forkStart != null) { if (forkStart.loopFallThru) { dropTargets.Add(new DropTarget(xEnd, yEnd - 0.5F, LegInsertionLoc.Normal)); } else if (forkStart.loopStart) { dropTargets.Add(new DropTarget(forkStart.x, forkStart.y - 0.5F, LegInsertionLoc.Normal)); } else { dropTargets.Add(new DropTarget(xStart, yStart + 0.5F, LegInsertionLoc.PreSplit)); dropTargets.Add(new DropTarget(forkStart.x, forkStart.y - 0.5F, LegInsertionLoc.Normal)); if (forkStart.x != xEnd) { // Empty fork. dropTargets.Add(new DropTarget(xEnd, yEnd - 0.5F, LegInsertionLoc.PostJoin)); } } } else if (xEnd != xStart) { // Below start control (use when a join is going) dropTargets.Add(new DropTarget(xStart, yStart + 0.5F, LegInsertionLoc.Normal)); if (yEnd > yStart) { // Right before join point. dropTargets.Add(new DropTarget(xEnd, yEnd - 0.5F, LegInsertionLoc.PostJoin)); } } else if (yEnd > yStart + 1.25) { // Straight down, but must have join at end. dropTargets.Add(new DropTarget(xStart, yStart + 0.5F, LegInsertionLoc.Normal)); dropTargets.Add(new DropTarget(xEnd, yEnd - 0.5F, LegInsertionLoc.PostJoin)); } else { // Above end control (other cases). dropTargets.Add(new DropTarget(xEnd, yEnd - 0.5F, LegInsertionLoc.Normal)); } bool startHorizontal = false; if (forkStart != null) { startHorizontal = forkStart.loopStart; } if (forkStart != null && forkStart.x != controlPosition2.x) { // The fork start in a different horizontal position than it ends. This is probably due to a fork with no controls on it. // Create the path in two pieces. float xMiddle = forkStart.x; float yMiddle = forkStart.y; SymPath path1 = PathFromStartToEnd(xStart, yStart, xMiddle, yMiddle, startHorizontal, 0); SymPath path3 = PathFromStartToEnd(xMiddle, yMiddle, xEnd, yEnd, false, controlPosition2.loopBottom); SymPath path2 = new SymPath(new[] { path1.LastPoint, path3.FirstPoint }); return(SymPath.Join(SymPath.Join(path1, path2, PointKind.Normal), path3, PointKind.Normal)); } else { return(PathFromStartToEnd(xStart, yStart, xEnd, yEnd, startHorizontal, controlPosition2.loopBottom)); } }
private void CreateLegBetweenControls(CourseView.ControlView controlView1, ControlPosition controlPosition1, CourseView.ControlView controlView2, ControlPosition controlPosition2, int splitLegIndex, ForkPosition forkStart) { List <DropTarget> dropTargets; SymPath path = PathBetweenControls(controlPosition1, controlPosition2, forkStart, out dropTargets); CourseObj courseObj = new TopologyLegCourseObj(controlView1.controlId, controlView1.courseControlIds[splitLegIndex], controlView2.courseControlIds[0], courseObjRatio, appearance, path); CourseLayer layer; if (LegInSpecificVariation(controlView1.courseControlIds[splitLegIndex], controlView2.courseControlIds[0])) { layer = courseLayerSpecificVariation; } else { layer = courseLayerAllVariationsAndParts; } courseObj.layer = layer; courseLayout.AddCourseObject(courseObj); // No drop targets between map issue and start. if (!(eventDB.GetControl(controlView1.controlId).kind == ControlPointKind.MapIssue && eventDB.GetControl(controlView2.controlId).kind == ControlPointKind.Start)) { // Add the drop targets foreach (DropTarget dropTarget in dropTargets) { courseObj = new TopologyDropTargetCourseObj(controlView1.controlId, controlView1.courseControlIds[splitLegIndex], controlView2.courseControlIds[0], courseObjRatio, appearance, LocationFromAbstractPosition(dropTarget.abstractX, dropTarget.abstractY), dropTarget.insertionLoc); // Along the selected leg, show the drop targets in light gray. Along other legs, drop targets are invisible. if (controlView1.courseControlIds[splitLegIndex] == courseControlIdSelection1 && controlView2.courseControlIds.Contains(courseControlIdSelection2)) { courseObj.layer = CourseLayer.AllVariations; } else { courseObj.layer = CourseLayer.InvisibleObjects; } courseLayout.AddCourseObject(courseObj); } } if (forkStart != null && forkStart.variationCode != '\0') { // There is a variation fork. courseObj = CreateVariationCode(controlView1, controlPosition1, splitLegIndex, forkStart); courseLayout.AddCourseObject(courseObj); } }