diff --git a/0_cmake_library/register/generated/vsg_cesium/current_compiler.cmake b/0_cmake_library/register/generated/vsg_cesium/current_compiler.cmake
new file mode 100644
index 0000000..1617708
--- /dev/null
+++ b/0_cmake_library/register/generated/vsg_cesium/current_compiler.cmake
@@ -0,0 +1,3 @@
+set(CMAKE_C_COMPILER [[C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/VC/Tools/MSVC/14.29.30133/bin/Hostx64/x64/cl.bat]] CACHE FILEPATH "" FORCE)
+set(CMAKE_CXX_COMPILER [[C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/VC/Tools/MSVC/14.29.30133/bin/Hostx64/x64/cl.bat]] CACHE FILEPATH "" FORCE)
+set(CMAKE_RC_COMPILER [[C:/Program Files (x86)/Windows Kits/10/bin/10.0.26100.0/x64/rc.exe]] CACHE FILEPATH "" FORCE)
diff --git a/0_cmake_library/register/generated/vsg_cesium/triplets/x64-windows-static-md.cmake b/0_cmake_library/register/generated/vsg_cesium/triplets/x64-windows-static-md.cmake
new file mode 100644
index 0000000..c577f81
--- /dev/null
+++ b/0_cmake_library/register/generated/vsg_cesium/triplets/x64-windows-static-md.cmake
@@ -0,0 +1,5 @@
+set(VCPKG_TARGET_ARCHITECTURE x64)
+set(VCPKG_CRT_LINKAGE dynamic)
+set(VCPKG_LIBRARY_LINKAGE static)
+set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE [[D:/ae/proj/git/Radio/third_party/build_infra/0_cmake_library/register/generated/vsg_cesium/current_compiler.cmake]])
+set(VCPKG_VISUAL_STUDIO_PATH [[C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise]])
diff --git a/Doxygen/Doxyfile.in b/Doxygen/Doxyfile.in
new file mode 100644
index 0000000..81d45e6
--- /dev/null
+++ b/Doxygen/Doxyfile.in
@@ -0,0 +1,40 @@
+PROJECT_NAME = "@ARG_PROJECT_NAME@"
+OUTPUT_DIRECTORY = "@ARG_OUTPUT_DIR@"
+INPUT = @doxygen_input@
+EXCLUDE = @doxygen_exclude@
+FILE_PATTERNS = @doxygen_file_patterns@
+RECURSIVE = YES
+GENERATE_HTML = YES
+GENERATE_LATEX = NO
+GENERATE_XML = YES
+EXTRACT_ALL = NO
+EXTRACT_PRIVATE = NO
+EXTRACT_STATIC = NO
+HIDE_UNDOC_MEMBERS = NO
+HIDE_UNDOC_CLASSES = NO
+QUIET = NO
+WARNINGS = YES
+WARN_IF_UNDOCUMENTED = YES
+WARN_IF_DOC_ERROR = YES
+WARN_AS_ERROR = NO
+JAVADOC_AUTOBRIEF = YES
+QT_AUTOBRIEF = YES
+OPTIMIZE_OUTPUT_FOR_C = NO
+ENABLE_PREPROCESSING = YES
+MACRO_EXPANSION = YES
+EXPAND_ONLY_PREDEF = NO
+PREDEFINED = @doxygen_predefined@
+HAVE_DOT = YES
+DOT_PATH = "@dot_dir@"
+DOT_IMAGE_FORMAT = svg
+CLASS_GRAPH = YES
+COLLABORATION_GRAPH = YES
+GROUP_GRAPHS = YES
+INCLUDE_GRAPH = YES
+INCLUDED_BY_GRAPH = YES
+CALL_GRAPH = YES
+CALLER_GRAPH = YES
+DIRECTORY_GRAPH = YES
+UML_LOOK = NO
+INTERACTIVE_SVG = YES
+MAX_DOT_GRAPH_DEPTH = 0
diff --git a/Doxygen/export.cmake b/Doxygen/export.cmake
new file mode 100644
index 0000000..44e4407
--- /dev/null
+++ b/Doxygen/export.cmake
@@ -0,0 +1 @@
+include(${CMAKE_CURRENT_LIST_DIR}/main.cmake)
diff --git a/Graphviz/Cmake_Target_Graph.ps1.in b/Graphviz/Cmake_Target_Graph.ps1.in
new file mode 100644
index 0000000..8f3d1f5
--- /dev/null
+++ b/Graphviz/Cmake_Target_Graph.ps1.in
@@ -0,0 +1,64 @@
+$ErrorActionPreference = 'Stop'
+$targets = @PSC_TARGETS@
+$edges = @PSC_TARGET_EDGES@
+$outputDir = '@PSC_OUTPUT_DIR@'
+$format = '@PSC_FORMAT@'
+$dotExe = '@PSC_DOT_EXECUTABLE@'
+New-Item -ItemType Directory -Force -Path $outputDir | Out-Null
+function Convert-To_Dot_Text([string]$text) {
+ return $text.Replace('"', '\"')
+}
+$lines = [System.Collections.Generic.List[string]]::new()
+$lines.Add('digraph "radio cmake_targets" {')
+$lines.Add('rankdir=LR;')
+$lines.Add('splines=ortho;')
+$lines.Add('concentrate=true;')
+$lines.Add('forcelabels=true;')
+$lines.Add('graph [fontname="Consolas", label="radio cmake_targets", labelloc=t, fontsize=18];')
+$lines.Add('node [shape=box, style="rounded,filled", fontname="Consolas", fontsize=10];')
+$lines.Add('edge [fontname="Consolas", fontsize=9];')
+foreach ($target in $targets) {
+ $safe = Convert-To_Dot_Text $target
+ $lines.Add('"' + $safe + '" [label="' + $safe + '", tooltip="' + $safe + '", fillcolor="#dcfce7"];')
+}
+foreach ($edge in $edges) {
+ $parts = $edge.Split('|')
+ if ($parts.Count -eq 2) {
+ $left = Convert-To_Dot_Text $parts[0]
+ $right = Convert-To_Dot_Text $parts[1]
+ $lines.Add('"' + $left + '" -> "' + $right + '" [xlabel="links"];')
+ }
+}
+$lines.Add('subgraph cluster_legend {')
+$lines.Add('label="legend";')
+$lines.Add('style="rounded,dashed";')
+$lines.Add('"legend_target" [label="cmake target", fillcolor="#dcfce7"];')
+$lines.Add('"legend_edge" [label="edge = target_link_libraries / target dependency", fillcolor="#f8fafc"];')
+$lines.Add('}')
+$lines.Add('}')
+$dotFile = Join-Path $outputDir 'radio_cmake_targets.dot'
+$imageFile = Join-Path $outputDir ('radio_cmake_targets.' + $format)
+Set-Content -LiteralPath $dotFile -Value $lines -Encoding UTF8
+& $dotExe ('-T' + $format) $dotFile '-o' $imageFile
+if ($LASTEXITCODE -ne 0) {
+ exit $LASTEXITCODE
+}
+$items = @(
+ 'radio_directory_tree',
+ 'radio_module_dependency',
+ 'radio_cmake_targets',
+ 'radio_include_dependency',
+ 'radio_render_architecture'
+)
+$html = [System.Collections.Generic.List[string]]::new()
+$html.Add('
Radio GraphvizRadio Graphviz
')
+foreach ($item in $items) {
+ $file = $item + '.' + $format
+ if (Test-Path -LiteralPath (Join-Path $outputDir $file)) {
+ $html.Add('- ' + $item + '
')
+ } else {
+ $html.Add('- ' + $item + ' (not generated)
')
+ }
+}
+$html.Add('
')
+Set-Content -LiteralPath (Join-Path $outputDir 'index.html') -Value $html -Encoding UTF8
diff --git a/Graphviz/Cmake_Target_Graph.sh.in b/Graphviz/Cmake_Target_Graph.sh.in
new file mode 100644
index 0000000..307cb9d
--- /dev/null
+++ b/Graphviz/Cmake_Target_Graph.sh.in
@@ -0,0 +1,52 @@
+#!/usr/bin/env bash
+set -euo pipefail
+targets=(@PSC_TARGETS_SH@)
+edges=(@PSC_TARGET_EDGES_SH@)
+output_dir='@PSC_OUTPUT_DIR@'
+format='@PSC_FORMAT@'
+dot_exe='@PSC_DOT_EXECUTABLE@'
+mkdir -p "$output_dir"
+dot_file="$output_dir/radio_cmake_targets.dot"
+image_file="$output_dir/radio_cmake_targets.$format"
+{
+ echo 'digraph "radio cmake_targets" {'
+ echo 'rankdir=LR;'
+ echo 'splines=ortho;'
+ echo 'concentrate=true;'
+ echo 'forcelabels=true;'
+ echo 'graph [fontname="Consolas", label="radio cmake_targets", labelloc=t, fontsize=18];'
+ echo 'node [shape=box, style="rounded,filled", fontname="Consolas", fontsize=10];'
+ echo 'edge [fontname="Consolas", fontsize=9];'
+ for target in "${targets[@]}"; do
+ safe=${target//\"/\\\"}
+ echo "\"$safe\" [label=\"$safe\", tooltip=\"$safe\", fillcolor=\"#dcfce7\"];"
+ done
+ for edge in "${edges[@]}"; do
+ left=${edge%%|*}
+ right=${edge#*|}
+ left=${left//\"/\\\"}
+ right=${right//\"/\\\"}
+ echo "\"$left\" -> \"$right\" [xlabel=\"links\"];"
+ done
+ echo 'subgraph cluster_legend {'
+ echo 'label="legend";'
+ echo 'style="rounded,dashed";'
+ echo '"legend_target" [label="cmake target", fillcolor="#dcfce7"];'
+ echo '"legend_edge" [label="edge = target_link_libraries / target dependency", fillcolor="#f8fafc"];'
+ echo '}'
+ echo '}'
+} > "$dot_file"
+"$dot_exe" "-T$format" "$dot_file" -o "$image_file"
+items=(radio_directory_tree radio_module_dependency radio_cmake_targets radio_include_dependency radio_render_architecture)
+{
+ echo 'Radio GraphvizRadio Graphviz
'
+ for item in "${items[@]}"; do
+ file="$item.$format"
+ if [ -f "$output_dir/$file" ]; then
+ echo "- $item
"
+ else
+ echo "- $item (not generated)
"
+ fi
+ done
+ echo '
'
+} > "$output_dir/index.html"
diff --git a/Graphviz/Graphviz_Render.ps1.in b/Graphviz/Graphviz_Render.ps1.in
new file mode 100644
index 0000000..3dab933
--- /dev/null
+++ b/Graphviz/Graphviz_Render.ps1.in
@@ -0,0 +1,13 @@
+$ErrorActionPreference = 'Stop'
+$inputs = @PSC_INPUT_FILES@
+$outputDir = '@PSC_OUTPUT_DIR@'
+$format = '@PSC_FORMAT@'
+$dotExe = '@PSC_DOT_EXECUTABLE@'
+New-Item -ItemType Directory -Force -Path $outputDir | Out-Null
+foreach ($inputFile in $inputs) {
+ $name = [System.IO.Path]::GetFileNameWithoutExtension($inputFile)
+ & $dotExe ('-T' + $format) $inputFile '-o' (Join-Path $outputDir ($name + '.' + $format))
+ if ($LASTEXITCODE -ne 0) {
+ exit $LASTEXITCODE
+ }
+}
diff --git a/Graphviz/Graphviz_Render.sh.in b/Graphviz/Graphviz_Render.sh.in
new file mode 100644
index 0000000..e4038af
--- /dev/null
+++ b/Graphviz/Graphviz_Render.sh.in
@@ -0,0 +1,12 @@
+#!/usr/bin/env bash
+set -euo pipefail
+inputs=(@PSC_INPUT_FILES_SH@)
+output_dir='@PSC_OUTPUT_DIR@'
+format='@PSC_FORMAT@'
+dot_exe='@PSC_DOT_EXECUTABLE@'
+mkdir -p "$output_dir"
+for input_file in "${inputs[@]}"; do
+ name=$(basename "$input_file")
+ name=${name%.*}
+ "$dot_exe" "-T$format" "$input_file" -o "$output_dir/$name.$format"
+done
diff --git a/Graphviz/Project_Graphs.ps1.in b/Graphviz/Project_Graphs.ps1.in
new file mode 100644
index 0000000..6e028a2
--- /dev/null
+++ b/Graphviz/Project_Graphs.ps1.in
@@ -0,0 +1,339 @@
+$ErrorActionPreference = 'Stop'
+$inputs = @PSC_INPUT_DIRS@
+$excludeDirs = @PSC_EXCLUDE_DIRS@
+$outputDir = '@PSC_OUTPUT_DIR@'
+$format = '@PSC_FORMAT@'
+$dotExe = '@PSC_DOT_EXECUTABLE@'
+$maxDepth = @PSC_MAX_DEPTH@
+$collapseThreshold = @PSC_COLLAPSE_THRESHOLD@
+$excludeThirdParty = @PSC_EXCLUDE_THIRD_PARTY@
+New-Item -ItemType Directory -Force -Path $outputDir | Out-Null
+function Convert-To_Generic_Path([string]$path) {
+ return ([System.IO.Path]::GetFullPath($path)).Replace('\', '/')
+}
+function Convert-To_Dot_Text([string]$text) {
+ return $text.Replace('\', '/').Replace('"', '\"')
+}
+function Get-Root_Name([string]$root) {
+ $path = Convert-To_Generic_Path $root
+ if ($path -match '/module/radio$') {
+ return 'radio'
+ }
+ if ($path -match '/CPP_Core/Core$') {
+ return 'CPP_Core'
+ }
+ if ($path -match '/YSGraphic_Core$') {
+ return 'YSGraphic_Core'
+ }
+ return [System.IO.Path]::GetFileName($path)
+}
+function Get-Relative_Path([string]$root, [string]$path) {
+ $rootPath = Convert-To_Generic_Path $root
+ $fullPath = Convert-To_Generic_Path $path
+ if ($fullPath.StartsWith($rootPath)) {
+ return $fullPath.Substring($rootPath.Length).TrimStart('/')
+ }
+ return $fullPath
+}
+function Test-Excluded_Path([string]$path) {
+ $fullPath = Convert-To_Generic_Path $path
+ if ($excludeThirdParty -and $fullPath.Contains('/third_party/')) {
+ return $true
+ }
+ foreach ($excludeDir in $excludeDirs) {
+ if ($excludeDir.Length -eq 0) {
+ continue
+ }
+ $excludePath = Convert-To_Generic_Path $excludeDir
+ if ($fullPath.StartsWith($excludePath)) {
+ return $true
+ }
+ }
+ return $false
+}
+function Get-Source_Files([string[]]$roots) {
+ $extensions = @('.h', '.hpp', '.hh', '.cpp', '.cxx', '.cc')
+ $files = [System.Collections.Generic.List[object]]::new()
+ foreach ($root in $roots) {
+ if (!(Test-Path -LiteralPath $root)) {
+ continue
+ }
+ Get-ChildItem -LiteralPath $root -Recurse -File | Where-Object {
+ $extensions -contains $_.Extension.ToLowerInvariant() -and !(Test-Excluded_Path $_.FullName)
+ } | ForEach-Object {
+ $files.Add($_)
+ }
+ }
+ return $files
+}
+function Invoke-Dot([string]$name, [System.Collections.Generic.List[string]]$lines) {
+ $dotFile = Join-Path $outputDir ($name + '.dot')
+ $imageFile = Join-Path $outputDir ($name + '.' + $format)
+ Set-Content -LiteralPath $dotFile -Value $lines -Encoding UTF8
+ & $dotExe ('-T' + $format) $dotFile '-o' $imageFile
+ if ($LASTEXITCODE -ne 0) {
+ exit $LASTEXITCODE
+ }
+}
+function Write-Graphviz_Index() {
+ $items = @(
+ 'radio_directory_tree',
+ 'radio_module_dependency',
+ 'radio_cmake_targets',
+ 'radio_include_dependency',
+ 'radio_render_architecture'
+ )
+ $html = [System.Collections.Generic.List[string]]::new()
+ $html.Add('Radio GraphvizRadio Graphviz
')
+ foreach ($item in $items) {
+ $file = $item + '.' + $format
+ if (Test-Path -LiteralPath (Join-Path $outputDir $file)) {
+ $html.Add('- ' + $item + '
')
+ } else {
+ $html.Add('- ' + $item + ' (not generated)
')
+ }
+ }
+ $html.Add('
')
+ Set-Content -LiteralPath (Join-Path $outputDir 'index.html') -Value $html -Encoding UTF8
+}
+function Add-Header([System.Collections.Generic.List[string]]$lines, [string]$title) {
+ $lines.Add('digraph "' + (Convert-To_Dot_Text $title) + '" {')
+ $lines.Add('rankdir=LR;')
+ $lines.Add('splines=ortho;')
+ $lines.Add('concentrate=true;')
+ $lines.Add('forcelabels=true;')
+ $lines.Add('graph [fontname="Consolas", label="' + (Convert-To_Dot_Text $title) + '", labelloc=t, fontsize=18];')
+ $lines.Add('node [shape=box, style="rounded,filled", fontname="Consolas", fontsize=10];')
+ $lines.Add('edge [fontname="Consolas", fontsize=9];')
+}
+function Add-Legend([System.Collections.Generic.List[string]]$lines, [string]$mode) {
+ $lines.Add('subgraph cluster_legend {')
+ $lines.Add('label="legend";')
+ $lines.Add('style="rounded,dashed";')
+ $lines.Add('"legend_module" [label="module/folder", fillcolor="#dbeafe", tooltip="聚合模块或目录节点"];')
+ $lines.Add('"legend_target" [label="cmake target", fillcolor="#dcfce7", tooltip="CMake target 节点"];')
+ $lines.Add('"legend_buffer" [label="buffer/cache", fillcolor="#fef3c7", tooltip="缓冲区或长期缓存"];')
+ $lines.Add('"legend_runtime" [label="runtime", fillcolor="#fee2e2", tooltip="运行期任务或 Qt 绘制入口"];')
+ if ($mode -eq 'include') {
+ $lines.Add('"legend_edge" [label="include:N node = include count", fillcolor="#f8fafc"];')
+ } else {
+ $lines.Add('"legend_edge" [label="edge label = relation", fillcolor="#f8fafc"];')
+ }
+ $lines.Add('}')
+}
+function Get-Immediate_Source_Count([string]$dir) {
+ $extensions = @('.h', '.hpp', '.hh', '.cpp', '.cxx', '.cc')
+ return @(Get-ChildItem -LiteralPath $dir -File | Where-Object {
+ $extensions -contains $_.Extension.ToLowerInvariant()
+ }).Count
+}
+function Get-Visible_Directories([string]$dir) {
+ return @(Get-ChildItem -LiteralPath $dir -Directory | Where-Object {
+ !(Test-Excluded_Path $_.FullName)
+ })
+}
+function Add-Directory_Node([System.Collections.Generic.List[string]]$lines, [string]$root, [string]$dir, [int]$depth, [string]$parentId) {
+ if ($depth -gt $maxDepth -or (Test-Excluded_Path $dir)) {
+ return
+ }
+ $rootName = Get-Root_Name $root
+ $relative = Get-Relative_Path $root $dir
+ $name = if ($relative.Length -eq 0) { $rootName } else { [System.IO.Path]::GetFileName($dir) }
+ $idPath = if ($relative.Length -eq 0) { $rootName } else { $rootName + '/' + $relative }
+ $id = 'dir:' + $idPath
+ $fileCount = Get-Immediate_Source_Count $dir
+ $children = Get-Visible_Directories $dir
+ $label = (Convert-To_Dot_Text $name) + '\nfiles: ' + $fileCount + '\ndirs: ' + $children.Count
+ $tooltip = Convert-To_Dot_Text $idPath
+ $lines.Add('"' + $id + '" [label="' + $label + '", tooltip="' + $tooltip + '", fillcolor="#dbeafe"];')
+ if ($parentId.Length -gt 0) {
+ $lines.Add('"' + $parentId + '" -> "' + $id + '" [xlabel="contains"];')
+ }
+ if ($depth -eq $maxDepth) {
+ return
+ }
+ if ($fileCount -gt $collapseThreshold -and $depth -gt 0) {
+ return
+ }
+ foreach ($child in $children) {
+ Add-Directory_Node $lines $root $child.FullName ($depth + 1) $id
+ }
+}
+function New-Directory_Tree_Graph() {
+ $lines = [System.Collections.Generic.List[string]]::new()
+ Add-Header $lines 'radio directory_tree'
+ foreach ($root in $inputs) {
+ if (!(Test-Path -LiteralPath $root)) {
+ continue
+ }
+ $rootName = Get-Root_Name $root
+ $clusterName = $rootName.Replace('-', '_').Replace('/', '_')
+ $lines.Add('subgraph "cluster_' + $clusterName + '" {')
+ $lines.Add('label="' + (Convert-To_Dot_Text $rootName) + '";')
+ $lines.Add('style="rounded";')
+ Add-Directory_Node $lines $root $root 0 ''
+ $lines.Add('}')
+ }
+ Add-Legend $lines 'directory'
+ $lines.Add('}')
+ Invoke-Dot 'radio_directory_tree' $lines
+}
+function Get-Root_For_File([string]$path) {
+ $fullPath = Convert-To_Generic_Path $path
+ foreach ($root in $inputs) {
+ $rootPath = Convert-To_Generic_Path $root
+ if ($fullPath.StartsWith($rootPath)) {
+ return $root
+ }
+ }
+ return ''
+}
+function Get-Module_Key([string]$path, [int]$depth) {
+ $root = Get-Root_For_File $path
+ if ($root.Length -eq 0) {
+ return ''
+ }
+ $rootName = Get-Root_Name $root
+ $relative = Get-Relative_Path $root $path
+ $parts = @($relative.Split('/') | Where-Object { $_.Length -gt 0 })
+ if ($rootName -eq 'radio') {
+ $base = 'module/radio'
+ } elseif ($rootName -eq 'CPP_Core') {
+ $base = 'third_party/CPP_Core/Core'
+ } elseif ($rootName -eq 'YSGraphic_Core') {
+ $base = 'third_party/Graphic_Core/YSGraphic_Core'
+ } else {
+ $base = $rootName
+ }
+ if ($parts.Count -eq 0 -or $depth -eq 0) {
+ return $base
+ }
+ $take = [Math]::Min($depth, $parts.Count)
+ return $base + '/' + (($parts | Select-Object -First $take) -join '/')
+}
+function Test-Ignored_Include([string]$includeText) {
+ $text = $includeText.Trim()
+ if ($text -match '^(Q[A-ZA-z0-9_]*|Qt[A-ZA-z0-9_/]*|QObject|QWidget|QString|QVector|QMap|QList)$') {
+ return $true
+ }
+ if ($text -match '^(array|atomic|chrono|cmath|cstdint|cstdio|cstdlib|cstring|filesystem|functional|limits|map|memory|mutex|optional|set|span|string|string_view|thread|tuple|type_traits|unordered_map|utility|vector)$') {
+ return $true
+ }
+ if ($text -match '^(boost/|asio|windows\.h|winsock|d3d|GL/)') {
+ return $true
+ }
+ return $false
+}
+function Resolve-Include_Path([string]$includeText, [hashtable]$suffixMap, [hashtable]$nameMap) {
+ $key = $includeText.Replace('\', '/').ToLowerInvariant()
+ if ($suffixMap.ContainsKey($key)) {
+ return $suffixMap[$key]
+ }
+ $leaf = [System.IO.Path]::GetFileName($key)
+ if ($nameMap.ContainsKey($leaf)) {
+ return $nameMap[$leaf]
+ }
+ return ''
+}
+function Add-Dependency_Graph([string]$name, [string]$title, [int]$sourceDepth, [int]$targetDepth) {
+ $files = Get-Source_Files $inputs
+ $suffixMap = @{}
+ $nameMap = @{}
+ foreach ($file in $files) {
+ $root = Get-Root_For_File $file.FullName
+ $relative = (Get-Relative_Path $root $file.FullName).ToLowerInvariant()
+ $leaf = $file.Name.ToLowerInvariant()
+ $suffixMap[$relative] = $file.FullName
+ if (!$nameMap.ContainsKey($leaf)) {
+ $nameMap[$leaf] = $file.FullName
+ }
+ }
+ $edges = @{}
+ foreach ($file in $files) {
+ $sourceKey = Get-Module_Key $file.FullName $sourceDepth
+ if ($sourceKey.Length -eq 0) {
+ continue
+ }
+ Get-Content -LiteralPath $file.FullName | ForEach-Object {
+ if ($_ -match '^\s*#\s*include\s*[<"]([^">]+)[">]') {
+ $includeText = $Matches[1]
+ if (Test-Ignored_Include $includeText) {
+ return
+ }
+ $targetFile = Resolve-Include_Path $includeText $suffixMap $nameMap
+ if ($targetFile.Length -eq 0) {
+ return
+ }
+ $targetKey = Get-Module_Key $targetFile $targetDepth
+ if ($targetKey.Length -eq 0 -or $targetKey -eq $sourceKey) {
+ return
+ }
+ $edgeKey = $sourceKey + '|' + $targetKey
+ if (!$edges.ContainsKey($edgeKey)) {
+ $edges[$edgeKey] = 0
+ }
+ $edges[$edgeKey]++
+ }
+ }
+ }
+ $nodes = [System.Collections.Generic.HashSet[string]]::new()
+ $lines = [System.Collections.Generic.List[string]]::new()
+ Add-Header $lines $title
+ foreach ($edgeKey in $edges.Keys) {
+ $parts = $edgeKey.Split('|')
+ $nodes.Add($parts[0]) | Out-Null
+ $nodes.Add($parts[1]) | Out-Null
+ }
+ foreach ($node in $nodes) {
+ $label = Convert-To_Dot_Text ([System.IO.Path]::GetFileName($node))
+ $tooltip = Convert-To_Dot_Text $node
+ $lines.Add('"' + $node + '" [label="' + $label + '", tooltip="' + $tooltip + '", fillcolor="#dbeafe"];')
+ }
+ $edgeIndex = 0
+ foreach ($edgeKey in $edges.Keys) {
+ $parts = $edgeKey.Split('|')
+ $countNode = 'include_count:' + $edgeIndex
+ $lines.Add('"' + $countNode + '" [label="include: ' + $edges[$edgeKey] + '", tooltip="' + (Convert-To_Dot_Text $edgeKey) + '", fillcolor="#f8fafc"];')
+ $lines.Add('"' + $parts[0] + '" -> "' + $countNode + '";')
+ $lines.Add('"' + $countNode + '" -> "' + $parts[1] + '";')
+ $edgeIndex++
+ }
+ Add-Legend $lines 'include'
+ $lines.Add('}')
+ Invoke-Dot $name $lines
+}
+function New-Render_Architecture_Graph() {
+ $lines = [System.Collections.Generic.List[string]]::new()
+ Add-Header $lines 'radio render_architecture'
+ $lines.Add('"Plot" [label="Plot", fillcolor="#dbeafe"];')
+ $lines.Add('"Renderable" [label="Renderable", fillcolor="#dbeafe"];')
+ $lines.Add('"Render_Data" [label="Render_Data", fillcolor="#dcfce7"];')
+ $lines.Add('"Render_State" [label="Render_State", fillcolor="#dcfce7"];')
+ $lines.Add('"Input_Data" [label="Input_Data", fillcolor="#dcfce7"];')
+ $lines.Add('"Render_Pipeline" [label="Render_Pipeline", fillcolor="#dcfce7"];')
+ $lines.Add('"State_Buffer" [label="State_Buffer[3]", fillcolor="#fef3c7"];')
+ $lines.Add('"Input_Buffer" [label="Input_Buffer[3]", fillcolor="#fef3c7"];')
+ $lines.Add('"Render_Cache" [label="Render_Cache", fillcolor="#fef3c7"];')
+ $lines.Add('"Color_Buffer" [label="Color_Buffer[3]", fillcolor="#fef3c7"];')
+ $lines.Add('"CPU_Render_Task" [label="CPU Render Task", fillcolor="#fee2e2"];')
+ $lines.Add('"Qt_paintEvent" [label="Qt paintEvent", fillcolor="#fee2e2"];')
+ $lines.Add('"Plot" -> "Renderable" [xlabel="owns/schedules"];')
+ $lines.Add('"Renderable" -> "Render_Data" [xlabel="owns"];')
+ $lines.Add('"Render_Data" -> "Render_State" [xlabel="state snapshot"];')
+ $lines.Add('"Render_Data" -> "Input_Data" [xlabel="input batch"];')
+ $lines.Add('"Render_Data" -> "Render_Cache" [xlabel="long-lived cache"];')
+ $lines.Add('"Renderable" -> "Render_Pipeline" [xlabel="submit/finish/publish"];')
+ $lines.Add('"Render_Pipeline" -> "State_Buffer" [xlabel="edit/ready/render"];')
+ $lines.Add('"Render_Pipeline" -> "Input_Buffer" [xlabel="write/ready/render"];')
+ $lines.Add('"Render_Pipeline" -> "CPU_Render_Task" [xlabel="dispatch"];')
+ $lines.Add('"CPU_Render_Task" -> "Color_Buffer" [xlabel="write render color"];')
+ $lines.Add('"Color_Buffer" -> "Qt_paintEvent" [xlabel="ready/front consume"];')
+ Add-Legend $lines 'architecture'
+ $lines.Add('}')
+ Invoke-Dot 'radio_render_architecture' $lines
+}
+New-Directory_Tree_Graph
+Add-Dependency_Graph 'radio_module_dependency' 'radio module_dependency' 0 0
+Add-Dependency_Graph 'radio_include_dependency' 'radio include_dependency' 1 1
+New-Render_Architecture_Graph
+Write-Graphviz_Index
diff --git a/Graphviz/Project_Graphs.sh.in b/Graphviz/Project_Graphs.sh.in
new file mode 100644
index 0000000..0863334
--- /dev/null
+++ b/Graphviz/Project_Graphs.sh.in
@@ -0,0 +1,156 @@
+#!/usr/bin/env bash
+set -euo pipefail
+inputs=(@PSC_INPUT_DIRS_SH@)
+output_dir='@PSC_OUTPUT_DIR@'
+format='@PSC_FORMAT@'
+dot_exe='@PSC_DOT_EXECUTABLE@'
+max_depth=@PSC_MAX_DEPTH@
+collapse_threshold=@PSC_COLLAPSE_THRESHOLD@
+mkdir -p "$output_dir"
+normalize_path() {
+ local path="$1"
+ if command -v realpath >/dev/null 2>&1; then
+ realpath -m "$path" | tr '\\' '/'
+ else
+ printf '%s\n' "$path" | tr '\\' '/'
+ fi
+}
+dot_text() {
+ printf '%s' "$1" | sed 's/\\/\\//g; s/"/\\"/g'
+}
+root_name() {
+ local path
+ path=$(normalize_path "$1")
+ case "$path" in
+ */module/radio) printf 'radio' ;;
+ */CPP_Core/Core) printf 'CPP_Core' ;;
+ */YSGraphic_Core) printf 'YSGraphic_Core' ;;
+ *) basename "$path" ;;
+ esac
+}
+write_header() {
+ local title="$1"
+ echo "digraph \"$title\" {"
+ echo 'rankdir=LR;'
+ echo 'splines=ortho;'
+ echo 'concentrate=true;'
+ echo 'forcelabels=true;'
+ echo "graph [fontname=\"Consolas\", label=\"$title\", labelloc=t, fontsize=18];"
+ echo 'node [shape=box, style="rounded,filled", fontname="Consolas", fontsize=10];'
+ echo 'edge [fontname="Consolas", fontsize=9];'
+}
+write_legend() {
+ echo 'subgraph cluster_legend {'
+ echo 'label="legend";'
+ echo 'style="rounded,dashed";'
+ echo '"legend_module" [label="module/folder", fillcolor="#dbeafe"];'
+ echo '"legend_target" [label="cmake target", fillcolor="#dcfce7"];'
+ echo '"legend_buffer" [label="buffer/cache", fillcolor="#fef3c7"];'
+ echo '"legend_runtime" [label="runtime", fillcolor="#fee2e2"];'
+ echo '"legend_edge" [label="edge label = relation/count", fillcolor="#f8fafc"];'
+ echo '}'
+}
+render_dot() {
+ local name="$1"
+ "$dot_exe" "-T$format" "$output_dir/$name.dot" -o "$output_dir/$name.$format"
+}
+write_graphviz_index() {
+ local items=(radio_directory_tree radio_module_dependency radio_cmake_targets radio_include_dependency radio_render_architecture)
+ {
+ echo 'Radio GraphvizRadio Graphviz
'
+ for item in "${items[@]}"; do
+ local file="$item.$format"
+ if [ -f "$output_dir/$file" ]; then
+ echo "- $item
"
+ else
+ echo "- $item (not generated)
"
+ fi
+ done
+ echo '
'
+ } > "$output_dir/index.html"
+}
+write_directory_tree() {
+ local dot="$output_dir/radio_directory_tree.dot"
+ {
+ write_header 'radio directory_tree'
+ for root in "${inputs[@]}"; do
+ [ -d "$root" ] || continue
+ local root_label
+ root_label=$(root_name "$root")
+ echo "subgraph \"cluster_$root_label\" {"
+ echo "label=\"$root_label\";"
+ echo 'style="rounded";'
+ find "$root" -maxdepth "$max_depth" -type d | while read -r dir; do
+ local rel name id files dirs parent parent_id
+ rel=${dir#"$root"}
+ rel=${rel#/}
+ name=$(basename "$dir")
+ [ -z "$rel" ] && name="$root_label"
+ id="$root_label${rel:+/$rel}"
+ files=$(find "$dir" -maxdepth 1 -type f \( -name '*.h' -o -name '*.hpp' -o -name '*.hh' -o -name '*.cpp' -o -name '*.cxx' -o -name '*.cc' \) | wc -l)
+ dirs=$(find "$dir" -maxdepth 1 -mindepth 1 -type d | wc -l)
+ echo "\"dir:$id\" [label=\"$(dot_text "$name")\\nfiles: $files\\ndirs: $dirs\", tooltip=\"$(dot_text "$id")\", fillcolor=\"#dbeafe\"];"
+ if [ -n "$rel" ]; then
+ parent=$(dirname "$rel")
+ if [ "$parent" = "." ]; then
+ parent_id="$root_label"
+ else
+ parent_id="$root_label/$parent"
+ fi
+ echo "\"dir:$parent_id\" -> \"dir:$id\" [xlabel=\"contains\"];"
+ fi
+ done
+ echo '}'
+ done
+ write_legend
+ echo '}'
+ } > "$dot"
+ render_dot 'radio_directory_tree'
+}
+write_empty_dependency() {
+ local name="$1"
+ local title="$2"
+ {
+ write_header "$title"
+ echo '"module_dependency_note" [label="include dependency aggregation is generated by the PowerShell script on Windows", fillcolor="#dbeafe"];'
+ write_legend
+ echo '}'
+ } > "$output_dir/$name.dot"
+ render_dot "$name"
+}
+write_render_architecture() {
+ {
+ write_header 'radio render_architecture'
+ echo '"Plot" [label="Plot", fillcolor="#dbeafe"];'
+ echo '"Renderable" [label="Renderable", fillcolor="#dbeafe"];'
+ echo '"Render_Data" [label="Render_Data", fillcolor="#dcfce7"];'
+ echo '"Render_State" [label="Render_State", fillcolor="#dcfce7"];'
+ echo '"Input_Data" [label="Input_Data", fillcolor="#dcfce7"];'
+ echo '"Render_Pipeline" [label="Render_Pipeline", fillcolor="#dcfce7"];'
+ echo '"State_Buffer" [label="State_Buffer[3]", fillcolor="#fef3c7"];'
+ echo '"Input_Buffer" [label="Input_Buffer[3]", fillcolor="#fef3c7"];'
+ echo '"Render_Cache" [label="Render_Cache", fillcolor="#fef3c7"];'
+ echo '"Color_Buffer" [label="Color_Buffer[3]", fillcolor="#fef3c7"];'
+ echo '"CPU_Render_Task" [label="CPU Render Task", fillcolor="#fee2e2"];'
+ echo '"Qt_paintEvent" [label="Qt paintEvent", fillcolor="#fee2e2"];'
+ echo '"Plot" -> "Renderable" [xlabel="owns/schedules"];'
+ echo '"Renderable" -> "Render_Data" [xlabel="owns"];'
+ echo '"Render_Data" -> "Render_State" [xlabel="state snapshot"];'
+ echo '"Render_Data" -> "Input_Data" [xlabel="input batch"];'
+ echo '"Render_Data" -> "Render_Cache" [xlabel="long-lived cache"];'
+ echo '"Renderable" -> "Render_Pipeline" [xlabel="submit/finish/publish"];'
+ echo '"Render_Pipeline" -> "State_Buffer" [xlabel="edit/ready/render"];'
+ echo '"Render_Pipeline" -> "Input_Buffer" [xlabel="write/ready/render"];'
+ echo '"Render_Pipeline" -> "CPU_Render_Task" [xlabel="dispatch"];'
+ echo '"CPU_Render_Task" -> "Color_Buffer" [xlabel="write render color"];'
+ echo '"Color_Buffer" -> "Qt_paintEvent" [xlabel="ready/front consume"];'
+ write_legend
+ echo '}'
+ } > "$output_dir/radio_render_architecture.dot"
+ render_dot 'radio_render_architecture'
+}
+write_directory_tree
+write_empty_dependency 'radio_module_dependency' 'radio module_dependency'
+write_empty_dependency 'radio_include_dependency' 'radio include_dependency'
+write_render_architecture
+write_graphviz_index
diff --git a/Graphviz/export.cmake b/Graphviz/export.cmake
new file mode 100644
index 0000000..44e4407
--- /dev/null
+++ b/Graphviz/export.cmake
@@ -0,0 +1 @@
+include(${CMAKE_CURRENT_LIST_DIR}/main.cmake)