A course view is a static view of all or part of a course. It is a static snapshot, and doesn't change if the underlying course changes. It also handles subsetting for map exchanges, relay variations, the all controls view, etc. It is the basis for control descriptions and the course drawing.
예제 #1
0
        // Format the given CourseView into a bunch of course objects, and add it to the given course Layout
        public RectangleF FormatCourseToLayout(SymbolDB symbolDB, CourseView courseViewAllVariations, CourseView specificVariation, CourseLayout courseLayout, CourseLayer layerAllVariations, CourseLayer layerSpecificVariation)
        {
            this.eventDB = courseViewAllVariations.EventDB;
            this.symbolDB = symbolDB;
            this.courseLayout = courseLayout;
            this.courseLayerAllVariationsAndParts = layerAllVariations;
            this.courseLayerSpecificVariation = layerSpecificVariation;
            this.controlViewsAllVariationsAndParts = courseViewAllVariations.ControlViews;
            this.controlViewsSpecificVariation = specificVariation.ControlViews;
            this.controlPositions = new ControlPosition[controlViewsAllVariationsAndParts.Count];
            this.courseControlIdsSpecificVariation = QueryEvent.EnumCourseControlIds(eventDB, specificVariation.CourseDesignator).ToArray();
            this.variationMap = QueryEvent.GetVariantCodeMapping(eventDB, courseViewAllVariations.CourseDesignator);

            SizeF totalAbstractSize = AssignControlPositions(0, controlViewsAllVariationsAndParts.Count, 0, 0);

            // Now create objects now that the positions have been created.
            scaleRatio = 1.0F;
            appearance = new CourseAppearance();

            for (int index = 0; index < controlViewsAllVariationsAndParts.Count; ++index) {
                CreateObjectsForControlView(controlViewsAllVariationsAndParts[index], controlPositions[index]);
            }

            PointF bottomCenter = LocationFromAbstractPosition(0, 0);
            SizeF size = SizeFromAbstractSize(totalAbstractSize);
            RectangleF rect = new RectangleF(bottomCenter.X - size.Width / 2, bottomCenter.Y - size.Height, size.Width, size.Height);
            rect.Inflate(widthUnit, heightUnit);
            return rect;
        }
예제 #2
0
 // Get the description kind to use.
 private DescriptionKind GetDescriptionKind(CourseView courseView)
 {
     if (descPrintSettings.UseCourseDefault) {
         return QueryEvent.GetDefaultDescKind(eventDB, courseView.BaseCourseId);
     }
     else {
         return descPrintSettings.DescKind;
     }
 }
예제 #3
0
        // Get a punch pattern renderer for rendering the description from a course view.
        private PunchesRenderer GetRenderer(CourseView courseView)
        {
            PunchcardFormat punchcardFormat = eventDB.GetEvent().punchcardFormat;

            PunchesRenderer renderer = new PunchesRenderer(eventDB);
            renderer.CellSize = punchPrintSettings.BoxSize / 0.254F;
            renderer.CourseView = courseView;
            renderer.PunchcardFormat = punchcardFormat;
            renderer.Margin = 0;

            return renderer;
        }
예제 #4
0
        // Get the angle from the given control index to the next control.
        public static double ComputeAngleOut(EventDB eventDB, CourseView courseView, int controlIndex)
        {
            PointF pt1 = eventDB.GetControl(courseView.ControlViews[controlIndex].controlId).location;

            // Get index of next control.
            int nextControlIndex = courseView.GetNextControl(controlIndex);
            if (nextControlIndex < 0)
                return double.NaN;

            // By default, the location of the next control is the direction.
            PointF pt2 = eventDB.GetControl(courseView.ControlViews[nextControlIndex].controlId).location;

            // If there is a custom leg, then use the location of the first bend instead.
            Id<Leg> legId = QueryEvent.FindLeg(eventDB, courseView.ControlViews[controlIndex].controlId, courseView.ControlViews[nextControlIndex].controlId);
            if (legId.IsNotNone) {
                Leg leg = eventDB.GetLeg(legId);
                if (leg.bends != null && leg.bends.Length > 0)
                    pt2 = leg.bends[0];
            }

            return Math.Atan2(pt2.Y - pt1.Y, pt2.X - pt1.X);
        }
예제 #5
0
        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);
            }
        }
예제 #6
0
        private void CreateControlNumber(CourseView.ControlView controlView, ControlPosition controlPosition)
        {
            Id<ControlPoint> controlId = controlView.controlId;
            Id<CourseControl> courseControlId = controlView.courseControlIds[0];
            ControlPoint control = eventDB.GetControl(controlId);
            PointF location = LocationFromAbstractPosition(controlPosition.x, controlPosition.y);

            CourseLayer layer;

            if (ControlViewInSpecificVariation(controlView))
                layer = courseLayerSpecificVariation;
            else
                layer = courseLayerAllVariationsAndParts;

            CourseObj courseObj;

            switch (control.kind) {
                case ControlPointKind.Start:
                case ControlPointKind.MapExchange:
                    // Triangle looks best if we displace it down a bit (0.8 looks right).
                    courseObj = new StartCourseObj(controlId, courseControlId, scaleRatio * 0.75F, appearance, 0, new PointF(location.X, location.Y - 0.8F), CrossHairOptions.NoCrossHair);
                    break;

                case ControlPointKind.Finish:
                    courseObj = new FinishCourseObj(controlId, courseControlId, scaleRatio * 0.75F, appearance, null, location, CrossHairOptions.NoCrossHair);
                    break;

                case ControlPointKind.Normal:
                    courseObj = new ControlNumberCourseObj(controlId, courseControlId, scaleRatio, appearance, control.code, location);
                    break;

                case ControlPointKind.CrossingPoint:
                    courseObj = new CrossingCourseObj(controlId, courseControlId, Id<Special>.None, scaleRatio * 1.5F, appearance, 0, location);
                    break;

                default:
                    Debug.Fail("bad control kind");
                    return;
            }

            courseObj.layer = layer;
            courseLayout.AddCourseObject(courseObj);
        }
예제 #7
0
        // Create the normal view onto a score course
        private static CourseView CreateScoreCourseView(EventDB eventDB, CourseDesignator courseDesignator)
        {
            Course course = eventDB.GetCourse(courseDesignator.CourseId);
            CourseView courseView = new CourseView(eventDB, courseDesignator);
            Id<CourseControl> courseControlId;

            courseView.courseName = course.name;
            courseView.scoreColumn = course.scoreColumn;

            courseControlId = course.firstCourseControl;

            while (courseControlId.IsNotNone) {
                ControlView controlView = new ControlView();
                CourseControl courseControl = eventDB.GetCourseControl(courseControlId);

                controlView.courseControlIds = new[] { courseControlId };
                controlView.controlId = courseControl.control;

                // Ordinals assigned after sorting.
                controlView.ordinal = -1;

                controlView.joinIndex = -1;

                // Move to the next control.
                courseView.controlViews.Add(controlView);
                courseControlId = courseControl.nextCourseControl;
            }

            // Sort the control views: first by kind, then by score, then by code.
            courseView.controlViews.Sort(delegate(ControlView view1, ControlView view2) {
                ControlPoint control1 = eventDB.GetControl(view1.controlId);
                ControlPoint control2 = eventDB.GetControl(view2.controlId);
                CourseControl courseControl1 = eventDB.GetCourseControl(view1.courseControlIds[0]);
                CourseControl courseControl2 = eventDB.GetCourseControl(view2.courseControlIds[0]);

                if (control1.kind < control2.kind)
                    return -1;
                else if (control1.kind > control2.kind)
                    return 1;

                if (courseControl1.points != courseControl2.points)
                    return courseControl1.points.CompareTo(courseControl2.points);
                int result = Util.CompareCodes(control1.code, control2.code);
                if (result != 0)
                    return result;

                return view1.controlId.id.CompareTo(view2.controlId.id);
            });

            // Assign ordinals, if applicable. If scores in column A, then no ordinals will be assigned.
            if (courseView.scoreColumn != 0) {
                int ordinal = course.firstControlOrdinal;
                foreach (ControlView control in courseView.controlViews) {
                    if (eventDB.GetControl(control.controlId).kind == ControlPointKind.Normal)
                        control.ordinal = ordinal++;
                }
            }

            courseView.Finish();
            return courseView;
        }
예제 #8
0
        // Count controls in a course view that match a predicate.
        private static string CountControls(CourseView courseView, Predicate<Id<ControlPoint>> predicate)
        {
            string desc = "";
            int[] count = new int[6];

            foreach (CourseView.ControlView controlView in courseView.ControlViews) {
                if (predicate(controlView.controlId)) {
                    ControlPoint control = courseView.EventDB.GetControl(controlView.controlId);
                    if (control.kind == ControlPointKind.Normal || control.kind == ControlPointKind.Start ||
                        control.kind == ControlPointKind.Finish || control.kind == ControlPointKind.CrossingPoint || control.kind == ControlPointKind.MapExchange)
                    {
                        count[(int) control.kind] += 1;
                    }
                }
            }

            // Add the counts of each control kind to a string.

            if (count[(int) ControlPointKind.Normal] > 0)
                desc = AddCount(desc, count[(int) ControlPointKind.Normal], SelectionDescriptionText.Control_Singular, SelectionDescriptionText.Control_Plural);

            if (count[(int) ControlPointKind.Start] > 0)
                desc = AddCount(desc, count[(int) ControlPointKind.Start], SelectionDescriptionText.Start_Singular, SelectionDescriptionText.Start_Plural);

            if (count[(int) ControlPointKind.Finish] > 0)
                desc = AddCount(desc, count[(int) ControlPointKind.Finish], SelectionDescriptionText.Finish_Singular, SelectionDescriptionText.Finish_Plural);

            if (count[(int) ControlPointKind.CrossingPoint] > 0)
                desc = AddCount(desc, count[(int) ControlPointKind.CrossingPoint], SelectionDescriptionText.MandCrossing_Singular, SelectionDescriptionText.MandCrossing_Plural);

            if (count[(int) ControlPointKind.MapExchange] > 0)
                desc = AddCount(desc, count[(int) ControlPointKind.MapExchange], SelectionDescriptionText.MapExchange_Singular, SelectionDescriptionText.MapExchange_Plural);

            // If we didn't find anthing the count string will still be empty.
            if (desc == "")
                desc = SelectionDescriptionText.None;

            return desc;
        }
예제 #9
0
        // Get a directive line for a marked route (not to the finish). The legId must be valid, because a marked route only occurs
        // with a real leg id.
        private DescriptionLine GetMarkedRouteLine(CourseView.ControlView controlViewFrom, CourseView.ControlView controlViewTo, Id<Leg> legId)
        {
            Leg leg = eventDB.GetLeg(legId);

            Debug.Assert(leg.flagging != FlaggingKind.None && leg.flagging != FlaggingKind.End);

            DescriptionLine line = new DescriptionLine();
            line.kind = DescriptionLineKind.Directive;
            line.boxes = new object[2];

            // Figure out the distance in the directive, rounded to nearest 10m.
            string distanceText;
            float distance = QueryEvent.ComputeFlaggedLegLength(eventDB, leg.controlId1, leg.controlId2, legId);
            distance = (float) (Math.Round(distance / 10.0) * 10.0);      // round to nearest 10 m.
            distanceText = string.Format("{0} m", distance);

            // Box 1: directive graphics.
            string symbolId = (leg.flagging == FlaggingKind.Begin) ? "13.1" : "13.2";
            line.boxes[0] = symbolDB[symbolId];

            // Box 2: distance of the flagging
            line.boxes[1] = distanceText;

            // Get the text version of the control using the Textifier.
            Textifier textifier = new Textifier(eventDB, symbolDB, language);
            line.textual = textifier.CreateTextForDirective(symbolId, distanceText);

            // The course control IDs, for use in coordinating the selection
            line.isLeg = true;
            line.controlId = controlViewFrom.controlId;
            line.courseControlId = controlViewFrom.courseControlIds[0];
            line.courseControlId2 = controlViewTo.courseControlIds[0];

            return line;
        }
예제 #10
0
        protected override void WriteCourseStart(CourseView courseView, string courseName, int courseNumber, string[] classNames, bool isScore, int variationNumber, VariationInfo variationInfo)
        {
            xmlWriter.WriteStartElement("Course");

            if (variationInfo != null) {
                xmlWriter.WriteElementString("Name", variationInfo.CodeString);
                xmlWriter.WriteElementString("CourseFamily", courseName);
            }
            else {
                xmlWriter.WriteElementString("Name", courseName);
            }

            if (!isScore) {
                xmlWriter.WriteElementString("Length", XmlConvert.ToString(Math.Round(courseView.MaxTotalLength / 100F) * 100F));   // round to nearest 100m
                if (courseView.TotalClimb > 0)
                    xmlWriter.WriteElementString("Climb", XmlConvert.ToString(Math.Round(courseView.TotalClimb / 5, MidpointRounding.AwayFromZero) * 5.0));  // round to nearest 5m
            }
        }
예제 #11
0
        // Get a directive line for a map exchange at a control (not to the finish).
        private DescriptionLine GetMapExchangeAtControlLine(CourseView.ControlView controlWithExchange)
        {
            DescriptionLine line = new DescriptionLine();
            line.kind = DescriptionLineKind.Directive;
            line.boxes = new object[2];

            // Distance is 0m at the control!
            string distanceText = string.Format("{0} m", 0);

            // Box 1: directive graphics.
            string symbolId = "13.5control";
            line.boxes[0] = symbolDB[symbolId];

            // Box 2: distance of the flagging
            line.boxes[1] = distanceText;

            // Get the text version of the control using the Textifier.
            Textifier textifier = new Textifier(eventDB, symbolDB, language);
            line.textual = textifier.CreateTextForDirective(symbolId, distanceText);

            // The course control IDs, for use in coordinating the selection
            line.controlId = controlWithExchange.controlId;
            line.courseControlId = controlWithExchange.courseControlIds[0];

            return line;
        }
예제 #12
0
        protected override void WriteCourseControl(ControlPointKind kind, CourseView.ControlView controlView, bool isScore, ref int sequenceNumber, ref float distanceThisLeg)
        {
            switch (kind) {
                case ControlPointKind.Start:
                    xmlWriter.WriteElementString("StartPointCode", controlCodeMap[controlView.controlId]);
                    break;

                case ControlPointKind.Finish:
                    xmlWriter.WriteElementString("FinishPointCode", controlCodeMap[controlView.controlId]);
                    if (!isScore) {
                        xmlWriter.WriteElementString("DistanceToFinish", XmlConvert.ToString(Math.Round(distanceThisLeg)));
                        distanceThisLeg = 0;
                    }
                    break;

                case ControlPointKind.MapExchange:
                case ControlPointKind.Normal:
                    xmlWriter.WriteStartElement("CourseControl");

                    // With map exchanges, the sequence can be different than the ordinals. We always use the sequence.
                    xmlWriter.WriteElementString("Sequence", XmlConvert.ToString(sequenceNumber++));

                    xmlWriter.WriteElementString("ControlCode", controlCodeMap[controlView.controlId]);

                    if (!isScore) {
                        xmlWriter.WriteElementString("LegLength", XmlConvert.ToString(Math.Round(distanceThisLeg)));
                        distanceThisLeg = 0;
                    }
                    if (isScore) {
                        int points = eventDB.GetCourseControl(controlView.courseControlIds[0]).points;
                        if (points > 0)
                            xmlWriter.WriteElementString("ScoreOPoints", XmlConvert.ToString(points));
                    }

                    xmlWriter.WriteEndElement();         // "CourseControl"
                    break;

                case ControlPointKind.CrossingPoint:
                    // Intentionally skip crossing points.
                    break;
            }
        }
예제 #13
0
        protected override void WriteCourseControl(ControlPointKind kind, CourseView.ControlView controlView, bool isScore, ref int sequenceNumber, ref float distanceThisLeg)
        {
            xmlWriter.WriteStartElement("CourseControl");

            switch (kind) {
                case ControlPointKind.Start:
                    xmlWriter.WriteAttributeString("type", "Start");
                    break;

                case ControlPointKind.Finish:
                    // UNDONE: special instruction for taped route
                    xmlWriter.WriteAttributeString("type", "Finish");
                    if (eventDB.GetControl(controlView.controlId).symbolIds?[0] == "14.1")
                        xmlWriter.WriteAttributeString("specialInstruction", "TapedRoute");
                    else if(eventDB.GetControl(controlView.controlId).symbolIds?[0] == "14.2")
                        xmlWriter.WriteAttributeString("specialInstruction", "FunnelTapedRoute");

                    break;

                case ControlPointKind.MapExchange:
                    xmlWriter.WriteAttributeString("type", "Start");
                    break;

                case ControlPointKind.Normal:
                    xmlWriter.WriteAttributeString("type", "Control");
                    break;

                case ControlPointKind.CrossingPoint:
                    xmlWriter.WriteAttributeString("type", "CrossingPoint");
                    if (eventDB.GetControl(controlView.controlId).symbolIds?[0] == "13.4")
                        xmlWriter.WriteAttributeString("specialInstruction", "MandatoryOutOfBoundsAreaPassage");
                    else
                        xmlWriter.WriteAttributeString("specialInstruction", "MandatoryCrossingPoint");
                    break;
            }

            xmlWriter.WriteElementString("Control", controlCodeMap[controlView.controlId]);

            if (!isScore && controlView.ordinal > 0) {
                xmlWriter.WriteElementString("MapText", XmlConvert.ToString(controlView.ordinal));
            }
            if (!isScore && kind != ControlPointKind.Start) {
                xmlWriter.WriteElementString("LegLength", XmlConvert.ToString(Math.Round(distanceThisLeg)));
                distanceThisLeg = 0;
            }
            if (isScore && kind == ControlPointKind.Normal) {
                int points = eventDB.GetCourseControl(controlView.courseControlIds[0]).points;
                if (points > 0)
                    xmlWriter.WriteElementString("Score", XmlConvert.ToString(points));
            }

            xmlWriter.WriteEndElement();  // "CourseControl"
        }
예제 #14
0
 protected abstract void WriteCourseStart(CourseView courseView, string courseName, int courseNumber, string[] classNames, bool isScore, int variationNumber, VariationInfo variationInfo);
예제 #15
0
 protected abstract void WriteCourseControl(ControlPointKind kind, CourseView.ControlView controlView, bool isScore, ref int sequenceNumber, ref float distanceThisLeg);
예제 #16
0
        // Get a regular 8-box line for a start or regular control.
        private DescriptionLine GetRegularLine(CourseView.CourseViewKind kind, int scoreColumn, CourseView.ControlView controlView, Dictionary<string, string> descriptionKey)
        {
            Event ev = eventDB.GetEvent();
            ControlPoint control = eventDB.GetControl(controlView.controlId);
            CourseControl courseControl;

            if (controlView.courseControlIds[0].IsNone)
                courseControl = null;
            else
                courseControl = eventDB.GetCourseControl(controlView.courseControlIds[0]);

            Debug.Assert(control.kind == ControlPointKind.Normal || control.kind == ControlPointKind.Start || control.kind == ControlPointKind.MapExchange);

            DescriptionLine line = new DescriptionLine();
            line.kind = DescriptionLineKind.Normal;
            line.boxes = new object[8];

            // Box A: ordinal or start triangle or points.
            if (control.kind == ControlPointKind.Start || control.kind == ControlPointKind.MapExchange)
                line.boxes[0] = symbolDB["start"];
            else if (kind != CourseView.CourseViewKind.AllControls && controlView.ordinal > 0)
                line.boxes[0] = Convert.ToString(controlView.ordinal);
            else
                line.boxes[0] = null;

            // Box B: code of the control
            if (control.kind == ControlPointKind.Normal)
                line.boxes[1] = Convert.ToString(control.code);

            // Boxes C-H, from the symbols
            for (int i = 2; i < 8; ++i) {
                String symbolID = control.symbolIds[i - 2];
                if (symbolID != null) {
                    line.boxes[i] = symbolDB[control.symbolIds[i - 2]];

                    // See if we need to add this to the key.
                    bool addToKey;
                    if (ev.customSymbolKey.TryGetValue(symbolID, out addToKey) && addToKey && Symbol.ContainsLanguage(ev.customSymbolText[symbolID], language)) {
                        descriptionKey[symbolID] = Symbol.GetBestSymbolText(symbolDB, ev.customSymbolText[symbolID], language, false, "", "");
                    }
                }
            }

            // Box F -- may be text instead of a symbol.
            if (control.columnFText != null) {
                Debug.Assert(line.boxes[5] == null);
                line.boxes[5] = control.columnFText;
            }

            // Put points in the score column, for a score course.
            if (control.kind == ControlPointKind.Normal && scoreColumn >= 0 && courseControl != null) {
                int points = courseControl.points;
                if (points > 0)
                    line.boxes[scoreColumn] = Convert.ToString(courseControl.points);
                else
                    line.boxes[scoreColumn] = null;
            }

            // Get the text version of the control using the Textifier.
            Textifier textifier = new Textifier(eventDB, symbolDB, language);
            line.textual = textifier.CreateTextForControl(controlView.controlId, "");

            // The course control ID, for use in coordinating the selection
            line.controlId = controlView.controlId;
            line.courseControlId = controlView.courseControlIds[0];

            return line;
        }
예제 #17
0
        private void CreateObjectsForControlView(CourseView.ControlView controlView, ControlPosition controlPosition)
        {
            CreateControlNumber(controlView, controlPosition);

            if (controlView.legTo != null) {
                for (int i = 0; i < controlView.legTo.Length; ++i) {
                    ForkPosition forkStart;
                    if (controlPosition.forkStart != null)
                        forkStart = controlPosition.forkStart[i];
                    else
                        forkStart = null;

                    CreateLegBetweenControls(controlView, controlPosition, controlViewsAllVariationsAndParts[controlView.legTo[i]], controlPositions[controlView.legTo[i]], i, forkStart);
                }
            }
        }
예제 #18
0
        private void WriteLegLengthTable(EventDB eventDB, CourseView courseView)
        {
            BeginTable("", 3, "leftalign", "leftalign", "rightalign");
            WriteTableHeaderRow(ReportText.ColumnHeader_Leg, ReportText.ColumnHeader_Controls, ReportText.ColumnHeader_Length);

            // Go through the control views.
            int controlViewIndex = 0;
            float distanceThisLeg = 0;
            float totalLegs = 0;
            int legNumber = 1;
            Id<ControlPoint> controlIdPrev = Id<ControlPoint>.None;

            while (controlViewIndex >= 0 && controlViewIndex < courseView.ControlViews.Count) {
                CourseView.ControlView controlView = courseView.ControlViews[controlViewIndex];
                ControlPointKind kind = eventDB.GetControl(controlView.controlId).kind;

                // Don't report crossing points.
                if (kind != ControlPointKind.CrossingPoint) {
                    if (controlIdPrev.IsNotNone) {
                        string legText = string.Format("{0}\u2013{1}", Util.ControlPointName(eventDB, controlIdPrev, NameStyle.Medium), Util.ControlPointName(eventDB, controlView.controlId, NameStyle.Medium));
                        WriteTableRow(Convert.ToString(legNumber), legText, string.Format("{0} m", Math.Round(distanceThisLeg)));
                        totalLegs += distanceThisLeg;
                        legNumber += 1;
                    }

                    controlIdPrev = controlView.controlId;
                    distanceThisLeg = 0;
                }

                if (controlView.legLength != null)
                    distanceThisLeg += controlView.legLength[0];

                controlViewIndex = courseView.GetNextControl(controlViewIndex);
            }

            // Write average row
            if (legNumber > 1) {
                BeginTableRow("summaryrow");
                WriteSpannedTableCell(2, ReportText.LegLength_Average);
                WriteTableCell(string.Format("{0} m", Convert.ToString(Math.Round(totalLegs / (float) (legNumber - 1)))));
                EndTableRow();
            }

            EndTable();
        }
예제 #19
0
        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;
        }
예제 #20
0
        // Describe a course.
        private static TextPart[] DescribeCourse(EventDB eventDB, CourseView activeCourseView)
        {
            List<TextPart> list = new List<TextPart>();
            CourseDesignator courseDesignator = activeCourseView.CourseDesignator;

            // Course name
            if (courseDesignator.AllParts) {
                list.Add(new TextPart(TextFormat.Title, string.Format(SelectionDescriptionText.CourseName, activeCourseView.CourseName)));
            }
            else {
                list.Add(new TextPart(TextFormat.Title, string.Format(SelectionDescriptionText.CourseNameAndPart, activeCourseView.CourseName, courseDesignator.Part + 1)));
            }

            if (activeCourseView.Kind == CourseView.CourseViewKind.Normal || activeCourseView.Kind == CourseView.CourseViewKind.AllVariations) {
                // Course length
                if (!courseDesignator.AllParts) {
                    list.Add(new TextPart(TextFormat.Header, SelectionDescriptionText.Length));
                    list.Add(new TextPart(TextFormat.SameLine,
                        Util.GetLengthInKm(activeCourseView.PartLength, activeCourseView.PartLength, 2)));
                }
                else {
                    list.Add(new TextPart(TextFormat.Header, SelectionDescriptionText.Length));
                    list.Add(new TextPart(TextFormat.SameLine,
                        Util.GetLengthInKm(activeCourseView.MinTotalLength, activeCourseView.MaxTotalLength, 2)));

                    // If the user specified a length, show both that length and the calculated length.
                    if (activeCourseView.MinTotalLength != activeCourseView.MinMeasuredLength || activeCourseView.MaxTotalLength != activeCourseView.MaxMeasuredLength) {
                        list.Add(new TextPart(TextFormat.Header, SelectionDescriptionText.CalculatedLength));
                        list.Add(new TextPart(TextFormat.SameLine,
                            Util.GetLengthInKm(activeCourseView.MinMeasuredLength, activeCourseView.MaxMeasuredLength, 2)));
                    }
                }

                // Don't have climb for a single part of multi-part course.
                if (activeCourseView.TotalClimb >= 0 && courseDesignator.AllParts) {
                    list.Add(new TextPart(TextFormat.Header, SelectionDescriptionText.Climb + "  "));
                    list.Add(new TextPart(TextFormat.SameLine,
                        string.Format("{0:#,###} m", Math.Round(activeCourseView.TotalClimb, MidpointRounding.AwayFromZero))));
                }
            }
            else if (activeCourseView.Kind == CourseView.CourseViewKind.Score) {
                // Total controls
                list.Add(new TextPart(TextFormat.Header, SelectionDescriptionText.TotalControls + "  "));
                list.Add(new TextPart(TextFormat.SameLine,
                    string.Format("{0}", activeCourseView.TotalNormalControls)));

                if (activeCourseView.TotalScore > 0) {
                    list.Add(new TextPart(TextFormat.Header, SelectionDescriptionText.TotalScore + "  "));
                    list.Add(new TextPart(TextFormat.SameLine,
                        string.Format("{0}", activeCourseView.TotalScore)));
                }
            }

            // What is the competitor load?
            int load = QueryEvent.GetCourseLoad(eventDB, activeCourseView.BaseCourseId);
            if (load >= 0) {
                list.Add(new TextPart(TextFormat.Header, SelectionDescriptionText.CompetitorLoad));
                list.Add(new TextPart(TextFormat.SameLine, string.Format("{0}", load)));
            }

            return list.ToArray();
        }
예제 #21
0
 // Describe the selection, and return an array of TextParts for display in the UI.
 public static TextPart[] DescribeSelection(SymbolDB symbolDB, EventDB eventDB, CourseView activeCourseView, SelectionMgr.SelectionInfo selection)
 {
     if (selection.SelectionKind == SelectionMgr.SelectionKind.Key) {
         return DescribeKey(eventDB);
     }
     else if (selection.SelectionKind == SelectionMgr.SelectionKind.TextLine) {
         return DescribeTextLine(eventDB, selection.SelectedControl, selection.SelectedTextLineKind);
     }
     else if (selection.SelectionKind == SelectionMgr.SelectionKind.Control) {
         return DescribeControlPoint(symbolDB, eventDB, selection.SelectedControl, DescKind.DescPane);
     }
     else if (selection.SelectionKind == SelectionMgr.SelectionKind.Leg) {
         return DescribeLeg(eventDB, selection.SelectedCourseControl, selection.SelectedCourseControl2, DescKind.DescPane);
     }
     else if (selection.SelectionKind == SelectionMgr.SelectionKind.Special) {
         return DescribeSpecial(eventDB, selection.SelectedSpecial, activeCourseView.ScaleRatio, DescKind.DescPane);
     }
     else if (selection.SelectionKind == SelectionMgr.SelectionKind.MapExchangeAtControl) {
         return DescribeMapExchangeAtControl(eventDB, selection.SelectedControl);
     }
     else if (selection.ActiveCourseDesignator.IsNotAllControls) {
         return DescribeCourse(eventDB, activeCourseView);
     }
     else {
         return DescribeAllControls(eventDB, activeCourseView);
     }
 }
예제 #22
0
        // Create an filtered All Controls view -- show controls from the control collection, but only includes some.
        // excludedCourses contains an array of course ids to excluded from the contgrols.
        // kindFilter, if non-null, limits the controls to this kind of controls.
        public static CourseView CreateFilteredAllControlsView(EventDB eventDB, CourseDesignator[] excludedCourses, ControlPointKind kindFilter, bool addSpecials, bool addDescription)
        {
            CourseView courseView = new CourseView(eventDB, CourseDesignator.AllControls);

            courseView.courseName = MiscText.AllControls;
            courseView.scoreColumn = -1;

            // Add every control to the course view, subject to the filters.
            foreach (Id<ControlPoint> controlId in eventDB.AllControlPointIds) {
                ControlPoint control = eventDB.GetControl(controlId);

                // Check if the control is filtered out.

                if (excludedCourses != null) {
                    // Filter excluded courses.
                    foreach (CourseDesignator excludedCourseDesignator in excludedCourses) {
                        if (QueryEvent.CourseUsesControl(eventDB, excludedCourseDesignator, controlId))
                            goto SKIP;
                    }
                }

                if (kindFilter != ControlPointKind.None) {
                    // Filter on control type.
                    if (control.kind != kindFilter)
                        goto SKIP;
                }

                // We are going to include this control in the collection.

                ControlView controlView = new ControlView();

                controlView.courseControlIds = new[] { Id<CourseControl>.None };
                controlView.controlId = controlId;

                // All controls doesn't have ordinals.
                controlView.ordinal = -1;

                controlView.joinIndex = -1;

                courseView.controlViews.Add(controlView);

               SKIP:        ;
            }

            // Sort the control views: first by kind, then by code.
            courseView.controlViews.Sort((view1, view2) => QueryEvent.CompareControlIds(eventDB, view1.controlId, view2.controlId));

            courseView.Finish();

            if (addSpecials) {
                // Add every special, regardless of courses it is on, except for descriptions. Descriptions are added to all
                // controls only if they appear in all courses (or specifically for the all controls view), and if "addDescription" is true
                foreach (Id<Special> specialId in eventDB.AllSpecialIds) {
                    Special special = eventDB.GetSpecial(specialId);
                    if (special.kind == SpecialKind.Descriptions) {
                        if (addDescription && QueryEvent.CourseContainsSpecial(eventDB, CourseDesignator.AllControls, specialId))
                            courseView.descriptionViews.Add(new DescriptionView(specialId, CourseDesignator.AllControls));
                    }
                    else
                        courseView.specialIds.Add(specialId);
                }
            }

            return courseView;
        }
예제 #23
0
        // Describe the all controls.
        private static TextPart[] DescribeAllControls(EventDB eventDB, CourseView activeCourseView)
        {
            List<TextPart> list = new List<TextPart>();

            // Course name
            list.Add(new TextPart(TextFormat.Title, activeCourseView.CourseName));

            // Count controls in use.
            list.Add(new TextPart(TextFormat.Header, SelectionDescriptionText.ControlsInUse));
            list.Add(new TextPart(TextFormat.NewLine, CountControls(activeCourseView, delegate(Id<ControlPoint> controlId) {
                return QueryEvent.CoursesUsingControl(eventDB, controlId).Length > 0;
            })));

            // Count controls not in use.
            list.Add(new TextPart(TextFormat.Header, SelectionDescriptionText.ControlsNotInUse));
            list.Add(new TextPart(TextFormat.NewLine, CountControls(activeCourseView, delegate(Id<ControlPoint> controlId) {
                return QueryEvent.CoursesUsingControl(eventDB, controlId).Length == 0;
            })));

            return list.ToArray();
        }
예제 #24
0
        // Get a directive line for a flagged route to map exchange (not to the finish). The distance between the controls is calculated and used for the distance
        // in the direction.
        private DescriptionLine GetMapExchangeLine(CourseView.ControlView controlViewFrom, CourseView.ControlView controlViewTo)
        {
            DescriptionLine line = new DescriptionLine();
            line.kind = DescriptionLineKind.Directive;
            line.boxes = new object[2];

            // Figure out the distance in the directive, rounded to nearest 10m.
            float distance;       // default distance is zero.
            string distanceText;
            distance = controlViewFrom.legLength[0];
            distance = (float) (Math.Round(distance / 10.0) * 10.0);      // round to nearest 10 m.
            distanceText = string.Format("{0} m", distance);

            // Box 1: directive graphics.
            string symbolId = "13.5";
            line.boxes[0] = symbolDB[symbolId];

            // Box 2: distance of the flagging
            line.boxes[1] = distanceText;

            // Get the text version of the control using the Textifier.
            Textifier textifier = new Textifier(eventDB, symbolDB, language);
            line.textual = textifier.CreateTextForDirective(symbolId, distanceText);

            // The course control IDs, for use in coordinating the selection
            line.isLeg = true;
            line.controlId = controlViewFrom.controlId;
            line.courseControlId = controlViewFrom.courseControlIds[0];
            line.courseControlId2 = controlViewTo.courseControlIds[0];

            return line;
        }
예제 #25
0
        // Create the standard view onto a regular course, or a single variation of a variation course.
        private static CourseView CreateStandardCourseView(EventDB eventDB, CourseDesignator courseDesignator)
        {
            Course course = eventDB.GetCourse(courseDesignator.CourseId);

            if (QueryEvent.HasVariations(eventDB, courseDesignator.CourseId) && courseDesignator.VariationInfo == null)
                throw new ApplicationException("Cannot create course view without specifying which variation");

            // Get sub-part of the course. firstCourseControls is the first control to process, lastCourseControl is the last one to
            // process, or None if process to the end of the course.
            Id<CourseControl> firstCourseControl, lastCourseControl;
            if (courseDesignator.AllParts) {
                firstCourseControl = course.firstCourseControl;
                lastCourseControl = Id<CourseControl>.None;
            }
            else {
                QueryEvent.GetCoursePartBounds(eventDB, courseDesignator, out firstCourseControl, out lastCourseControl);
            }

            CourseView courseView = new CourseView(eventDB, courseDesignator);
            int ordinal;

            courseView.courseName = course.name;
            courseView.scoreColumn = -1;

            ordinal = 1;
            ordinal = course.firstControlOrdinal;

            // To get the ordinals correct, we get the course control ids for all parts.
            List<Id<CourseControl>> courseControls = QueryEvent.EnumCourseControlIds(eventDB, courseDesignator.WithAllParts()).ToList();
            int index = 0;

            // Increase the ordinal value for each normal control before the first one we're considering.
            while (index < courseControls.Count && courseControls[index] != firstCourseControl) {
                CourseControl courseControl = eventDB.GetCourseControl(courseControls[index]);
                ControlPoint control = eventDB.GetControl(courseControl.control);
                if (control.kind == ControlPointKind.Normal)
                    ++ordinal;
                ++index;
            }

            for (; index < courseControls.Count; ++index) {
                Id<CourseControl> courseControlId = courseControls[index];

                ControlView controlView = new ControlView();
                CourseControl courseControl = eventDB.GetCourseControl(courseControlId);
                ControlPoint control = eventDB.GetControl(courseControl.control);

                controlView.courseControlIds = new[] { courseControlId };
                controlView.controlId = courseControl.control;

                // Set the ordinal number.
                if (control.kind == ControlPointKind.Normal)
                    controlView.ordinal = ordinal++;
                else if (control.kind == ControlPointKind.Start || control.kind == ControlPointKind.MapExchange)
                    controlView.ordinal = 0;
                else
                    controlView.ordinal = -1;

                controlView.joinIndex = -1;

                // Don't show the map exchange for the next part at the end of this part.
                if (courseControlId == lastCourseControl && !courseDesignator.AllParts && control.kind == ControlPointKind.MapExchange) {
                    controlView.hiddenControl = true;
                }

                // Set the legTo array with the next courseControlID. This is later updated
                // to the indices.
                if (index < courseControls.Count - 1 && courseControlId != lastCourseControl) {
                    Id<CourseControl> nextCourseControl = courseControls[index + 1];
                    controlView.legTo = new int[1] { nextCourseControl.id };   // legTo initially holds course control ids, later changed.
                }
                // Add the controlview.
                courseView.controlViews.Add(controlView);

                if (courseControlId == lastCourseControl)
                    break;
            }

            // If this is a part that should also have the finish on it, and it isn't the last part, then
            // add the finish.
            if (courseDesignator.IsNotAllControls && !courseDesignator.AllParts &&
                courseDesignator.Part != QueryEvent.CountCourseParts(eventDB, courseDesignator.CourseId) - 1 &&
                QueryEvent.GetPartOptions(eventDB, courseDesignator).ShowFinish)
            {
                if (QueryEvent.HasFinishControl(eventDB, courseDesignator.CourseId))
                    courseView.extraCourseControls.Add(QueryEvent.LastCourseControl(eventDB, courseDesignator.CourseId, false));
            }

            courseView.Finish();
            return courseView;
        }
예제 #26
0
        CourseLayout CreateCourseLayout(CourseView courseView)
        {
            // Create the CourseLayout.
            CourseLayout courseLayout = new CourseLayout();
            courseLayout.SetLayerColor(CourseLayer.Descriptions, NormalCourseAppearance.blackColorOcadId, NormalCourseAppearance.blackColorName, NormalCourseAppearance.blackColorC, NormalCourseAppearance.blackColorM, NormalCourseAppearance.blackColorY, NormalCourseAppearance.blackColorK, false);
            courseLayout.SetLayerColor(CourseLayer.MainCourse, NormalCourseAppearance.courseOcadId, NormalCourseAppearance.courseColorName,
                creationSettings.cyan, creationSettings.magenta, creationSettings.yellow, creationSettings.black, creationSettings.purpleOverprint);
            CourseFormatter.FormatCourseToLayout(symbolDB, courseView, courseAppearance, courseLayout, CourseLayer.MainCourse);

            return courseLayout;
        }
예제 #27
0
        // Create the view of all variations of a course with variations. Cannot be a single part of a multi-part course.
        // Does not contain ordinals.
        private static CourseView CreateAllVariationsCourseView(EventDB eventDB, CourseDesignator courseDesignator)
        {
            Course course = eventDB.GetCourse(courseDesignator.CourseId);

            if (!courseDesignator.AllParts)
                throw new ApplicationException("Cannot create all variations of a single part");

            CourseView courseView = new CourseView(eventDB, courseDesignator);

            courseView.courseName = course.name;
            courseView.scoreColumn = -1;

            // To get the ordinals correct, we get the course control ids for all parts.
            List<Id<CourseControl>> courseControls = QueryEvent.EnumCourseControlIds(eventDB, courseDesignator).ToList();

            for (int index = 0; index < courseControls.Count; ++index) {
                Id<CourseControl> courseControlId = courseControls[index];
                CourseControl courseControl = eventDB.GetCourseControl(courseControlId);

                // We add each split control only once, even though it has multiple variations. Check to see if we have already
                // handled it.
                bool alreadyHandled = false;
                if (courseControl.split) {
                    foreach (ControlView cv in courseView.controlViews) {
                        if (cv.courseControlIds.Contains(courseControlId))
                            alreadyHandled = true;
                    }
                }

                if (!alreadyHandled) {
                    ControlView controlView = new ControlView();

                    controlView.controlId = courseControl.control;

                    // Set the ordinal number. All variations does not include an ordinal.
                    controlView.ordinal = -1;

                    // Set all course control ids associated with split control, or a single one for a non-split control.
                    // Set the legTo array with the next courseControlID(s). This is later updated
                    // to the indices.
                    if (courseControl.split) {
                        controlView.courseControlIds = QueryEvent.AllVariationsOfCourseControl(eventDB, courseControlId).ToArray();
                        if (courseControl.nextCourseControl.IsNotNone) {
                            controlView.legTo = new int[controlView.courseControlIds.Length];
                            for (int i = 0; i < controlView.legTo.Length; ++i) {
                                controlView.legTo[i] = eventDB.GetCourseControl(controlView.courseControlIds[i]).nextCourseControl.id;
                            }
                        }
                        if (courseControl.loop)
                            controlView.joinIndex = courseControlId.id;
                        else
                            controlView.joinIndex = courseControl.splitEnd.id;
                    }
                    else {
                        controlView.courseControlIds = new[] { courseControlId };
                        if (courseControl.nextCourseControl.IsNotNone)
                            controlView.legTo = new int[1] { courseControl.nextCourseControl.id };   // legTo initially holds course control ids, later changed.
                        controlView.joinIndex = -1;
                    }

                    // Add the controlview.
                    courseView.controlViews.Add(controlView);
                }
            }

            courseView.Finish();
            return courseView;
        }
예제 #28
0
        // Write a map to the given file name.
        void ExportMap(CourseView courseView, string outputFilename)
        {
            // Create the CourseLayout.
            CourseLayout courseLayout = CreateCourseLayout(courseView);

            // Create the map and write it out.
            Map map = courseLayout.RenderToMap();
            using (map.Write()) {
                map.MapScale = courseView.MapScale;
                map.PrintScale = courseView.PrintScale;
                map.PrintArea = controller.GetCurrentPrintAreaRectangle(courseView.CourseDesignator);

                switch (controller.MapType) {
                    case MapType.OCAD:
                        // Set OCAD map as template.
                        // OCAD 6 doesn't support another OCAD file as a template.
                        if (! (creationSettings.fileFormat.kind == MapFileFormatKind.OCAD && creationSettings.fileFormat.version <= 6))
                            AddTemplateToMap(map, new TemplateInfo(controller.MapFileName, new PointF(0, 0), 0, 0, true));

                        // Use same real world coordinates as underlying map (nicer, but also works around bug in OCAD 11
                        // where background maps with real world coordinates aren't displayed if the map map doesn't have same real
                        // world coordinates).
                        map.RealWorldCoords = controller.MapRealWorldCoords;
                        break;

                    case MapType.Bitmap:
                    case MapType.PDF:
                        // Set bitmap as template.
                        PointF centerPoint = Geometry.RectCenter(controller.MapDisplay.MapBounds);

                        ImageFormat imageFormat;
                        string mapFileName;
                        float dpi;
                        if (CreateBitmapFile()) {
                            // Write a copy of the bitmap map.
                            mapFileName = CreateBitmapFileName(out imageFormat);
                            controller.MapDisplay.WriteBitmapMap(mapFileName, imageFormat, out dpi);
                        }
                        else {
                            // Use existing map file.
                            mapFileName = controller.MapFileName;
                            dpi = controller.MapDpi;
                        }

                        AddTemplateToMap(map, new TemplateInfo(mapFileName, centerPoint, dpi, 0, true));
                        break;

                    case MapType.None:
                        break;

                    default:
                        Debug.Fail("Unexpected map type");
                        break;
                }
            }

            WriteImageBitmaps(map);

            InputOutput.WriteFile(outputFilename, map, creationSettings.fileFormat);
        }
예제 #29
0
        // Update the names of all course views, and get the active course view, based on the active course id.
        void UpdateCourseViews()
        {
            List<KeyValuePair<Id<Course>,string>> courseViewPairs = new List<KeyValuePair<Id<Course>,string>>(); // Holds the list of course views and names, for sorting. Does NOT include all controls.

            // Get all the pairs of course ids in sorted order.
            Id<Course>[] courseIds = QueryEvent.SortedCourseIds(eventDB);

            // Copy to the names and ids arrays, adding in All Controls as the first element.
            courseViewNames = new string[courseIds.Length + 1];
            courseViewIds = new Id<Course>[courseIds.Length + 1];
            courseViewNames[0] = MiscText.AllControls;
            courseViewIds[0] = Id<Course>.None;
            for (int i = 1; i < courseViewIds.Length; ++i) {
                courseViewNames[i] = eventDB.GetCourse(courseIds[i - 1]).name;
                courseViewIds[i] = courseIds[i-1];
            }

            // Figure out which course view is the active one. We have already validate that the active course id
            // is present.
            if (activeCourseDesignator.IsAllControls) {
                activeCourseViewIndex = 0;
                activeCourseView = CourseView.CreateViewingCourseView(eventDB, CourseDesignator.AllControls);
            }
            else {
                for (int i = 1; i < courseViewIds.Length; ++i) {
                    if (courseViewIds[i] == activeCourseDesignator.CourseId) {
                        activeCourseViewIndex = i;
                        activeCourseView = CourseView.CreateViewingCourseView(eventDB, activeCourseDesignator);
                    }
                }
            }

            // Get/create the topology course view. Not supported (null) for score and all controls. Always shows
            // all variations for a course with variations.
            if (activeCourseView.Kind == CourseView.CourseViewKind.Normal) {
                if (QueryEvent.HasVariations(activeCourseView.EventDB, activeCourseView.BaseCourseId) || !activeCourseView.CourseDesignator.AllParts) {
                    topologyCourseView = CourseView.CreateViewingCourseView(eventDB, new CourseDesignator(activeCourseView.BaseCourseId));
                }
                else {
                    topologyCourseView = activeCourseView;
                }
            }
            else if (activeCourseView.Kind == CourseView.CourseViewKind.AllVariations) {
                topologyCourseView = activeCourseView;
            }
            else {
                topologyCourseView = null;
            }
        }
예제 #30
0
        // Is the given control view in the specific variation.
        private bool ControlViewInSpecificVariation(CourseView.ControlView controlView)
        {
            if (controlViewsSpecificVariation == controlViewsAllVariationsAndParts)
                return true;

            foreach (Id<CourseControl> idCourseControl in controlView.courseControlIds) {
                if (courseControlIdsSpecificVariation.Contains(idCourseControl))
                    return true;
            }

            return false;
        }