実現したいこと
タイトルの通りですが、通常はY軸の左右どちらかに出る軸ラベルを軸の真上に描きたい。
発生している問題・分からないこと
複数のY軸が存在している場合にその座標の取り方がわからない。
具体的な描画の仕方も。
該当のソースコード
特になし
試したこと・調べたこと
- teratailやGoogle等で検索した
- ソースコードを自分なりに変更した
- 知人に聞いた
- その他
上記の詳細・結果
生成AI(chatGPTやgemini)に訊いても、やたらと存在しないAPIを使ったり、変な位置に表示したり、何も表示できなかったりで解決できなかった。
どうも、Y軸の真上はY軸の担当描画エリア外らしく、工夫が必要らしい、というところまではわかりました。
補足
特になし
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
回答1件
0
ベストアンサー
通常はY軸の左右どちらかに出る軸ラベルを軸の真上に描きたい。
ラベルを目盛りに重ねるって意味ですよね?見にくくないですか?(余計なお世話か^^;
Custom Axisを作れば、どうとでも描画できそうです。
ScottPlot5 WinForms Demo
ScottPlot5 WinForms Demo/Demos/CustomAxis.cs at main · ScottPlot/ScottPlot
ただ実際どうやるかは、既存実装を参考にしながら試行錯誤が必要そうです。
YAxisBase.cs at main · ScottPlot/ScottPlot
AxisBase.cs at main · ScottPlot/ScottPlot
FloatingAxisでよければリフレクションでごまかせましたが、残念ながら端に寄せるとはみ出た部分が消えますorz
cs
1using System.Reflection; 2using ScottPlot; 3using ScottPlot.AxisPanels; 4using ScottPlot.Plottables; 5using ScottPlot.WinForms; 6 7namespace Qa3g98l2vj841kv; 8 9public partial class Form1 : Form 10{ 11 public Form1() 12 { 13 InitializeComponent(); 14 15 var formsPlot1 = new FormsPlot { Dock = DockStyle.Fill, Parent = this, }; 16 17 // カスタムAxesに入れ替える 18 formsPlot1.Plot.Axes.Remove(formsPlot1.Plot.Axes.Left); 19 formsPlot1.Plot.Axes.AddLeftAxis(new CenterTickLabelYAxis(Edge.Left)); 20 21 var sig1 = formsPlot1.Plot.Add.Signal(Generate.Sin(mult: 0.01)); 22 sig1.Axes.YAxis = formsPlot1.Plot.Axes.Left; 23 formsPlot1.Plot.Axes.Left.Label.Text = "Left Axis"; 24 formsPlot1.Plot.Axes.Left.Label.ForeColor = sig1.Color; 25 26 27 formsPlot1.Plot.Axes.Remove(formsPlot1.Plot.Axes.Right); 28 formsPlot1.Plot.Axes.AddRightAxis(new CenterTickLabelYAxis(Edge.Right)); 29 30 var sig2 = formsPlot1.Plot.Add.Signal(Generate.Cos(mult: 100)); 31 sig2.Axes.YAxis = formsPlot1.Plot.Axes.Right; 32 formsPlot1.Plot.Axes.Right.Label.Text = "Right Axis"; 33 formsPlot1.Plot.Axes.Right.Label.ForeColor = sig2.Color; 34 35 36 // FloatingAxisでよければリフレクションでごまかせるが... 37 var floatingY = new FloatingAxis(formsPlot1.Plot.Axes.Left) 38 { 39 // 残念ながら端に寄せるとはみ出た部分が消えるorz 40 //Position = 0, 41 }; 42 formsPlot1.Plot.Add.Plottable(floatingY); 43 44 var labelStyle = (LabelStyle)floatingY.GetType() 45 .GetProperty("TickLabelStyle", BindingFlags.NonPublic | BindingFlags.Instance)! 46 .GetValue(floatingY)!; 47 labelStyle.Alignment = Alignment.MiddleCenter; 48 } 49} 50 51// カスタムAxes(Y軸専用) 52internal class CenterTickLabelYAxis : YAxisBase 53{ 54 public override Edge Edge { get; } 55 56 public CenterTickLabelYAxis(Edge edge) 57 { 58 if (edge != Edge.Left && edge != Edge.Right) throw new InvalidOperationException(); 59 60 Edge = edge; 61 TickGenerator = new ScottPlot.TickGenerators.NumericAutomatic(); 62 LabelRotation = edge == Edge.Left ? -90 : 90; 63 } 64 65 public override float Measure(Paint paint) 66 { 67 if (!IsVisible) return 0; 68 if (!Range.HasBeenSet) return SizeWhenNoData; 69 70 float maxTickLabelWidth = TickGenerator.Ticks.Length > 0 71 ? TickGenerator.Ticks.Select(x => TickLabelStyle.Measure(x.Label, paint).Width).Max() : 0; 72 73 // 軸上にラベルを表示するのでPaddingBetweenTickAndAxisLabelsは不要 74 float axisLabelHeight = string.IsNullOrEmpty(LabelStyle.Text) && LabelStyle.Image is null 75 ? EmptyLabelPadding.Horizontal 76 : LabelStyle.Measure(LabelText, paint).Height 77 //+ PaddingBetweenTickAndAxisLabels.Horizontal 78 + PaddingOutsideAxisLabels.Horizontal; 79 80 // ラベルが軸の反対側に半分出るので1/2 81 return maxTickLabelWidth / 2 + axisLabelHeight; 82 } 83 84 public override void Render(RenderPack rp, float size, float offset) 85 { 86 if (!IsVisible) return; 87 88 PixelRect panelRect = GetPanelRect(rp.DataRect, size, offset, rp.Paint); 89 float x = Edge == Edge.Left 90 ? panelRect.Left + PaddingOutsideAxisLabels.Horizontal 91 : panelRect.Right - PaddingOutsideAxisLabels.Horizontal; 92 Pixel labelPoint = new(x, rp.DataRect.VerticalCenter); 93 94 LabelAlignment = Alignment.UpperCenter; 95 LabelStyle.Render(rp.Canvas, labelPoint, rp.Paint); 96 97 DrawTicks2(rp, TickLabelStyle, panelRect, TickGenerator.Ticks, this, MajorTickStyle, MinorTickStyle); 98 DrawFrame(rp, panelRect, Edge, FrameLineStyle); 99 } 100 101 private static void DrawTicks2(RenderPack rp, LabelStyle label, PixelRect panelRect, IEnumerable<Tick> ticks, IAxis axis, TickMarkStyle majorStyle, TickMarkStyle minorStyle) 102 { 103 Paint paint = rp.Paint; 104 105 foreach (Tick tick in ticks) 106 { 107 paint.Color = tick.IsMajor ? majorStyle.Color : minorStyle.Color; 108 paint.StrokeWidth = tick.IsMajor ? majorStyle.Width : minorStyle.Width; 109 paint.IsAntialias = tick.IsMajor ? majorStyle.AntiAlias : minorStyle.AntiAlias; 110 float tickLength = tick.IsMajor ? majorStyle.Length : minorStyle.Length; 111 float yPx = axis.GetPixel(tick.Position, panelRect); 112 float x = axis.Edge == Edge.Left ? panelRect.Right : panelRect.Left; 113 114 // 左右にtickLength分の線を引く 115 PixelLine pxLine = new(x - tickLength, yPx, x + tickLength, yPx); 116 117 var lineStyle = tick.IsMajor ? majorStyle : minorStyle; 118 lineStyle.Render(rp.Canvas, paint, pxLine); 119 120 if (string.IsNullOrWhiteSpace(tick.Label) || !label.IsVisible) continue; 121 label.Text = tick.Label; 122 123 float xPx = x; // 軸に重ねるので余計なオフセット計算なし 124 125 Pixel px = new(xPx, yPx); 126 if (label.Rotation == 0) 127 label.Alignment = Alignment.MiddleCenter; // 何も気にせずMiddleCenterに描く 128 label.Render(rp.Canvas, px, paint); 129 } 130 } 131}
追記
Y軸の真上というのは、たとえば、示してくれたグラフでいうと"LeftAxis"の文字が、左のY軸の0.01の数字とForm1という文字の間に横書きで表示されて欲しいという意味でした。
cs
1using ScottPlot; 2using ScottPlot.AxisPanels; 3using ScottPlot.WinForms; 4 5namespace Qa3g98l2vj841kv; 6 7public partial class Form1 : Form 8{ 9 public Form1() 10 { 11 InitializeComponent(); 12 13 var formsPlot1 = new FormsPlot { Dock = DockStyle.Fill, Parent = this, }; 14 formsPlot1.Plot.Title("Plot Title"); 15 16 formsPlot1.Plot.Axes.Remove(formsPlot1.Plot.Axes.Left); 17 formsPlot1.Plot.Axes.AddLeftAxis(new HorizontalLabelYAxis(Edge.Left)); 18 19 var sig1 = formsPlot1.Plot.Add.Signal(Generate.Sin(mult: 0.01)); 20 sig1.Axes.YAxis = formsPlot1.Plot.Axes.Left; 21 formsPlot1.Plot.Axes.Left.Label.Text = "Left Axis"; 22 formsPlot1.Plot.Axes.Left.Label.ForeColor = sig1.Color; 23 24 25 formsPlot1.Plot.Axes.Remove(formsPlot1.Plot.Axes.Right); 26 formsPlot1.Plot.Axes.AddRightAxis(new HorizontalLabelYAxis(Edge.Right)); 27 28 var sig2 = formsPlot1.Plot.Add.Signal(Generate.Cos(mult: 100)); 29 sig2.Axes.YAxis = formsPlot1.Plot.Axes.Right; 30 formsPlot1.Plot.Axes.Right.Label.Text = "Right Axisssssssssssssss"; 31 formsPlot1.Plot.Axes.Right.Label.ForeColor = sig2.Color; 32 33 34 var sig3 = formsPlot1.Plot.Add.Signal(Generate.RandomSample(50)); 35 var left2 = new HorizontalLabelYAxis(Edge.Left) { LabelText = "Secondary Left Axis", LabelFontColor = sig3.Color, }; 36 sig3.Axes.YAxis = left2; 37 formsPlot1.Plot.Axes.AddLeftAxis(left2); 38 } 39} 40 41internal class HorizontalLabelYAxis : YAxisBase 42{ 43 public override Edge Edge { get; } 44 public PixelSize AxisLabelSize { get; private set; } 45 46 public HorizontalLabelYAxis(Edge edge) 47 { 48 if (edge != Edge.Left && edge != Edge.Right) throw new InvalidOperationException(); 49 50 Edge = edge; 51 TickGenerator = new ScottPlot.TickGenerators.NumericAutomatic(); 52 LabelAlignment = Alignment.UpperCenter; // 軸に対して上部中央 53 LabelRotation = 0; // 回転なし 54 } 55 56 public override float Measure(Paint paint) 57 { 58 if (!IsVisible) return 0; 59 if (!Range.HasBeenSet) return SizeWhenNoData; 60 61 float maxTickLabelWidth = TickGenerator.Ticks.Length > 0 62 ? TickGenerator.Ticks.Select(x => TickLabelStyle.Measure(x.Label, paint).Width).Max() : 0; 63 64 // 軸ラベルサイズ 65 AxisLabelSize = string.IsNullOrEmpty(LabelStyle.Text) && LabelStyle.Image is null 66 ? PixelSize.Zero : LabelStyle.Measure(LabelText, paint).Size; 67 68 // 目盛りラベル幅と軸ラベル幅半分の大きい方を採用+適当にパディング 69 return MathF.Max(maxTickLabelWidth, AxisLabelSize.Width / 2) 70 + PaddingBetweenTickAndAxisLabels.Horizontal 71 + PaddingOutsideAxisLabels.Horizontal; 72 } 73 74 public override void Render(RenderPack rp, float size, float offset) 75 { 76 if (!IsVisible) return; 77 78 PixelRect panelRect = GetPanelRect(rp.DataRect, size, offset, rp.Paint); 79 float x = Edge == Edge.Left ? panelRect.Right : panelRect.Left; 80 Pixel labelPoint = new(x, rp.DataRect.Top); 81 //Drawing.DrawDebugRectangle(rp.Canvas, rp.Paint, panelRect, labelPoint, LabelFontColor); 82 83 // 上にずらす座標計算(内側の軸から順に上に乗っていく仕様) 84 var offsetY = 0f; 85 foreach (var axes in rp.Plot.Axes.GetAxes(Edge).OfType<HorizontalLabelYAxis>()) 86 { 87 offsetY += axes.AxisLabelSize.Height; 88 if (axes == this) break; 89 } 90 LabelStyle.Render(rp.Canvas, labelPoint.MovedUp(offsetY), rp.Paint); 91 92 DrawTicks(rp, TickLabelStyle, panelRect, TickGenerator.Ticks, this, MajorTickStyle, MinorTickStyle); 93 DrawFrame(rp, panelRect, Edge, FrameLineStyle); 94 } 95}
投稿2026/01/30 12:03
編集2026/01/31 23:11総合スコア10237
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。


2026/01/31 17:11
2026/01/31 21:43
2026/02/01 03:48