Beispiel #1
0
        public Image Convert()
        {
            var   sigs           = Score.TimeSignatures.OrderBy(p => p.Key).ToList();
            int   lanesCount     = Ched.Core.Constants.LanesCount;
            int   columnTick     = MeasurementProfile.CalcColumnTickFromTicksPerBeat(Score.TicksPerBeat);
            int   paddingTick    = MeasurementProfile.CalcPaddingTickFromTicksPerBeat(Score.TicksPerBeat);
            float laneWidth      = MeasurementProfile.UnitLaneWidth + MeasurementProfile.BorderThickness;
            float wholeLaneWidth = laneWidth * lanesCount;
            float columnHeight   = columnTick * MeasurementProfile.UnitBeatHeight / Score.TicksPerBeat;
            float paddingHeight  = paddingTick * MeasurementProfile.UnitBeatHeight / Score.TicksPerBeat;
            int   headTick       = 0;

            var bmp                   = new Bitmap((int)(MeasurementProfile.PaddingWidth * 2 + wholeLaneWidth) * (int)Math.Ceiling((double)Score.GetLastTick() / columnTick), (int)(paddingHeight * 2 + columnHeight));
            var longEndsPos           = new HashSet <NotePosition>(Score.LongNotes['2'].Concat(Score.LongNotes['3']).Select(p => p.Last().Position));
            var unUsedShortNoteQueues = Score.ShortNotes.ToDictionary(p => p.Key, p =>
            {
                var q = new ConcurrentPriorityQueue <NoteDefinition, int>();
                foreach (var item in p.Value)
                {
                    q.Enqueue(item, -item.Position.Tick);
                }
                return(q);
            });
            var usingShortNoteQueues = Score.ShortNotes.ToDictionary(p => p.Key, p => new ConcurrentPriorityQueue <NoteDefinition, int>());
            var unUsedLongNoteQueues = Score.LongNotes.ToDictionary(p => p.Key, p =>
            {
                var q = new ConcurrentPriorityQueue <List <NoteDefinition>, int>();
                foreach (var item in p.Value)
                {
                    q.Enqueue(item, -item[0].Position.Tick);
                }
                return(q);
            });
            var usingLongNoteQueues = Score.LongNotes.ToDictionary(p => p.Key, p => new ConcurrentPriorityQueue <List <NoteDefinition>, int>());
            var emptyLong           = Enumerable.Empty <List <NoteDefinition> >();

            bool visibleStep(NoteDefinition p) => p.Type == '1' || p.Type == '2' || p.Type == '3';
            void moveToOrigin(Graphics g) => g.TranslateTransform(MeasurementProfile.PaddingWidth, paddingHeight + columnHeight - 1, MatrixOrder.Append);
            void moveToColumn(Graphics g, int columnsCount) => g.TranslateTransform((MeasurementProfile.PaddingWidth * 2 + wholeLaneWidth) * columnsCount, 0, MatrixOrder.Append);
            void moveToHead(Graphics g, int head) => g.TranslateTransform(0, head * MeasurementProfile.UnitBeatHeight / TicksPerBeat, MatrixOrder.Append);

            using (var g = Graphics.FromImage(bmp))
            {
                int   columnsCount = 0;
                SizeF strSize      = g.MeasureString("000", MeasurementProfile.Font);
                var   dc           = new DrawingContext(g, NoteColorProfile);
                g.Clear(BackgroundColorProfile.BackgroundColor);

                while (unUsedShortNoteQueues.Any(p => p.Value.Count > 0) || usingShortNoteQueues.Any(p => p.Value.Count > 0) || usingLongNoteQueues.Any(p => p.Value.Count > 0) || unUsedLongNoteQueues.Any(p => p.Value.Count > 0))
                {
                    int tailTick = headTick + columnTick;

                    // 範囲外に出たノーツの更新
                    foreach (var type in usingShortNoteQueues)
                    {
                        while (type.Value.Count > 0 && type.Value.Peek().Position.Tick < headTick - paddingTick)
                        {
                            type.Value.Dequeue();
                        }
                    }

                    foreach (var type in usingLongNoteQueues)
                    {
                        while (type.Value.Count > 0 && type.Value.Peek()[type.Value.Peek().Count - 1].Position.Tick < headTick - paddingTick)
                        {
                            type.Value.Dequeue();
                        }
                    }

                    // 範囲内に入るノーツの更新
                    foreach (var type in unUsedShortNoteQueues)
                    {
                        while (type.Value.Count > 0 && type.Value.Peek().Position.Tick < tailTick + paddingTick)
                        {
                            var item = type.Value.Dequeue();
                            usingShortNoteQueues[type.Key].Enqueue(item, -item.Position.Tick);
                        }
                    }

                    foreach (var type in unUsedLongNoteQueues)
                    {
                        while (type.Value.Count > 0 && type.Value.Peek()[0].Position.Tick < tailTick + paddingTick)
                        {
                            var item = type.Value.Dequeue();
                            usingLongNoteQueues[type.Key].Enqueue(item, -item[item.Count - 1].Position.Tick);
                        }
                    }

                    g.ResetTransform();
                    g.ScaleTransform(1, -1);
                    moveToOrigin(g);
                    moveToHead(g, headTick);
                    moveToColumn(g, columnsCount);

                    // レーン分割線描画
                    using (var pen = new Pen(BackgroundColorProfile.LaneBorderColor, MeasurementProfile.BorderThickness))
                    {
                        for (int i = 0; i <= lanesCount; i++)
                        {
                            if (i % 2 != 0)
                            {
                                continue;
                            }
                            float x = i * laneWidth;
                            g.DrawLine(pen, x, GetYPositionFromTick(headTick - paddingTick), x, GetYPositionFromTick(tailTick + paddingTick));
                        }
                    }

                    // 時間ガイドの描画
                    // そのイベントが含まれる小節(ただし[小節開始Tick, 小節開始Tick + 小節Tick)の範囲)からその拍子を適用

                    using (var beatPen = new Pen(BackgroundColorProfile.BeatLineColor, MeasurementProfile.BorderThickness))
                        using (var barPen = new Pen(BackgroundColorProfile.BarLineColor, MeasurementProfile.BorderThickness))
                        {
                            int headPos = 0;
                            int pos     = 0;
                            for (int j = 0; j < sigs.Count; j++)
                            {
                                int barTick  = (int)(TicksPerBeat * sigs[j].Value);
                                int beatTick = Math.Min(TicksPerBeat, barTick);

                                while (pos <= tailTick)
                                {
                                    if (j < sigs.Count - 1 && pos - headPos >= (sigs[j + 1].Key - headPos) / barTick * barTick)
                                    {
                                        break;
                                    }
                                    float y = GetYPositionFromTick(pos);
                                    g.DrawLine((pos - headPos) % barTick == 0 ? barPen : beatPen, 0, y, wholeLaneWidth, y);
                                    pos += beatTick;
                                }
                                headPos = pos;
                            }
                        }


                    // ノーツ描画
                    foreach (var hold in usingLongNoteQueues.ContainsKey('2') ? usingLongNoteQueues['2'] : emptyLong)
                    {
                        dc.DrawHoldBackground(new RectangleF(
                                                  (MeasurementProfile.UnitLaneWidth + MeasurementProfile.BorderThickness) * hold[0].Position.LaneIndex + MeasurementProfile.BorderThickness,
                                                  GetYPositionFromTick(hold[0].Position.Tick),
                                                  (MeasurementProfile.UnitLaneWidth + MeasurementProfile.BorderThickness) * hold[0].Position.Width - MeasurementProfile.BorderThickness,
                                                  GetYPositionFromTick(hold[1].Position.Tick - hold[0].Position.Tick)
                                                  ));
                    }

                    foreach (var slide in usingLongNoteQueues.ContainsKey('3') ? usingLongNoteQueues['3'] : emptyLong)
                    {
                        var visibleSteps = slide.Where(visibleStep).ToList();
                        for (int i = 0; i < slide.Count - 1; i++)
                        {
                            dc.DrawSlideBackground(
                                laneWidth * slide[i].Position.Width - MeasurementProfile.BorderThickness,
                                laneWidth * slide[i + 1].Position.Width - MeasurementProfile.BorderThickness,
                                laneWidth * slide[i].Position.LaneIndex,
                                GetYPositionFromTick(slide[i].Position.Tick),
                                laneWidth * slide[i + 1].Position.LaneIndex,
                                GetYPositionFromTick(slide[i + 1].Position.Tick) + 0.4f,
                                GetYPositionFromTick(visibleSteps.Last(p => p.Position.Tick <= slide[i].Position.Tick).Position.Tick),
                                GetYPositionFromTick(visibleSteps.First(p => p.Position.Tick >= slide[i + 1].Position.Tick).Position.Tick),
                                MeasurementProfile.ShortNoteHeight);
                        }
                    }

                    foreach (var airAction in usingLongNoteQueues.ContainsKey('4') ? usingLongNoteQueues['4'] : emptyLong)
                    {
                        dc.DrawAirHoldLine(
                            laneWidth * (airAction[0].Position.LaneIndex + airAction[0].Position.Width / 2f),
                            GetYPositionFromTick(airAction[0].Position.Tick),
                            GetYPositionFromTick(airAction[airAction.Count - 1].Position.Tick),
                            MeasurementProfile.ShortNoteHeight);
                    }

                    foreach (var hold in usingLongNoteQueues.ContainsKey('2') ? usingLongNoteQueues['2'] : emptyLong)
                    {
                        dc.DrawHoldBegin(GetRectFromNotePosition(hold[0].Position));
                        dc.DrawHoldEnd(GetRectFromNotePosition(hold[hold.Count - 1].Position));
                    }

                    foreach (var slide in usingLongNoteQueues.ContainsKey('3') ? usingLongNoteQueues['3'] : emptyLong)
                    {
                        dc.DrawSlideBegin(GetRectFromNotePosition(slide[0].Position));
                        foreach (var item in slide.Where(visibleStep).Skip(1))
                        {
                            if (item.Position.Tick < headTick - paddingTick)
                            {
                                continue;
                            }
                            if (item.Position.Tick > tailTick + paddingTick)
                            {
                                break;
                            }
                            dc.DrawSlideStep(GetRectFromNotePosition(item.Position));
                        }
                    }

                    // ロング終点AIR
                    var airs = usingShortNoteQueues.ContainsKey('5') ? usingShortNoteQueues['5'] : Enumerable.Empty <NoteDefinition>();
                    foreach (var air in airs)
                    {
                        if (!longEndsPos.Contains(air.Position))
                        {
                            continue;
                        }
                        dc.DrawAirStep(GetRectFromNotePosition(air.Position));
                    }

                    var shortNotesDic = usingShortNoteQueues['1'].GroupBy(p => p.Type).ToDictionary(p => p.Key, p => p.Select(q => q.Position));
                    void drawNotes(char key, Action <NotePosition> drawer)
                    {
                        if (!shortNotesDic.ContainsKey(key))
                        {
                            return;
                        }
                        foreach (var item in shortNotesDic[key])
                        {
                            drawer(item);
                        }
                    }

                    drawNotes('1', item => dc.DrawTap(GetRectFromNotePosition(item)));
                    drawNotes('2', item => dc.DrawExTap(GetRectFromNotePosition(item)));
                    drawNotes('5', item => dc.DrawExTap(GetRectFromNotePosition(item)));
                    drawNotes('6', item => dc.DrawExTap(GetRectFromNotePosition(item)));
                    drawNotes('3', item => dc.DrawFlick(GetRectFromNotePosition(item)));
                    drawNotes('4', item => dc.DrawDamage(GetRectFromNotePosition(item)));

                    foreach (var airAction in usingLongNoteQueues.ContainsKey('4') ? usingLongNoteQueues['4'] : emptyLong)
                    {
                        foreach (var item in airAction.Skip(1))
                        {
                            if (item.Position.Tick < headTick - paddingTick)
                            {
                                continue;
                            }
                            if (item.Position.Tick > tailTick + paddingTick)
                            {
                                break;
                            }
                            dc.DrawAirAction(GetRectFromNotePosition(item.Position).Expand(-MeasurementProfile.ShortNoteHeight * 0.28f));
                        }
                    }

                    foreach (var air in airs)
                    {
                        var vd = air.Type == '2' || air.Type == '5' || air.Type == '6' ? VerticalAirDirection.Down : VerticalAirDirection.Up;
                        var hd = air.Type == '1' || air.Type == '2' || air.Type == '7' ? HorizontalAirDirection.Center :
                                 (air.Type == '3' || air.Type == '5' || air.Type == '8' ? HorizontalAirDirection.Left : HorizontalAirDirection.Right);
                        dc.DrawAir(GetRectFromNotePosition(air.Position), vd, hd);
                    }

                    g.ResetTransform();
                    moveToOrigin(g);
                    moveToHead(g, headTick);
                    moveToColumn(g, columnsCount);

                    // 小節番号
                    using (var brush = new SolidBrush(BackgroundColorProfile.BarIndexColor))
                    {
                        int pos      = 0;
                        int barCount = 0;
                        for (int j = 0; j < sigs.Count; j++)
                        {
                            int currentBarTick = (int)(TicksPerBeat * sigs[j].Value);
                            for (int i = 0; pos + i * currentBarTick < tailTick; i++)
                            {
                                if (j < sigs.Count - 1 && i * currentBarTick >= (sigs[j + 1].Key - pos) / currentBarTick * currentBarTick)
                                {
                                    break;
                                }

                                int tick = pos + i * currentBarTick;
                                barCount++;
                                if (tick < headTick)
                                {
                                    continue;
                                }
                                var point = new PointF(-strSize.Width, -GetYPositionFromTick(tick) - strSize.Height);
                                g.DrawString(string.Format("{0:000}", barCount), MeasurementProfile.Font, brush, point);
                            }

                            if (j < sigs.Count - 1)
                            {
                                pos += (sigs[j + 1].Key - pos) / currentBarTick * currentBarTick;
                            }
                        }
                    }

                    float rightBaseX = wholeLaneWidth + strSize.Width / 3;

                    // BPM
                    using (var brush = new SolidBrush(BackgroundColorProfile.BpmColor))
                    {
                        foreach (var item in Score.BpmDefinitions.Where(p => p.Key >= headTick && p.Key < tailTick))
                        {
                            var point = new PointF(rightBaseX, -GetYPositionFromTick(item.Key) - strSize.Height);
                            g.DrawString(string.Format("{0:000.#}", item.Value), MeasurementProfile.Font, brush, point);
                        }
                    }

                    // 次の列に移動
                    headTick += columnTick;
                    columnsCount++;
                }
            }

            return(bmp);
        }