public GroupMetrics(string ID_Type, Metrics metrics) : base() { MetricsList.Add(metrics); _objectType = ID_Type; _originX = metrics.OriginX; _originY = metrics.OriginY; _top = metrics.Top; _right = metrics.Right; _bottom = metrics.Bottom; _left = metrics.Left; }
/// <summary> /// If isBelow, metrics.Top is moved to bottomBoundary + bottomPadding and bottomBoundary is then set to metrics.Bottom /// otherwise metrics.Bottom is moved to topBoundary - topPadding ans topBoundary is then set to metrics.Top. /// Does nothing if metrics is null. /// </summary> private void MoveMetrics(Metrics metrics, bool isBelow, ref float topBoundary, float topPadding, ref float bottomBoundary, float bottomPadding) { Debug.Assert(NoteheadExtendersMetrics == null); if(isBelow) { MoveBelowBottomBoundary(metrics, ref bottomBoundary, bottomPadding); } else { MoveAboveTopBoundary(metrics, ref topBoundary, topPadding); } SetExternalBoundary(); }
private void MoveBelowBottomBoundary(Metrics metrics, ref float bottomBoundary, float padding) { Debug.Assert(padding >= 0.0F); float newTop = bottomBoundary + padding; metrics.Move(0F, newTop - metrics.Top); bottomBoundary = metrics.Bottom; SetExternalBoundary(); }
private void MoveAboveTopBoundary(Metrics metrics, ref float topBoundary, float padding) { Debug.Assert(padding >= 0.0F); float newBottom = topBoundary - padding; metrics.Move(0F, newBottom - metrics.Bottom); topBoundary = metrics.Top; SetExternalBoundary(); }
/// <summary> /// Returns the stem which the otherChordTopDownHeadsMetrics would have if their duration class was crotchet. /// DummyStemMetrics are used when aligning synchronous chords. /// </summary> private StemMetrics DummyStemMetrics( List<HeadMetrics> otherChordTopDownHeadsMetrics, VerticalDir stemDirection, float fontHeight, Metrics flagsBlockMetrics, BeamBlock beamBlock, float strokeWidth) { List<HeadMetrics> tempTopDownHeadsMetrics = new List<HeadMetrics>(); foreach(HeadMetrics headMetrics in otherChordTopDownHeadsMetrics) { HeadMetrics newHeadMetrics = new HeadMetrics(headMetrics, DurationClass.crotchet); tempTopDownHeadsMetrics.Add(newHeadMetrics); } return NewStemMetrics(tempTopDownHeadsMetrics, stemDirection, fontHeight, flagsBlockMetrics, beamBlock, strokeWidth); }
/// <summary> /// Writes a time signature to the SVG file. /// The metrics have been set in SvgSystem.Justify() /// </summary> public void WriteSVG(SvgWriter w, string timeSigSignature, double originX, double originY) { Metrics.WriteSVG(w); }
/// <summary> /// Returns the (positive) amount by which to raise the metrics argument /// so that its bottom is level with the top of a chord component which /// it overlaps. /// The argument metrics is padded on all sides using the padding argument. /// </summary> public new float OverlapHeight(Metrics metrics, float padding) { float maxOverlapHeight = float.MinValue; float overlap = 0; #region _stemMetrics if(_stemMetrics != null) { overlap = _stemMetrics.OverlapHeight(metrics, padding); if(overlap != 0F) maxOverlapHeight = maxOverlapHeight > overlap ? maxOverlapHeight : overlap; } #endregion #region _flagsBlockMetrics if(_flagsBlockMetrics != null) { overlap = _flagsBlockMetrics.OverlapHeight(metrics, padding); if(overlap != 0F) maxOverlapHeight = maxOverlapHeight > overlap ? maxOverlapHeight : overlap; } #endregion #region _topDownHeadsMetrics if(_headsMetricsTopDown != null) { foreach(HeadMetrics headMetric in _headsMetricsTopDown) { overlap = headMetric.OverlapHeight(metrics, padding); if(overlap != 0F) maxOverlapHeight = maxOverlapHeight > overlap ? maxOverlapHeight : overlap; } } #endregion #region _accidentalsMetrics if(_topDownAccidentalsMetrics != null) { foreach(AccidentalMetrics accidentalMetric in _topDownAccidentalsMetrics) { overlap = accidentalMetric.OverlapHeight(metrics, padding); if(overlap != 0F) maxOverlapHeight = maxOverlapHeight > overlap ? maxOverlapHeight : overlap; } } #endregion #region _ledgerlineBlocksMetrics if(_upperLedgerlineBlockMetrics != null) { overlap = _upperLedgerlineBlockMetrics.OverlapHeight(metrics, padding); if(overlap != 0F) maxOverlapHeight = maxOverlapHeight > overlap ? maxOverlapHeight : overlap; } if(_lowerLedgerlineBlockMetrics != null) { overlap = _lowerLedgerlineBlockMetrics.OverlapHeight(metrics, padding); if(overlap != 0F) maxOverlapHeight = maxOverlapHeight > overlap ? maxOverlapHeight : overlap; } #endregion #region if(_cautionaryBracketsMetrics != null) { foreach(CautionaryBracketMetrics cautionaryBracketMetrics in _cautionaryBracketsMetrics) { overlap = cautionaryBracketMetrics.OverlapHeight(metrics, padding); if(overlap != 0F) maxOverlapHeight = maxOverlapHeight > overlap ? maxOverlapHeight : overlap; } } #endregion #region _ornamentMetrics if(_ornamentMetrics != null) { overlap = _ornamentMetrics.OverlapHeight(metrics, padding); if(overlap != 0F) maxOverlapHeight = maxOverlapHeight > overlap ? maxOverlapHeight : overlap; } #endregion #region _lyricMetrics if(_lyricMetrics != null) { overlap = _lyricMetrics.OverlapHeight(metrics, padding); if(overlap != 0F) maxOverlapHeight = maxOverlapHeight > overlap ? maxOverlapHeight : overlap; } #endregion #region _dynamicMetrics if(_dynamicMetrics != null) { overlap = _dynamicMetrics.OverlapHeight(metrics, padding); if(overlap != 0F) maxOverlapHeight = maxOverlapHeight > overlap ? maxOverlapHeight : overlap; } #endregion #region NoteheadExtendersMetricsBefore if(NoteheadExtendersMetricsBefore != null) { foreach(NoteheadExtenderMetrics nem in NoteheadExtendersMetricsBefore) { overlap = nem.OverlapHeight(metrics, padding); if(overlap != 0F) maxOverlapHeight = maxOverlapHeight > overlap ? maxOverlapHeight : overlap; } } #endregion return maxOverlapHeight; }
private void SetBoundary(Metrics metrics) { _top = _top < metrics.Top ? _top : metrics.Top; _right = _right > metrics.Right ? _right : metrics.Right; _bottom = _bottom > metrics.Bottom ? _bottom : metrics.Bottom; _left = _left < metrics.Left ? _left : metrics.Left; }
/// <summary> /// Use this function to check all atomic overlaps (single character or line boxMetrics). /// </summary> /// <param name="metrics"></param> /// <returns></returns> public bool Overlaps(Metrics metrics) { bool verticalOverlap = true; bool horizontalOverlap = true; if((metrics.Top > Bottom) || (metrics.Bottom < Top)) verticalOverlap = false; if((metrics.Left > Right) || (metrics.Right < Left)) horizontalOverlap = false; return verticalOverlap && horizontalOverlap; }
/// <summary> /// If the padded argument overlaps this metrics horizontally, /// and this.Top is smaller than paddedArgument.Bottom, /// this.Bottom - paddedArgument.Right is returned. /// Otherwise 0F is returned. /// </summary> /// <param name="arg"></param> /// <returns></returns> public float OverlapHeight(Metrics arg, float padding) { float newArgRight = arg.Right + padding; float newArgLeft = arg.Left - padding; float newArgBottom = arg.Bottom + padding; bool horizontalOverlap = true; if((newArgRight < Left) || (newArgLeft > Right)) horizontalOverlap = false; if(horizontalOverlap && newArgBottom > Top) { float overlap = newArgBottom - Top; return (overlap); } else return 0F; }
/// <summary> /// Adds the metrics to the MetricsList and includes it in this object's boundary. /// The boundary is used for collision checking. All objects which should move together with this object /// must be added to the MetricsList. /// </summary> /// <param name="metrics"></param> public virtual void Add(Metrics metrics) { MetricsList.Add(metrics); ResetBoundary(); }
/// <summary> /// If the top edge of the metrics object lies above any line in this top edge, /// then this top edge is adjusted accordingly. /// </summary> public override void Add(Metrics metrics) { HLine hLine = new HLine(metrics.Left, metrics.Right, metrics.Top); AddLineToUpperEdge(hLine); }
/// <summary> /// Adds either the top or the bottom of the metrics object to this horizontal edge, /// depending on whether this is a top or bottom edge. /// </summary> /// <param name="metrics"></param> public abstract void Add(Metrics metrics);
/// <summary> /// If the bottom edge of the metrics object lies below any line in this bottom edge, /// then this bottom edge is adjusted accordingly. /// </summary> public override void Add(Metrics metrics) { HLine line = new HLine(metrics.Left, metrics.Right, metrics.Bottom); Add(line); }
/// <summary> /// Sets the stem. Pass flagsBlockMetrics=null for duration classes having no flags. /// </summary> private StemMetrics NewStemMetrics(List<HeadMetrics> topDownHeadsMetrics, ChordSymbol chord, Metrics flagsBlockMetrics, float strokeWidth) { return NewStemMetrics(topDownHeadsMetrics, chord.Stem.Direction, chord.FontHeight, flagsBlockMetrics, chord.BeamBlock, strokeWidth); }
private StemMetrics NewStemMetrics( List<HeadMetrics> topDownHeadsMetrics, VerticalDir stemDirection, float fontHeight, Metrics flagsBlockMetrics, BeamBlock beamBlock, float strokeWidth) { HeadMetrics outerNotehead = FindOuterNotehead(topDownHeadsMetrics, stemDirection); HeadMetrics innerNotehead = FindInnerNotehead(topDownHeadsMetrics, stemDirection); string noteheadID = outerNotehead.ID_Type; NoteheadStemPositions_px nspPX = CLichtFontMetrics.ClichtNoteheadStemPositionsDictPX[noteheadID]; float outerNoteheadAlignmentY = (outerNotehead.Bottom + outerNotehead.Top) / 2F; float innerNoteheadAlignmentY = (innerNotehead.Bottom + innerNotehead.Top) / 2F; float delta = _gap * 0.1F; float octave = (_gap * 3.5F) + delta; // a little more than 1 octave float sixth = (_gap * 2.5F) + delta; // a little more than 1 sixth float top = 0F; float bottom = 0F; float x = 0F; if(stemDirection == VerticalDir.up) { x = outerNotehead.RightStemX - (strokeWidth / 2); bottom = outerNoteheadAlignmentY + (nspPX.RightStemY_px * fontHeight); if(beamBlock != null) { top = beamBlock.DefaultStemTipY; } else { if(flagsBlockMetrics != null) { top = flagsBlockMetrics.Top; } else top = innerNoteheadAlignmentY - octave; if(top > (_gap * 2)) { top = (_gap * 2) - delta; } } } else // stem is down { x = outerNotehead.LeftStemX + (strokeWidth / 2); top = outerNoteheadAlignmentY + (nspPX.LeftStemY_px * fontHeight); if(beamBlock != null) { bottom = beamBlock.DefaultStemTipY; } else { if(flagsBlockMetrics != null) { bottom = flagsBlockMetrics.Bottom; } else bottom = innerNoteheadAlignmentY + octave; if(bottom < (_gap * 2)) { bottom = (_gap * 2) + delta; } } } StemMetrics stemMetrics = new StemMetrics(top, x, bottom, strokeWidth, stemDirection); return stemMetrics; }
/// <summary> /// If the previousMetrics overlaps this metrics vertically, and this.Left is les than /// than previousMetrics.Right, previousMetrics.Right - this.Left is returned. /// If there is no vertical overlap, this or the previousMetrics is a RestMetrics, and /// the metrics overlap, half the width of the rest is returned. /// Otherwise float.MinValue is returned. /// </summary> public float OverlapWidth(Metrics previousMetrics) { bool verticalOverlap = true; if(!(this is BarlineMetrics)) { if((previousMetrics.Top > Bottom) || (previousMetrics.Bottom < Top)) verticalOverlap = false; } float overlap = float.MinValue; if(verticalOverlap && previousMetrics.Right > Left) { overlap = previousMetrics.Right - this.Left; if(this._objectType == "b" || this._objectType == "n") { overlap -= ((this.Right - this.Left) * 0.15F); overlap = overlap > 0F ? overlap : 0F; } } if(!verticalOverlap && this is RestMetrics && this.OriginX <= previousMetrics.Right) overlap = previousMetrics.Right - this.OriginX; if(!verticalOverlap && previousMetrics is RestMetrics && previousMetrics.OriginX >= Left) overlap = previousMetrics.OriginX - Left; return overlap; }
/// <summary> /// The flagBlock is moved to the correct x-position wrt noteheads. /// The distance between the inner notehead's alignmentY and the closest edge of the flagsBlock is set to a little less than two spaces. /// If there is only one flag, the distance is increased to a little less than three spaces (1 octave). /// If the stem is up and the bottom of the flagBlock is too low, the flagBlock is moved up. /// If the stem is down and the top of the flagBlock is too high, the flagBlock is moved down. /// </summary> private void SetFlagsPositionReNoteheads(List<HeadMetrics> topDownHeadsMetrics, Metrics flagsBlockMetrics, VerticalDir stemDirection, float stemThickness) { Debug.Assert(flagsBlockMetrics != null); HeadMetrics outerNoteheadMetrics = FindOuterNotehead(topDownHeadsMetrics, stemDirection); HeadMetrics innerNoteheadMetrics = FindInnerNotehead(topDownHeadsMetrics, stemDirection); float innerNoteheadAlignmentY = (innerNoteheadMetrics.Bottom + innerNoteheadMetrics.Top) / 2F; float minDist = _gap * 1.8F; // constant found by experiment float deltaX = 0; float deltaY = 0; if(stemDirection == VerticalDir.up) { deltaY = minDist - (innerNoteheadAlignmentY - flagsBlockMetrics.Bottom); if(flagsBlockMetrics.ID_Type == "Right1Flag") deltaY += _gap; deltaY *= -1; if(flagsBlockMetrics.ID_Type == "Right1Flag") { if((flagsBlockMetrics.Bottom + deltaY) > (_gap * 2.5F)) { deltaY = (_gap * 2.5F) - flagsBlockMetrics.Bottom; } } else // other right flag types if((flagsBlockMetrics.Bottom + deltaY) > (_gap * 3.5F)) { deltaY = (_gap * 3.5F) - flagsBlockMetrics.Bottom; } deltaX = outerNoteheadMetrics.RightStemX - (stemThickness / 2F); } else // stem is down { deltaY = minDist - (flagsBlockMetrics.Top - innerNoteheadAlignmentY); if(flagsBlockMetrics.ID_Type == "Left1Flag") deltaY += _gap; if(flagsBlockMetrics.ID_Type == "Left1Flag") { if((flagsBlockMetrics.Top + deltaY) < (_gap * 1.5F)) { deltaY = (_gap * 1.5F) - flagsBlockMetrics.Top; } } else // other left flag types if((flagsBlockMetrics.Top + deltaY) < (_gap * 0.5F)) { deltaY = (_gap * 0.5F) - flagsBlockMetrics.Top; } deltaX = outerNoteheadMetrics.LeftStemX + (stemThickness / 2F); } flagsBlockMetrics.Move(deltaX, deltaY); // the flagsBlockMetrics is added to either this.MetricsList or this.PostJustificationMetrics in a later function. }
/// <summary> /// returns float.MinValue if no overlap is found /// </summary> /// <param name="followingMetrics"></param> /// <returns></returns> public new float OverlapWidth(Metrics followingMetrics) { float maxOverlapWidth = float.MinValue; float overlap = float.MinValue; #region _stemMetrics if(_stemMetrics != null) { overlap = followingMetrics.OverlapWidth(_stemMetrics); maxOverlapWidth = maxOverlapWidth > overlap ? maxOverlapWidth : overlap; } #endregion #region _flagsBlockMetrics if(_flagsBlockMetrics != null) { overlap = followingMetrics.OverlapWidth(_flagsBlockMetrics); maxOverlapWidth = maxOverlapWidth > overlap ? maxOverlapWidth : overlap; } #endregion #region _topDownHeadsMetrics if(_headsMetricsTopDown != null) { foreach(HeadMetrics headMetric in _headsMetricsTopDown) { overlap = followingMetrics.OverlapWidth(headMetric); maxOverlapWidth = maxOverlapWidth > overlap ? maxOverlapWidth : overlap; } } #endregion #region _accidentalsMetrics if(_topDownAccidentalsMetrics != null) { foreach(AccidentalMetrics accidentalMetric in _topDownAccidentalsMetrics) { overlap = followingMetrics.OverlapWidth(accidentalMetric); maxOverlapWidth = maxOverlapWidth > overlap ? maxOverlapWidth : overlap; } } #endregion #region ledgerlineBlocksMetrics if(_upperLedgerlineBlockMetrics != null) { overlap = followingMetrics.OverlapWidth(_upperLedgerlineBlockMetrics); maxOverlapWidth = maxOverlapWidth > overlap ? maxOverlapWidth : overlap; } if(_lowerLedgerlineBlockMetrics != null) { overlap = followingMetrics.OverlapWidth(_lowerLedgerlineBlockMetrics); maxOverlapWidth = maxOverlapWidth > overlap ? maxOverlapWidth : overlap; } #endregion #region if(_cautionaryBracketsMetrics != null) { foreach(CautionaryBracketMetrics cautionaryBracketMetrics in _cautionaryBracketsMetrics) { overlap = followingMetrics.OverlapWidth(cautionaryBracketMetrics); maxOverlapWidth = maxOverlapWidth > overlap ? maxOverlapWidth : overlap; } } #endregion #region _ornamentMetrics if(_ornamentMetrics != null) { overlap = followingMetrics.OverlapWidth(_ornamentMetrics); maxOverlapWidth = maxOverlapWidth > overlap ? maxOverlapWidth : overlap; } #endregion #region _lyricMetrics if(_lyricMetrics != null) { overlap = followingMetrics.OverlapWidth(_lyricMetrics); maxOverlapWidth = maxOverlapWidth > overlap ? maxOverlapWidth : overlap; } #endregion #region _dynamicMetrics if(_dynamicMetrics != null) { overlap = followingMetrics.OverlapWidth(_dynamicMetrics); maxOverlapWidth = maxOverlapWidth > overlap ? maxOverlapWidth : overlap; } #endregion #region NoteheadExtendersMetrics // NoteheadExtenders should only be created after JustifyHorizontally(), // so they should be null here. Debug.Assert(NoteheadExtendersMetrics == null); #endregion return maxOverlapWidth; }
/// <summary> /// noteObjects[noteObjectIndex] is the first OutputChordSymbol or OutputRestSymbol in the tuplet, and is the /// noteObject to which the tuplet is attached, /// </summary> /// <param name="graphics"></param> /// <param name="tupletDef"></param> /// <param name="noteObjects"></param> /// <param name="noteObjectIndex"></param> public Tuplet(Graphics graphics, TupletDef tupletDef, List <NoteObject> noteObjects, int noteObjectIndex) : base(noteObjects[noteObjectIndex]) { List <NoteObject> tupletChordsAndRests = GetTupletChordsAndRests(noteObjects, noteObjectIndex, tupletDef); var gap = M.PageFormat.GapVBPX; var textInfo = GetTupletTextInfo(tupletDef.InnerDuration, tupletDef.OuterDuration); Metrics = new TextMetrics(CSSObjectClass.tupletText, graphics, textInfo); var textHeight = (Metrics.Bottom - Metrics.Top); var textWidth = (Metrics.Right - Metrics.Left); bool isOver = (tupletDef.Orient == Orientation.up); double textXAlignment; if (tupletChordsAndRests.Count > 2) { int alignedIndex = (int)(tupletChordsAndRests.Count) / 2; Metrics metrics = tupletChordsAndRests[alignedIndex].Metrics; if (metrics is ChordMetrics cMetrics) { textXAlignment = cMetrics.OriginX - textWidth / 4; } else { textXAlignment = ((metrics.Right - metrics.Left) / 2) + metrics.Left; } } else { M.Assert(tupletChordsAndRests.Count == 2); Metrics metrics1 = tupletChordsAndRests[0].Metrics; Metrics metrics2 = tupletChordsAndRests[1].Metrics; textXAlignment = ((metrics1.Left + metrics2.Right) / 2) - textWidth / 4; } //textYAlignment = (isOver) ? metrics.Top - gap - (textHeight / 2) : metrics.Bottom + gap + (textHeight / 2); double textYAlignment = (isOver) ? double.MaxValue : double.MinValue;; foreach (NoteObject noteObject in tupletChordsAndRests) { var metrics = noteObject.Metrics; if (isOver) { textYAlignment = (metrics.Top < textYAlignment) ? metrics.Top : textYAlignment; } else { textYAlignment = (metrics.Bottom > textYAlignment) ? metrics.Bottom : textYAlignment; } } #region move vertically off the staff if necessary StaffMetrics staffMetrics = tupletChordsAndRests[0].Voice.Staff.Metrics; if (isOver) { double topMax = staffMetrics.StafflinesTop - gap - (textHeight / 2); if (textYAlignment > topMax) { textYAlignment = topMax; } } else { double topMin = staffMetrics.StafflinesBottom + gap + (textHeight / 2); if (textYAlignment < topMin) { textYAlignment = topMin; } } #endregion Metrics.Move(textXAlignment, textYAlignment + (textHeight / 2)); // set auto correctly later -- depends on beaming if (tupletDef.Bracket == TupletBracketDisplay.yes || tupletDef.Bracket == TupletBracketDisplay.auto) { double bracketHoriz = textYAlignment; double bracketLeft = tupletChordsAndRests[0].Metrics.Left - M.PageFormat.StafflineStemStrokeWidthVBPX; double bracketRight = tupletChordsAndRests[tupletChordsAndRests.Count - 1].Metrics.Right + M.PageFormat.StafflineStemStrokeWidthVBPX; double bracketHeight = gap * 0.75; _textAndBracketMetrics = new TupletMetrics((TextMetrics)Metrics, bracketHoriz, bracketLeft, bracketRight, bracketHeight, isOver); } }