diff --git a/dist/README.txt b/dist/README.txt
index b126fe2..f2e3e2e 100644
--- a/dist/README.txt
+++ b/dist/README.txt
@@ -1,5 +1,5 @@
UbisamBase 플랫폼 배포 폴더 (빌드할 때 자동 생성 - 직접 고치지 마세요)
-갱신 시각 : 2026-09-21 22:13:45
+갱신 시각 : 2026-09-21 22:17:54
빌드 구성 : Release
원본 소스 : D:\업무\03_개발\Source\용접기\08_UbisamPlatform\
----
diff --git a/dist/UbisamBase.Core.dll b/dist/UbisamBase.Core.dll
index f5de9e5..b311abf 100644
Binary files a/dist/UbisamBase.Core.dll and b/dist/UbisamBase.Core.dll differ
diff --git a/dist/UbisamBase.Launcher.exe b/dist/UbisamBase.Launcher.exe
index 70d3822..9980dd4 100644
Binary files a/dist/UbisamBase.Launcher.exe and b/dist/UbisamBase.Launcher.exe differ
diff --git a/dist/UbisamBase.Shell.exe b/dist/UbisamBase.Shell.exe
index 64bdd79..8ff640a 100644
Binary files a/dist/UbisamBase.Shell.exe and b/dist/UbisamBase.Shell.exe differ
diff --git a/dist/platform-version.json b/dist/platform-version.json
index aa5ca2c..af90d0a 100644
--- a/dist/platform-version.json
+++ b/dist/platform-version.json
@@ -1,4 +1,4 @@
{
-"version": "2026-09-21 22:13:45",
+"version": "2026-09-21 22:17:54",
"configuration": "Release"
}
diff --git a/doc/history/changelog.md b/doc/history/changelog.md
index f12ebb4..a5a0542 100644
--- a/doc/history/changelog.md
+++ b/doc/history/changelog.md
@@ -15,3 +15,4 @@
| 2026-09-21 | 플랫폼 자동 업데이트 추가 — Launcher가 켤 때 깃 저장소의 `dist`와 비교해 새 버전이면 받아서 실행(UI Setting에서 사용/자동 여부 선택) |
| 2026-09-21 | 업데이트 안내창을 플랫폼 디자인으로 교체(Launcher 자체 XAML, 시스템 테마 따름) |
| 2026-09-21 | `ApiService` 추가 — 사내 API를 앱이 대신 호출(토큰은 설정 파일에만, 호출 간격 제한·타임아웃 포함) |
+| 2026-09-21 | 차트 종류별 디자인 정리 — 면적·계단선·누적/가로 막대·원형·도넛·산점 추가, 계열 팔레트(Chart.Series1~6)와 툴팁·범례 테마 적용 |
diff --git a/doc/platform-guide.md b/doc/platform-guide.md
index 08a8e6d..a7268e2 100644
--- a/doc/platform-guide.md
+++ b/doc/platform-guide.md
@@ -299,7 +299,32 @@ private void Rebuild()
```
- **`Background="Transparent"`를 꼭 지정** — 기본값은 흰 배경이라 다크 테마에서 튐.
-- `ChartTheme.Column(values, name)`, `ChartTheme.Gauge(percent)`(도넛 게이지, `ISeries[]` 반환)도 있음.
+- **툴팁·범례는 따로 입혀야 한다** — 지정하지 않으면 LiveCharts 기본(밝은 바탕 + 검은 글자)이 나와
+ 다크 테마에서 튄다. 차트를 만든 뒤와 테마가 바뀐 뒤에 `ChartTheme.Apply(chart)`를 한 번 부른다
+ (`CartesianChart` / `PieChart` 둘 다 있음).
+
+### 4-1. 차트 종류별 — 무엇을 언제 쓰나
+
+| 만들기 | 생김새 | 쓰는 자리 |
+| --- | --- | --- |
+| `Line(values, name)` | accent 선 2px, 보간 없음, 면적 12% | 단일 측정값 추이(기본) |
+| `Line(values, name, i)` | 팔레트 i번 선, 면적 없음 | 여러 계열을 한 그래프에 겹칠 때 |
+| `ReferenceLine(values, name)` | 회색 파선, hover 없음 | 기준·목표·규격 상하한. **측정선보다 먼저** 넣어야 아래에 깔린다 |
+| `SmoothLine(values, name, i)` | 곡선 보간(0.65) | 추세만 보여줄 때. **공정 측정값에는 쓰지 말 것** — 없던 중간값을 지어낸다 |
+| `Area(values, name, i)` | 선 + 채움 24% | 누적량·점유량처럼 "쌓인 양" |
+| `StepLine(values, name, i)` | 계단선 | ON/OFF·단계값처럼 다음 값까지 유지되는 신호 |
+| `Column(values, name[, i])` | 각진 세로 막대, 채움 85% | 항목별 비교(수량·횟수) |
+| `StackedColumn(values, name, i)` | 누적 세로 막대 | 전체 대비 구성비를 시간축으로 |
+| `Bar(values, name[, i])` | 가로 막대 | 항목 이름이 길어 세로 막대 라벨이 겹칠 때(설비명·불량 유형) |
+| `Pie(values[, names])` | 원형, 조각 사이 판 색 경계 | 구성비. 조각 6개 이하일 때만 |
+| `Donut(values[, names])` | 가운데 빈 원형 | 구성비 + 가운데에 총계 표시 |
+| `Gauge(percent)` | 0~100 도넛 게이지 | 가동률·달성률 한 값 |
+| `Scatter(points, name, i)` | 점 9px, 판 색 테두리 | 두 값의 상관 |
+
+- **색은 직접 고르지 않는다.** 단일 계열은 accent 하나, 여러 계열은 `seriesIndex`를 0,1,2…로 넘기면
+ `Ubisam.Brush.Chart.Series1~6` 팔레트를 순서대로 쓴다(라이트/다크 각각 따로 정의돼 있다).
+ 7개째부터는 색이 처음으로 돌아가므로, 계열이 그보다 많으면 차트를 나누는 편이 낫다.
+- 팔레트 색이 필요하면 `ChartTheme.SeriesColor(i)`로 꺼내 쓴다(범례 표식 등).
- 소비 프로젝트에서 차트를 직접 쓰려면 `LiveChartsCore`, `LiveChartsCore.SkiaSharpView`,
`LiveChartsCore.SkiaSharpView.WPF`, `SkiaSharp` — 4개를 `$(UbisamPlatformDir)`에서
HintPath로 참조 추가해야 함(TestApp.csproj 예시 참고).
diff --git a/src/UbisamBase.Core/Charting/ChartTheme.cs b/src/UbisamBase.Core/Charting/ChartTheme.cs
index b6edaf1..2353f31 100644
--- a/src/UbisamBase.Core/Charting/ChartTheme.cs
+++ b/src/UbisamBase.Core/Charting/ChartTheme.cs
@@ -2,7 +2,9 @@ using System;
using System.Windows;
using System.Windows.Media;
using LiveChartsCore;
+using LiveChartsCore.Defaults;
using LiveChartsCore.SkiaSharpView;
+using LiveChartsCore.SkiaSharpView.WPF;
using LiveChartsCore.SkiaSharpView.Painting;
using LiveChartsCore.SkiaSharpView.Painting.Effects;
using SkiaSharp;
@@ -117,4 +119,204 @@ public static class ChartTheme
LabelsPaint = new SolidColorPaint(Muted) { SKTypeface = BodyTypeface },
SeparatorsPaint = new SolidColorPaint(GridLine, 1)
};
+
+ // ─────────────────────────────────────────────────────────────────────────
+ // 시리즈 색
+ // 단일 계열 차트는 지금까지처럼 accent 하나만 쓴다. 여러 계열을 한 그래프에 그릴 때만
+ // 아래 팔레트를 순서대로 쓴다 — 색을 화면마다 고르면 프로그램마다 톤이 달라진다.
+ // ─────────────────────────────────────────────────────────────────────────
+
+ /// 계열 순번(0부터)에 해당하는 색. 6개를 넘어가면 처음으로 돌아간다.
+ public static SKColor SeriesColor(int index)
+ => ReadColor("Ubisam.Brush.Chart.Series" + ((Math.Abs(index) % 6) + 1));
+
+ /// 꺾은선 — 여러 계열용. 색만 팔레트에서 가져오고 나머지는 단일 계열과 같다(두께 2, 보간 없음).
+ public static ISeries Line(double[] values, string name, int seriesIndex)
+ {
+ var color = SeriesColor(seriesIndex);
+ return new LineSeries
+ {
+ Values = values,
+ Name = name,
+ Stroke = new SolidColorPaint(color, 2),
+ Fill = null,
+ GeometrySize = 0,
+ LineSmoothness = 0,
+ AnimationsSpeed = AnimationSpeed
+ };
+ }
+
+ /// 부드러운 꺾은선 — 추세용. 공정 측정값처럼 "튄 지점"이 중요한 데이터에는 쓰지 말 것
+ /// (보간이 실제로 없던 중간값을 지어낸다).
+ public static ISeries SmoothLine(double[] values, string name, int seriesIndex = 0)
+ {
+ var color = SeriesColor(seriesIndex);
+ return new LineSeries
+ {
+ Values = values,
+ Name = name,
+ Stroke = new SolidColorPaint(color, 2),
+ Fill = null,
+ GeometrySize = 0,
+ LineSmoothness = 0.65,
+ AnimationsSpeed = AnimationSpeed
+ };
+ }
+
+ /// 면적 — 누적량·점유량처럼 "쌓인 양"을 보여줄 때. 겹쳐 그릴 수 있도록 채움을 24%로 둔다.
+ public static ISeries Area(double[] values, string name, int seriesIndex = 0)
+ {
+ var color = SeriesColor(seriesIndex);
+ return new LineSeries
+ {
+ Values = values,
+ Name = name,
+ Stroke = new SolidColorPaint(color, 2),
+ Fill = new SolidColorPaint(color.WithAlpha(0x3D)),
+ GeometrySize = 0,
+ LineSmoothness = 0,
+ AnimationsSpeed = AnimationSpeed
+ };
+ }
+
+ /// 계단선 — ON/OFF나 단계값처럼 "다음 값까지 그 상태가 유지되는" 신호용.
+ /// 이런 데이터를 꺾은선으로 그리면 없는 중간값이 있는 것처럼 보인다.
+ public static ISeries StepLine(double[] values, string name, int seriesIndex = 0)
+ {
+ var color = SeriesColor(seriesIndex);
+ return new StepLineSeries
+ {
+ Values = values,
+ Name = name,
+ Stroke = new SolidColorPaint(color, 2),
+ Fill = null,
+ GeometrySize = 0,
+ AnimationsSpeed = AnimationSpeed
+ };
+ }
+
+ /// 세로 막대 — 여러 계열용. 각진 모서리는 단일 계열과 같다.
+ public static ISeries Column(double[] values, string name, int seriesIndex)
+ => new ColumnSeries
+ {
+ Values = values,
+ Name = name,
+ Fill = new SolidColorPaint(SeriesColor(seriesIndex).WithAlpha(217)),
+ Rx = 0,
+ Ry = 0,
+ AnimationsSpeed = AnimationSpeed
+ };
+
+ /// 누적 막대 — 전체 대비 구성비를 볼 때. 같은 차트에 넣은 누적 막대끼리 쌓인다.
+ public static ISeries StackedColumn(double[] values, string name, int seriesIndex)
+ => new StackedColumnSeries
+ {
+ Values = values,
+ Name = name,
+ Fill = new SolidColorPaint(SeriesColor(seriesIndex).WithAlpha(217)),
+ Rx = 0,
+ Ry = 0,
+ AnimationsSpeed = AnimationSpeed
+ };
+
+ /// 가로 막대 — 항목 이름이 길어 세로 막대에서 라벨이 겹칠 때(설비명·불량 유형 등).
+ public static ISeries Bar(double[] values, string name, int seriesIndex = 0)
+ => new RowSeries
+ {
+ Values = values,
+ Name = name,
+ Fill = new SolidColorPaint(SeriesColor(seriesIndex).WithAlpha(217)),
+ Rx = 0,
+ Ry = 0,
+ AnimationsSpeed = AnimationSpeed
+ };
+
+ /// 원형 — 구성비. 조각은 팔레트 순서대로, 경계는 판 색으로 얇게 갈라 서로 붙어 보이지 않게 한다.
+ /// 조각이 6개를 넘으면 색이 돌아오므로 그때는 가로 막대(Bar)가 낫다.
+ public static ISeries[] Pie(double[] values, string[] names = null)
+ => BuildPie(values, names, innerRadius: 0);
+
+ /// 도넛 — 원형과 같되 가운데를 비워 총계·제목을 얹을 수 있다.
+ public static ISeries[] Donut(double[] values, string[] names = null)
+ => BuildPie(values, names, innerRadius: 52);
+
+ private static ISeries[] BuildPie(double[] values, string[] names, double innerRadius)
+ {
+ var series = new ISeries[values.Length];
+
+ for (var i = 0; i < values.Length; i++)
+ {
+ series[i] = new PieSeries
+ {
+ Values = new[] { values[i] },
+ Name = names != null && i < names.Length ? names[i] : string.Empty,
+ Fill = new SolidColorPaint(SeriesColor(i)),
+ Stroke = new SolidColorPaint(Surface, 2),
+ InnerRadius = innerRadius,
+ AnimationsSpeed = AnimationSpeed
+ };
+ }
+
+ return series;
+ }
+
+ /// 산점 — 두 값의 상관을 볼 때. 점이 겹쳐도 개수를 셀 수 있도록 판 색 테두리를 두른다.
+ public static ISeries Scatter(ObservablePoint[] points, string name, int seriesIndex = 0)
+ {
+ var color = SeriesColor(seriesIndex);
+ return new ScatterSeries
+ {
+ Values = points,
+ Name = name,
+ Fill = new SolidColorPaint(color.WithAlpha(0xC8)),
+ Stroke = new SolidColorPaint(Surface, 1),
+ GeometrySize = 9,
+ AnimationsSpeed = AnimationSpeed
+ };
+ }
+
+ // ─────────────────────────────────────────────────────────────────────────
+ // 툴팁 · 범례 — 차트 컨트롤에 직접 넣어야 하는 값들.
+ // 지정하지 않으면 LiveCharts 기본(밝은 바탕 + 검은 글자)이 나와서 다크 테마에서 튄다.
+ // ─────────────────────────────────────────────────────────────────────────
+
+ public static SolidColorPaint TooltipTextPaint => new SolidColorPaint(Text) { SKTypeface = BodyTypeface };
+
+ public static SolidColorPaint TooltipBackgroundPaint => new SolidColorPaint(Surface);
+
+ public static SolidColorPaint LegendTextPaint => new SolidColorPaint(Muted) { SKTypeface = BodyTypeface };
+
+ public static SolidColorPaint LegendBackgroundPaint => new SolidColorPaint(Surface);
+
+ /// 툴팁·범례 색을 이 차트에 입힌다. 차트를 만든 뒤와 테마가 바뀐 뒤에 한 번씩 불러주면 된다.
+ public static void Apply(CartesianChart chart)
+ {
+ if (chart == null)
+ {
+ return;
+ }
+
+ chart.TooltipTextPaint = TooltipTextPaint;
+ chart.TooltipBackgroundPaint = TooltipBackgroundPaint;
+ chart.TooltipTextSize = 12;
+ chart.LegendTextPaint = LegendTextPaint;
+ chart.LegendBackgroundPaint = LegendBackgroundPaint;
+ chart.LegendTextSize = 12;
+ }
+
+ /// 원형·도넛 차트에도 같은 처리.
+ public static void Apply(PieChart chart)
+ {
+ if (chart == null)
+ {
+ return;
+ }
+
+ chart.TooltipTextPaint = TooltipTextPaint;
+ chart.TooltipBackgroundPaint = TooltipBackgroundPaint;
+ chart.TooltipTextSize = 12;
+ chart.LegendTextPaint = LegendTextPaint;
+ chart.LegendBackgroundPaint = LegendBackgroundPaint;
+ chart.LegendTextSize = 12;
+ }
}
diff --git a/src/UbisamBase.Core/Themes/Colors.Dark.xaml b/src/UbisamBase.Core/Themes/Colors.Dark.xaml
index 447997d..2c0421f 100644
--- a/src/UbisamBase.Core/Themes/Colors.Dark.xaml
+++ b/src/UbisamBase.Core/Themes/Colors.Dark.xaml
@@ -65,4 +65,14 @@
+
+
+
+
+
+
+
+
diff --git a/src/UbisamBase.Core/Themes/Colors.Light.xaml b/src/UbisamBase.Core/Themes/Colors.Light.xaml
index 1025935..9ec1781 100644
--- a/src/UbisamBase.Core/Themes/Colors.Light.xaml
+++ b/src/UbisamBase.Core/Themes/Colors.Light.xaml
@@ -60,4 +60,12 @@
+
+
+
+
+
+
+
+