hgg-core-0.2.0.0: src/Graphics/Hgg/Spec/Setters.hs
-- |
-- Module : Graphics.Hgg.Spec.Setters
-- Description : Top-level setters that build VisualSpec (title / theme / axis / legend / annot etc.)
-- Copyright : (c) 2026 Aelysce Project (Toshiaki Honda)
-- License : BSD-3-Clause
--
-- [日本語]: 'Graphics.Hgg.Spec' の module 分割で切り出し。 'VisualSpec' を
-- `<>` で組み立てる top-level setter 群 ('layer' / 'title' / 'theme' /
-- 'facet' 系 / 'legend' 系 / @annot@ 系 / inset / 図サイズ / font setter 等) と
-- 'Labs'、 VisualSpec 依存の mark 構築子 3 種 ('histogramWide' / 'distCols' /
-- 'ridgeAutoFlip') を持つ。 図の合成演算子は 'Graphics.Hgg.Spec.Concat' 側。
-- 公開 API は従来どおり 'Graphics.Hgg.Spec' (facade) が re-export する。
-- 挙動・出力は完全に不変。
-- [English]: Split out of 'Graphics.Hgg.Spec' as part of its module split.
-- Holds the top-level setters that build 'VisualSpec' via `<>` ('layer' /
-- 'title' / 'theme' / the 'facet' family / the 'legend' family / the
-- @annot@ family / inset / figure size / font setters, etc.), 'Labs', and
-- the three VisualSpec-dependent mark constructors ('histogramWide' /
-- 'distCols' / 'ridgeAutoFlip'). Figure-composition operators live in
-- 'Graphics.Hgg.Spec.Concat'. The public API is unchanged:
-- 'Graphics.Hgg.Spec' (the facade) still re-exports everything. Behavior
-- and output are fully unchanged.
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
module Graphics.Hgg.Spec.Setters
( -- * layer 装着 + 基本 setter
layer, layers, purePlot, title, subtitle, caption, tag, xLabel, yLabel, zLabel
, Labs(..), labs, emptyLabs
, theme, facet, facetWrap, facetGrid, facetCols, facetScales, facetSpace
, subplots, subplotCols, subplotWidths, subplotHeights, subplotTags
, repeatFields, selectPanels, selectedSubplots
, scaleXDiscreteLimits, scaleYDiscreteLimits, applyDiscreteLimits, reindexLayer
-- * theme override setter
, plotBg, themePlotBg, panelFill, panelBorder, gridColor, themeGrid, themeAxisLine
, themeGridMajor, themeGridMinor, themeLegendPos
, themeTickLength, themeTickDir, themePlotMargin, themeBaseFontSize
, themeGridWidth, themeGridMinorWidth, themeAxisLineWidth
, themeAxisText, themeAxisTitle, themeLegendKeySize
, axisColor, textColor, tickColor, titleColor, titleHjust
, stripFill, themeStrip, legendKeyBg
, themeTitleFont, themeAxisLabelFont, themeTickFont, themeLegendFont
, themeFontFamily
, themeAxisTextAngle, themeAxisTextAngleX, themeAxisTextAngleY
, axisTextAngleXOf, axisTextAngleYOf
-- * 合成 preset (cowplot 風)
, themeCowplot, themeMinimalGrid, themeMap
, themeCowplotSized, themeMinimalGridSized, themeMapSized
-- * VisualSpec 依存の mark 構築子 (Constructors に置けない 3 種)
, histogramWide, distCols, ridgeAutoFlip
-- * 軸 / 凡例 / 装飾 / 座標系 / サイズ
, xAxis, yAxis, yAxisRight, toLeftY, toRightY
, legend, legendPos, legendOff, legendTitle, legendReverse, legendNcol, legendNrow
, guideColorNone
, refLine, refVertical, refHorizontal, refIdentity
, annotate, annotText, annotTextP, annotLine, annotLineP
, annotRect, annotRectP, annotArrow, annotArrowP
, inset, insetAt, insetElement
, marginal, marginalX, marginalY
, palette, paletteGGplot, continuousPalette
, scaleColorManual, scaleColorGradient2, scaleSize
, coordFlip, coordPolar, coordPolarY, coordPolarWith, coordPolarYWith, coordTernary
, coordTernaryWith -- ★ Phase 69 A4
, coordCartesian, coordCartesianX, coordCartesianY
, reverseX, reverseY, aspectRatio
, width, height, widthUnit, heightUnit, widthMm, heightMm, dpi
-- * font setter
, titleFont, axisLabelFont, tickFont, legendFont
) where
import Data.Maybe (catMaybes)
import Data.Monoid (First (..), Last (..))
import Data.Text (Text)
import Data.Vector (Vector)
import qualified Data.Vector as V
import Graphics.Hgg.Unit (Length, Pos (..), mm, (*~))
import Graphics.Hgg.Spec.Axis (AxisSpec)
import Graphics.Hgg.Spec.Column
import Graphics.Hgg.Spec.Bake (bakeSpec)
import Graphics.Hgg.Spec.Constructors (binCount, histogram, (<+>))
import Graphics.Hgg.Spec.Decoration
import Graphics.Hgg.Spec.Layer
import Graphics.Hgg.Spec.Mark
import Graphics.Hgg.Spec.Theme (Margin (..), ThemeName (..),
ThemeOverride (..), TickDir)
import Graphics.Hgg.Spec.Visual
-- ===========================================================================
-- Top-level setters
-- ===========================================================================
-- | [日本語]: spec の純粋値起点 (= 'mempty' alias)。 mempty 直接でも良いが、
-- 「これは plot spec の最初の値ですよ」 という意図を名前で示す。 副作用関数
-- (@plot@ / @saveSVG@ 等) との対比で `pure-` prefix。
-- [English]: The pure starting value for a spec (an alias for
-- 'mempty'). Using mempty directly would also work, but the name
-- signals the intent "this is the initial value of a plot spec" — the
-- `pure-` prefix contrasts with effectful functions (@plot@ /
-- @saveSVG@, etc.).
purePlot :: VisualSpec
purePlot = mempty
-- | [日本語]: 'Layer' を 'VisualSpec' に lift (= layer リストの単一要素
-- spec)。
-- [English]: Lifts a 'Layer' into a 'VisualSpec' (a spec with a
-- single-element layer list).
layer :: Layer -> VisualSpec
layer l = mempty { vsLayers = [l] }
-- | [日本語]: 'layer' のリスト版 (= @layer . mconcat@)。 hvega 風のリスト書きが
-- 好みの場合に: @layers [scatter "x" "y", colorBy "group"] =
-- layer (scatter "x" "y" <> colorBy "group")@。 等価な別名であり '<>' 版が正典
-- (doc の例は '<>' で統一)。 外部フィードバック (公開版 hgg へのコメント) を
-- 受けて追加した。
-- [English]: A list version of 'layer' (@layer . mconcat@). For those
-- who prefer hvega-style list syntax:
-- @layers [scatter "x" "y", colorBy "group"] =
-- layer (scatter "x" "y" <> colorBy "group")@. An equivalent alias — the
-- '<>' form is canonical (doc examples stick to '<>'). Added in response
-- to external feedback (comments on the public hgg release).
layers :: [Layer] -> VisualSpec
layers = layer . mconcat
title, xLabel, yLabel, zLabel :: Text -> VisualSpec
title t = mempty { vsTitle = Last (Just t) }
xLabel t = mempty { vsXLabel = Last (Just t) }
yLabel t = mempty { vsYLabel = Last (Just t) }
-- ★ Phase 64 A11: 三角座標 (ternary) 第 3 軸 (encZ 成分) のタイトル。
zLabel t = mempty { vsZLabel = Last (Just t) }
-- | [日本語]: 凡例タイトル (= ggplot scale_color_*(name=) / labs(color=))。
-- color/fill/shape/linetype の凡例ヘッダに表示。 軸タイトルは
-- 'xLabel'/'yLabel' を使う (= positional scale の name = 軸ラベル)。
-- [English]: The legend title (like ggplot's scale_color_*(name=) /
-- labs(color=)). Shown as the header of the color/fill/shape/linetype
-- legend. Use 'xLabel'/'yLabel' for axis titles (a positional scale's
-- name is the axis label).
legendTitle :: Text -> VisualSpec
legendTitle t = mempty { vsLegendTitle = Last (Just t) }
-- | [日本語]: labs サブシステムの個別 setter (= ggplot
-- labs(subtitle=,caption=,tag=))。 'subtitle' = title 直下の小見出し、
-- 'caption' = 図右下の注記、 'tag' = 左上隅のタグ。
-- [English]: Individual setters in the labs subsystem (like ggplot's
-- labs(subtitle=,caption=,tag=)). 'subtitle' is the sub-heading right
-- under the title, 'caption' is the note in the bottom-right corner,
-- and 'tag' is the tag in the top-left corner.
subtitle, caption, tag :: Text -> VisualSpec
subtitle t = mempty { vsSubtitle = Last (Just t) }
caption t = mempty { vsCaption = Last (Just t) }
tag t = mempty { vsTag = Last (Just t) }
-- | [日本語]: ggplot @labs()@ 相当のまとめ setter。 各フィールドは 'Maybe' で
-- 「指定しない」 を表す。 @labs emptyLabs { labsTitle = Just "T", labsX = Just "x" }@
-- のように 'emptyLabs' を起点に必要な label だけ埋める。 @labsColor@ は凡例
-- タイトル ('legendTitle')。 指定した label を 'mconcat' で合成するので既存
-- setter と等価。
-- [English]: A bundled setter equivalent to ggplot's @labs()@. Each
-- field is a 'Maybe' representing "not specified". Start from
-- 'emptyLabs' and fill only the labels you need, e.g.
-- @labs emptyLabs { labsTitle = Just "T", labsX = Just "x" }@.
-- @labsColor@ is the legend title ('legendTitle'). The specified labels
-- are combined with 'mconcat', so this is equivalent to the individual
-- setters.
data Labs = Labs
{ labsTitle :: Maybe Text
, labsSubtitle :: Maybe Text
, labsCaption :: Maybe Text
, labsTag :: Maybe Text
, labsX :: Maybe Text
, labsY :: Maybe Text
, labsColor :: Maybe Text -- = 凡例タイトル ('legendTitle')
} deriving (Show, Eq)
-- | [日本語]: 全フィールド未指定の 'Labs' 起点 (= record update のベース)。
-- [English]: The starting 'Labs' value with every field unspecified
-- (the base for record updates).
emptyLabs :: Labs
emptyLabs = Labs Nothing Nothing Nothing Nothing Nothing Nothing Nothing
labs :: Labs -> VisualSpec
labs lb = mconcat $ catMaybes
[ title <$> labsTitle lb
, subtitle <$> labsSubtitle lb
, caption <$> labsCaption lb
, tag <$> labsTag lb
, xLabel <$> labsX lb
, yLabel <$> labsY lb
, legendTitle <$> labsColor lb
]
theme :: ThemeName -> VisualSpec
theme t = mempty { vsTheme = Last (Just t) }
-- | [日本語]: element 単位 theme override の setter 群 (ggplot theme(element_*)
-- 相当)。 `theme ThemeGrey <> themeGrid False <> panelFill "#fafafa"` のように
-- `<>` で重ねる。
-- [English]: A family of per-element theme-override setters (like
-- ggplot's theme(element_*)). Stack them with `<>`, e.g.
-- `theme ThemeGrey <> themeGrid False <> panelFill "#fafafa"`.
themeGrid :: Bool -> VisualSpec -- panel.grid on/off (= major/minor 両方の糖衣)
themeGrid b = mempty { vsThemeOverride = mempty { toShowGrid = Last (Just b) } }
-- | [日本語]: grid major/minor の個別 on/off (cowplot @theme_minimal_grid()@
-- 等)。 優先順は 個別 > 一括 'themeGrid' > preset。
-- `theme ThemeMinimal <> themeGridMinor False` のように重ねる。
-- [English]: Individual on/off for grid major/minor (like cowplot's
-- @theme_minimal_grid()@). Priority is individual > the bulk
-- 'themeGrid' > preset. Stack it like
-- `theme ThemeMinimal <> themeGridMinor False`.
themeGridMajor :: Bool -> VisualSpec -- panel.grid.major on/off
themeGridMajor b = mempty { vsThemeOverride = mempty { toShowGridMajor = Last (Just b) } }
themeGridMinor :: Bool -> VisualSpec -- panel.grid.minor on/off
themeGridMinor b = mempty { vsThemeOverride = mempty { toShowGridMinor = Last (Just b) } }
-- | [日本語]: legend.position を theme に焼き込む (自作 theme を `<>` で配る用)。
-- 図レベルの 'legendPos' が指定されていればそちらが優先 (ggplot の theme() と
-- 個別指定の関係に同じ)。
-- [English]: Bakes legend.position into the theme (for distributing a
-- custom theme via `<>`). If the figure-level 'legendPos' is set, it
-- takes priority (the same relationship as ggplot's theme() vs. an
-- individual setting).
themeLegendPos :: LegendPosition -> VisualSpec
themeLegendPos p = mempty { vsThemeOverride = mempty { toLegendPos = Last (Just p) } }
-- | [日本語]: 軸目盛線の長さ (pt) を theme に焼き込む (ggplot
-- @axis.ticks.length@ 相当、 既定 2.75pt)。 tick 長は軸ラベル位置・マージン
-- 予約にも波及する (computeLayout が実効値を参照)。
-- [English]: Bakes the axis tick length (pt) into the theme (like
-- ggplot's @axis.ticks.length@, default 2.75pt). The tick length also
-- affects axis-label position and margin reservation (computeLayout
-- reads the effective value).
themeTickLength :: Double -> VisualSpec
themeTickLength d = mempty { vsThemeOverride = mempty { toTickLength = Last (Just d) } }
-- | [日本語]: ★ Phase 68: grid 線 (major・全 grid の base) の線幅 (pt) を theme に
-- 焼き込む (ggplot @theme(panel.grid = element_line(linewidth=))@ 相当)。 指定すると
-- Cartesian major に加え polar / ternary の grid も同じ幅に統一される (panel.grid は
-- 座標系非依存)。 未指定時は各座標系の現状値 (Cartesian major 1.0 / polar・ternary 0.5)。
-- minor は 'themeGridMinorWidth' 未指定なら自動で major × 0.5 に追従する。
-- [English]: ★ Phase 68: bakes the grid line width (pt) into the theme (like
-- ggplot's @theme(panel.grid = element_line(linewidth=))@). Setting it unifies
-- the Cartesian major with the polar / ternary grid at the same width
-- (panel.grid is coord-independent). When unspecified each coord keeps its
-- current literal (Cartesian major 1.0 / polar & ternary 0.5). The minor grid
-- follows major × 0.5 automatically unless 'themeGridMinorWidth' is set.
themeGridWidth :: Double -> VisualSpec
themeGridWidth d = mempty { vsThemeOverride = mempty { toGridWidth = Last (Just d) } }
-- | [日本語]: ★ Phase 68: Cartesian minor grid の線幅 (pt) を独立に上書きする (ggplot
-- @panel.grid.minor = element_line(linewidth=)@)。 未指定時は 'themeGridWidth'
-- (major) × 0.5 (ggplot @rel(0.5)@ 準拠・既定 0.5)。
-- [English]: ★ Phase 68: overrides the Cartesian minor grid width (pt)
-- independently (ggplot @panel.grid.minor = element_line(linewidth=)@).
-- When unspecified it is 'themeGridWidth' (major) × 0.5 (ggplot @rel(0.5)@,
-- default 0.5).
themeGridMinorWidth :: Double -> VisualSpec
themeGridMinorWidth d = mempty { vsThemeOverride = mempty { toGridMinorWidth = Last (Just d) } }
-- | [日本語]: ★ Phase 68: 軸線の線幅 (pt) を theme に焼き込む (ggplot @axis.line@ /
-- @panel.border@ 相当)。 axis.line (下辺/左辺)・panel border・ternary の三辺・
-- 右 Y 軸線を統一して太らせる。 未指定時は現状 1.0。 tick mark は対象外
-- (ggplot @axis.ticks@ = 別 element)。
-- [English]: ★ Phase 68: bakes the axis line width (pt) into the theme (like
-- ggplot's @axis.line@ / @panel.border@). Widens the axis.line (bottom/left),
-- panel border, ternary edges and the right Y axis uniformly. Defaults to the
-- current 1.0 when unspecified. Tick marks are excluded (ggplot @axis.ticks@
-- is a separate element).
themeAxisLineWidth :: Double -> VisualSpec
themeAxisLineWidth d = mempty { vsThemeOverride = mempty { toAxisLineWidth = Last (Just d) } }
-- | [日本語]: 軸目盛線の向き ('Graphics.Hgg.Spec.Theme.TickOut' 外 /
-- 'Graphics.Hgg.Spec.Theme.TickIn' 内 / 'Graphics.Hgg.Spec.Theme.TickBoth'
-- 両)。 TickIn は panel 外に出ないため、 軸ラベルは tick 長 0 と同じ位置に寄る
-- (ggplot の負 axis.ticks.length と同挙動)。
-- [English]: The axis tick direction ('Graphics.Hgg.Spec.Theme.TickOut'
-- outward / 'Graphics.Hgg.Spec.Theme.TickIn' inward /
-- 'Graphics.Hgg.Spec.Theme.TickBoth' both). Since TickIn does not extend
-- outside the panel, axis labels move to the same position as a tick
-- length of 0 (the same behavior as ggplot's negative
-- axis.ticks.length).
themeTickDir :: TickDir -> VisualSpec
themeTickDir d = mempty { vsThemeOverride = mempty { toTickDir = Last (Just d) } }
-- | [日本語]: 図の外周余白 (pt) を theme に焼き込む (ggplot @plot.margin@
-- 相当)。 引数順は ggplot @margin(t, r, b, l)@ と同じ。 指定時は自動算出の
-- 外周分 (各辺 half_line = 5.5pt) を __置き換える__ (加算ではない)。 軸ラベル・
-- title 帯・凡例などの内側予約は従来どおり自動算出のまま。
-- [English]: Bakes the figure's outer margin (pt) into the theme (like
-- ggplot's @plot.margin@). Argument order matches ggplot's
-- @margin(t, r, b, l)@. When specified, it __replaces__ the
-- automatically computed outer margin (each side's half_line = 5.5pt) —
-- it does not add to it. Inner reservations such as axis labels, the
-- title band and the legend are still computed automatically as
-- before.
themePlotMargin :: Double -> Double -> Double -> Double -> VisualSpec
themePlotMargin t r b l =
mempty { vsThemeOverride = mempty { toPlotMargin = Last (Just (Margin t r b l)) } }
-- | [日本語]: base font size (pt) を theme に焼き込む (ggplot @base_size@
-- 相当、 既定 11)。 各 slot の既定 font size はこれからの相対倍率で派生する
-- (title ×1.2 / axis.title ×1 / axis.text ×0.8 / legend.title ×1 /
-- legend.text ×0.8)。 'themeTitleFont' 等の個別指定 (fsSize) があればそちらが
-- 優先。
-- [English]: Bakes the base font size (pt) into the theme (like
-- ggplot's @base_size@, default 11). Each slot's default font size is
-- derived from this via a relative multiplier (title ×1.2 /
-- axis.title ×1 / axis.text ×0.8 / legend.title ×1 / legend.text ×0.8).
-- An individual override (fsSize) such as 'themeTitleFont' takes
-- priority when present.
themeBaseFontSize :: Double -> VisualSpec
themeBaseFontSize s =
mempty { vsThemeOverride = mempty { toBaseFontSize = Last (Just s) } }
-- | [日本語]: 軸目盛ラベル文字 (ggplot @axis.text@) の表示。 False =
-- element_blank 相当で文字のみ消える (tick 線の有無は 'themeTickLength' と
-- 独立)。 ラベル文字ぶんの margin 予約も連動して落ちる。 既定は 'ThemeVoid'
-- のみ False。
-- [English]: Whether to show axis tick-label text (ggplot's
-- @axis.text@). False is equivalent to element_blank — only the text
-- disappears (independent of whether tick marks are shown, controlled
-- by 'themeTickLength'). The margin reserved for label text is also
-- dropped accordingly. Default is False only for 'ThemeVoid'.
themeAxisText :: Bool -> VisualSpec -- axis.text on/off
themeAxisText b = mempty { vsThemeOverride = mempty { toShowAxisText = Last (Just b) } }
-- | [日本語]: 軸タイトル (ggplot @axis.title@) の表示。 False = element_blank
-- 相当 ('xLabel' / 'yLabel' 指定があっても描かず margin も予約しない)。
-- 既定は 'ThemeVoid' のみ False。
-- [English]: Whether to show the axis title (ggplot's @axis.title@).
-- False is equivalent to element_blank (even if 'xLabel' / 'yLabel' is
-- set, it is neither drawn nor reserves margin). Default is False only
-- for 'ThemeVoid'.
themeAxisTitle :: Bool -> VisualSpec -- axis.title on/off
themeAxisTitle b = mempty { vsThemeOverride = mempty { toShowAxisTitle = Last (Just b) } }
-- | [日本語]: 凡例キー 1 辺 (pt、 ggplot @legend.key.size@ 相当)。 キーの行
-- pitch = キー辺なので凡例の行間もこれで決まる (既定 = 1.2 lines = 1.2 × base ×
-- 1.3133、 base 11 で 17.34pt)。 凡例の margin 予約にも波及する。
-- [English]: The legend key's side length (pt, like ggplot's
-- @legend.key.size@). Since the key's row pitch equals the key side,
-- this also determines the legend's line spacing (default = 1.2 lines
-- = 1.2 × base × 1.3133, i.e. 17.34pt at base 11). It also affects the
-- legend's margin reservation.
themeLegendKeySize :: Double -> VisualSpec -- legend.key.size (pt)
themeLegendKeySize d = mempty { vsThemeOverride = mempty { toLegendKeySize = Last (Just d) } }
-- ===========================================================================
-- 合成 preset (cowplot 風)
-- ===========================================================================
-- 'ThemeName' の enum には足さず (JSON parity 維持)、 既存 setter を `<>` で
-- 束ねた 'VisualSpec' 値として提供する = 「自作 theme は setter 合成で表現する」
-- 方針の自己適用。 後ろに setter を重ねれば個別上書きできる
-- (例 @themeCowplot <> themeTickLength 5@)。 数値は R cowplot 1.2.0 の既定
-- (基準 font_size N: half_line=N/2 → margin N/2 pt / tick N/4 pt、 文字は
-- title ×16/14 bold / axis.title ×1 / axis.text ×12/14 の相対倍率、 黒基調)。
-- ★ 'themeBaseFontSize' を焼き込む sized 版が本体。 tick 長・外周
-- margin は base 派生の既定値に任せ、 明示 setter は置かない (= preset 後の
-- 'themeBaseFontSize' 上書きにも spacing が連動する)。
-- | [日本語]: cowplot @theme_cowplot(font_size = N)@ 相当 = grid なし・下/左の
-- 黒軸線・外向き tick N/4 pt・外周余白 N/2 pt・黒基調の文字・背景透過。
-- [English]: The equivalent of cowplot's
-- @theme_cowplot(font_size = N)@: no grid, black axis lines on the
-- bottom/left, outward ticks of N/4 pt, an outer margin of N/2 pt,
-- black-toned text, and a transparent background.
themeCowplotSized :: Double -> VisualSpec
themeCowplotSized n =
theme ThemeClassic
<> themeBaseFontSize n
<> cowplotFontsSized n
<> axisColor "#000000" <> tickColor "#000000"
<> textColor "#000000" <> titleColor "#000000"
<> themePlotBg False -- ★ cowplot は rect fill NA = 背景透過
<> themeLegendKeySize (1.1 * n) -- ★ cowplot は legend.key.size = 1.1×font_size
-- | [日本語]: cowplot @theme_cowplot()@ 相当 (= 既定 font_size 14)。
-- [English]: The equivalent of cowplot's @theme_cowplot()@ (default
-- font_size 14).
themeCowplot :: VisualSpec
themeCowplot = themeCowplotSized 14
-- | [日本語]: cowplot @theme_minimal_grid(font_size = N)@ 相当 = major grid
-- (grey85) のみ・軸線/枠/tick なし・黒基調の文字。
-- [English]: The equivalent of cowplot's
-- @theme_minimal_grid(font_size = N)@: only the major grid (grey85), no
-- axis lines/border/ticks, black-toned text.
themeMinimalGridSized :: Double -> VisualSpec
themeMinimalGridSized n =
theme ThemeMinimal
<> themeBaseFontSize n
<> cowplotFontsSized n
<> textColor "#000000" <> titleColor "#000000"
<> themeGridMinor False
<> gridColor "#d9d9d9"
<> panelBorder False
<> themeTickLength 0
<> themePlotBg False -- ★ cowplot は rect fill NA = 背景透過
<> themeLegendKeySize (1.1 * n) -- ★ cowplot は legend.key.size = 1.1×font_size
-- | [日本語]: cowplot @theme_minimal_grid()@ 相当 (= 既定 font_size 14)。
-- [English]: The equivalent of cowplot's @theme_minimal_grid()@
-- (default font_size 14).
themeMinimalGrid :: VisualSpec
themeMinimalGrid = themeMinimalGridSized 14
-- | [日本語]: cowplot @theme_map(font_size = N)@ 相当 = 軸線・grid・枠・tick
-- 線・軸ラベル文字・軸タイトルを全て消す (★ 'ThemeVoid' 既定で axis.text /
-- axis.title も blank)。 タイトル系と凡例は残る。 facet strip は theme_map が
-- grey80 で残すため 'stripFill' を明示 (ThemeVoid 既定は strip なし)。
-- [English]: The equivalent of cowplot's @theme_map(font_size = N)@:
-- removes axis lines, grid, border, tick marks, axis-label text and
-- axis titles entirely ('ThemeVoid' also blanks axis.text /
-- axis.title by default). Title elements and the legend remain. Since
-- theme_map keeps the facet strip at grey80, 'stripFill' is set
-- explicitly (ThemeVoid's default has no strip).
themeMapSized :: Double -> VisualSpec
themeMapSized n =
theme ThemeVoid
<> themeBaseFontSize n
<> cowplotFontsSized n
<> textColor "#000000" <> titleColor "#000000"
<> themeTickLength 0
<> themePlotBg False -- ★ cowplot は rect fill NA = 背景透過
<> stripFill "#cccccc" -- ★ theme_map は strip.background grey80 を残す
<> themeLegendKeySize (1.1 * n) -- ★ cowplot は legend.key.size = 1.1×font_size
-- | [日本語]: cowplot @theme_map()@ 相当 (= 既定 font_size 14)。
-- [English]: The equivalent of cowplot's @theme_map()@ (default
-- font_size 14).
themeMap :: VisualSpec
themeMap = themeMapSized 14
-- | [日本語]: preset 3 種で共有する cowplot(N) の文字設定 (title は bold)。
-- 倍率は cowplot 既定 rel_large = 16/14 (title) / rel_small = 12/14
-- (axis.text・legend)。 ggplot 既定倍率 (1.2/0.8) と異なるため base 派生に
-- 任せず明示する。
-- [English]: The cowplot(N) font settings shared by the three presets
-- (title is bold). The multipliers follow cowplot's defaults
-- rel_large = 16/14 (title) / rel_small = 12/14 (axis.text, legend).
-- Since these differ from ggplot's default multipliers (1.2/0.8), they
-- are set explicitly rather than left to derive from base.
cowplotFontsSized :: Double -> VisualSpec
cowplotFontsSized n =
themeTitleFont (fontSize (n * 16 / 14) <> fontWeight "bold")
<> themeAxisLabelFont (fontSize n)
<> themeTickFont (fontSize (n * 12 / 14))
<> themeLegendFont (fontSize (n * 12 / 14))
panelFill :: Text -> VisualSpec -- panel.background fill (= 塗り on + 色指定)
panelFill c = mempty { vsThemeOverride = mempty { toPanelBg = Last (Just c), toShowPanel = Last (Just True) } }
panelBorder :: Bool -> VisualSpec -- panel.border on/off
panelBorder b = mempty { vsThemeOverride = mempty { toShowBorder = Last (Just b) } }
themeAxisLine :: Bool -> VisualSpec -- axis.line (下/左 2 辺) on/off
themeAxisLine b = mempty { vsThemeOverride = mempty { toShowAxisLine = Last (Just b) } }
gridColor :: Text -> VisualSpec -- panel.grid colour
gridColor c = mempty { vsThemeOverride = mempty { toGridColor = Last (Just c) } }
plotBg :: Text -> VisualSpec -- plot.background fill
plotBg c = mempty { vsThemeOverride = mempty { toPlotBg = Last (Just c) } }
-- | [日本語]: plot.background を塗るか (★)。 @themePlotBg False@ = 塗らない
-- (= 透過、 ggplot @plot.background = element_blank()@ / cowplot fill NA
-- 相当)。
-- [English]: Whether to paint plot.background. @themePlotBg False@
-- means no paint (transparent, equivalent to ggplot's
-- @plot.background = element_blank()@ / cowplot's fill NA).
themePlotBg :: Bool -> VisualSpec -- plot.background 塗り on/off
themePlotBg b = mempty { vsThemeOverride = mempty { toShowBackground = Last (Just b) } }
axisColor :: Text -> VisualSpec -- axis 線/目盛り色
axisColor c = mempty { vsThemeOverride = mempty { toAxisColor = Last (Just c) } }
textColor :: Text -> VisualSpec -- 文字色
textColor c = mempty { vsThemeOverride = mempty { toTextColor = Last (Just c) } }
-- | [日本語]: theme 経由の font setter 群 (ggplot
-- theme(plot.title=element_text(...)) 等)。 vsTitleFont 等の専用 setter より
-- 優先される (= 後付け theme 上書き)。 `<>` で重ねる。
-- [English]: A family of font setters via theme (like ggplot's
-- theme(plot.title=element_text(...))). Takes priority over dedicated
-- setters such as vsTitleFont (a later theme override applied on top).
-- Stack them with `<>`.
themeTitleFont :: FontSpec -> VisualSpec -- plot.title
themeTitleFont f = mempty { vsThemeOverride = mempty { toTitleFont = Last (Just f) } }
themeAxisLabelFont :: FontSpec -> VisualSpec -- axis.title
themeAxisLabelFont f = mempty { vsThemeOverride = mempty { toAxisLabelFont = Last (Just f) } }
themeTickFont :: FontSpec -> VisualSpec -- axis.text
themeTickFont f = mempty { vsThemeOverride = mempty { toTickFont = Last (Just f) } }
themeLegendFont :: FontSpec -> VisualSpec -- legend.title / legend.text
themeLegendFont f = mempty { vsThemeOverride = mempty { toLegendFont = Last (Just f) } }
-- | [日本語]: ★ 全 text slot 共通の font family (ggplot
-- theme(text = element_text(family=...)) 相当)。 slot 別 FontSpec の
-- 'Graphics.Hgg.Spec.fontFamily' 指定があればそちらが優先 ('Graphics.Hgg.Render.Common.mkFontTS'
-- 解決)。 slot 丸ごとの 'themeTitleFont' 等と違い preset の fontSize
-- 焼き込みを潰さない。 PNG backend は family 名を正規化してフォントファイルを
-- 解決する (不在なら既定フォント + stderr 警告)。
-- [English]: The font family shared by all text slots (like ggplot's
-- theme(text = element_text(family=...))). A per-slot FontSpec setting
-- via 'Graphics.Hgg.Spec.fontFamily' takes priority when present
-- (resolved by 'Graphics.Hgg.Render.Common.mkFontTS'). Unlike whole-slot setters such as
-- 'themeTitleFont', it does not clobber a preset's baked-in fontSize.
-- The PNG backend normalizes the family name to resolve a font file (if
-- absent, it falls back to the default font plus a stderr warning).
themeFontFamily :: Text -> VisualSpec
themeFontFamily fam = mempty { vsThemeOverride = mempty { toFontFamily = Last (Just fam) } }
-- | [日本語]: axis.text の回転角 (度) を theme から指定。 per-axis 'Graphics.Hgg.Spec.Axis.axisRotate'
-- 未指定時の fallback。
-- [English]: Sets the axis.text rotation angle (degrees) from the
-- theme. A fallback used when the per-axis 'Graphics.Hgg.Spec.Axis.axisRotate' is not
-- specified.
themeAxisTextAngle :: Double -> VisualSpec
themeAxisTextAngle a = mempty { vsThemeOverride = mempty { toAxisTextAngle = Last (Just a) } }
-- | [日本語]: axis.text の __x 軸のみ__ の回転角 (度・CCW) を theme から指定。
-- 共通 'themeAxisTextAngle' より優先。 per-axis 'xAxis (axisRotate …)' が更に
-- 優先。
-- [English]: Sets the rotation angle (degrees, CCW) for axis.text on
-- __the x axis only__, from the theme. Takes priority over the common
-- 'themeAxisTextAngle'; the per-axis 'xAxis (axisRotate …)' takes
-- priority over this.
themeAxisTextAngleX :: Double -> VisualSpec
themeAxisTextAngleX a = mempty { vsThemeOverride = mempty { toAxisTextAngleX = Last (Just a) } }
-- | [日本語]: axis.text の __y 軸のみ__ の回転角 (度・CCW) を theme から指定。
-- [English]: Sets the rotation angle (degrees, CCW) for axis.text on
-- __the y axis only__, from the theme.
themeAxisTextAngleY :: Double -> VisualSpec
themeAxisTextAngleY a = mempty { vsThemeOverride = mempty { toAxisTextAngleY = Last (Just a) } }
-- | [日本語]: theme の x 軸 axis.text 回転角を解決 (軸別 'toAxisTextAngleX' >
-- 共通 'toAxisTextAngle')。 'Graphics.Hgg.Spec.Axis.resolveAxisAngle' の theme fallback 引数に渡す。
-- [English]: Resolves the theme's x-axis axis.text rotation angle
-- (the per-axis 'toAxisTextAngleX' takes priority over the common
-- 'toAxisTextAngle'). Passed as the theme fallback argument to
-- 'Graphics.Hgg.Spec.Axis.resolveAxisAngle'.
axisTextAngleXOf :: ThemeOverride -> Last Double
axisTextAngleXOf o = toAxisTextAngle o <> toAxisTextAngleX o
-- | [日本語]: theme の y 軸 axis.text 回転角を解決 (軸別 'toAxisTextAngleY' >
-- 共通 'toAxisTextAngle')。
-- [English]: Resolves the theme's y-axis axis.text rotation angle
-- (the per-axis 'toAxisTextAngleY' takes priority over the common
-- 'toAxisTextAngle').
axisTextAngleYOf :: ThemeOverride -> Last Double
axisTextAngleYOf o = toAxisTextAngle o <> toAxisTextAngleY o
-- | [日本語]: facet strip.background の塗り色を指定 (= 塗り on + 色)。
-- [English]: Sets the fill color for the facet strip.background
-- (turns fill on and sets the color).
stripFill :: Text -> VisualSpec
stripFill c = mempty { vsThemeOverride = mempty { toStripBg = Last (Just c), toShowStrip = Last (Just True) } }
-- | [日本語]: facet strip 矩形の on/off。
-- [English]: Toggles the facet strip rectangle on/off.
themeStrip :: Bool -> VisualSpec
themeStrip b = mempty { vsThemeOverride = mempty { toShowStrip = Last (Just b) } }
-- | [日本語]: プリセット専用だった 4 項目の theme 上書き setter (= 全プロパティ
-- `<>` 上書き)。 `theme ThemeGrey <> titleHjust 0.5 <> legendKeyBg "#fff"` の
-- ように重ねる。
-- [English]: Theme-override setters for four items that used to be
-- preset-only (all properties can be overridden with `<>`). Stack them
-- like `theme ThemeGrey <> titleHjust 0.5 <> legendKeyBg "#fff"`.
titleHjust :: Double -> VisualSpec -- plot.title の水平揃え (0=左、 0.5=中央)
titleHjust h = mempty { vsThemeOverride = mempty { toTitleHjust = Last (Just h) } }
titleColor :: Text -> VisualSpec -- plot.title / axis.title の文字色
titleColor c = mempty { vsThemeOverride = mempty { toTitleColor = Last (Just c) } }
tickColor :: Text -> VisualSpec -- 軸目盛線 (tick mark) の色
tickColor c = mempty { vsThemeOverride = mempty { toTickLineColor = Last (Just c) } }
legendKeyBg :: Text -> VisualSpec -- legend.key 背景塗り色 ("" なら塗らない)
legendKeyBg c = mempty { vsThemeOverride = mempty { toLegendKeyBg = Last (Just c) } }
facet :: ColRef -> VisualSpec
facet c = mempty { vsFacet = Last (Just c) }
-- | [日本語]: facet_wrap(~c, ncol=n)。 c で分割し n 列で複数行に折り返す。
-- ncol 未使用 (= 'facet' のみ) なら従来の 1 行 N 列。
-- [English]: facet_wrap(~c, ncol=n). Splits by c and wraps into
-- multiple rows of n columns. When ncol is unused ('facet' alone), it
-- falls back to the original single row of N columns.
facetWrap :: ColRef -> Int -> VisualSpec
facetWrap c n = mempty { vsFacet = Last (Just c), vsFacetNcol = Last (Just n) }
-- | [日本語]: facet の列数のみ指定 (= 既存 'facet' と併用)。
-- [English]: Specifies only the facet column count (used together with
-- the existing 'facet').
facetCols :: Int -> VisualSpec
facetCols n = mempty { vsFacetNcol = Last (Just n) }
-- | [日本語]: facet_wrap の scale 共有方式 (= ggplot facet_wrap(scales=))。
-- 'FacetFixed' (既定) = 共通 domain、 'FacetFree'/'FacetFreeX'/'FacetFreeY' =
-- 該当軸を panel ごとに独立 domain に。 free な軸は全 panel に軸を表示する。
-- 'facet' と併用。
-- [English]: The scale-sharing mode for facet_wrap (like ggplot's
-- facet_wrap(scales=)). 'FacetFixed' (default) uses a shared domain;
-- 'FacetFree'/'FacetFreeX'/'FacetFreeY' give the corresponding axis an
-- independent domain per panel. A free axis is drawn on every panel.
-- Used together with 'facet'.
facetScales :: FacetScales -> VisualSpec
facetScales fs = mempty { vsFacetScales = Last (Just fs) }
-- | [日本語]: facet_grid の panel サイズ配分 (= ggplot facet_grid(space=))。
-- 'SpaceFree' 等で free 軸の track 幅/高を data 範囲に比例配分する。 通常
-- 'facetScales' と併用。 facet_grid のみ有効。
-- [English]: The panel-size allocation for facet_grid (like ggplot's
-- facet_grid(space=)). 'SpaceFree' etc. allocate a free axis's track
-- width/height proportionally to its data range. Usually used together
-- with 'facetScales'; only effective for facet_grid.
facetSpace :: FacetSpace -> VisualSpec
facetSpace fs = mempty { vsFacetSpace = Last (Just fs) }
-- | [日本語]: facet_grid(row ~ col)。 row 変数の levels で行、 col 変数の
-- levels で列を作り 2 次元の cross 配置にする。 strip は上 (col 名)・右
-- (row 名)、 軸は最下行 x・左端列 y のみ (ggplot facet_grid 既定)。
-- [English]: facet_grid(row ~ col). Builds rows from the row variable's
-- levels and columns from the col variable's levels, giving a 2D
-- cross-tabulated layout. Strips appear at the top (col name) and right
-- (row name); axes appear only on the bottom row (x) and left column
-- (y) — ggplot's facet_grid default.
facetGrid :: ColRef -> ColRef -> VisualSpec
facetGrid rowC colC = mempty { vsFacetRow = Last (Just rowC)
, vsFacetCol = Last (Just colC) }
-- | [日本語]: panel grid (= facet とは独立、 各 spec を独立 panel として並べる)。
-- facet は 1 列でデータを分割するのに対し、 subplots は完全に別 spec を
-- 並べる。 DoE の MainEffects (= 複数 factor を横並び) で使う。
-- [English]: A panel grid (independent of facet — lays out separate
-- specs as independent panels). Whereas facet splits data by a single
-- column, subplots lays out entirely distinct specs. Used for DoE's
-- MainEffects (multiple factors side by side).
subplots :: [VisualSpec] -> VisualSpec
subplots ss = mempty { vsSubplots = ss }
-- | [日本語]: P18: subplots の 2D grid 折り返し列数。
-- [English]: P18: the wrap column count for the subplots 2D grid.
subplotCols :: Int -> VisualSpec
subplotCols n = mempty { vsSubplotCols = Last (Just n) }
-- | [日本語]: subplot 列の相対幅 (cowplot @plot_grid(rel_widths=)@ 相当)。
-- 統一グリッドの列 index 順の重みで、 列数に対して不足分は 1 で埋める
-- (エラーにしない)。 @(a <-> b) <> subplotWidths [1.3, 1]@ のように使う。
-- [English]: The relative widths of subplot columns (like cowplot's
-- @plot_grid(rel_widths=)@). Weights in the unified grid's column-index
-- order; any shortfall relative to the column count is padded with 1
-- (not an error). Use it like
-- @(a <-> b) <> subplotWidths [1.3, 1]@.
subplotWidths :: [Double] -> VisualSpec
subplotWidths ws = mempty { vsSubplotWidths = Last (Just ws) }
-- | [日本語]: subplot 行の相対高 (cowplot @plot_grid(rel_heights=)@ 相当)。
-- [English]: The relative heights of subplot rows (like cowplot's
-- @plot_grid(rel_heights=)@).
subplotHeights :: [Double] -> VisualSpec
subplotHeights hs = mempty { vsSubplotHeights = Last (Just hs) }
-- | [日本語]: subplot panel の自動タグ (cowplot @plot_grid(labels="AUTO")@
-- 相当)。 統一グリッドの panel 列挙順に \"A\",\"B\",… ('TagUpper') \/
-- \"a\",\"b\",… ('TagLower') \/ \"1\",\"2\",… ('TagNumeric') を各 panel の
-- 'tag' として注入する。 panel 自身の 'tag' 明示指定が優先 (個別 > 一括)。
-- @(a <-> b) <> subplotTags TagUpper@ のように使う。
-- [English]: Automatic subplot panel tags (like cowplot's
-- @plot_grid(labels="AUTO")@). Injects \"A\",\"B\",… ('TagUpper') /
-- \"a\",\"b\",… ('TagLower') / \"1\",\"2\",… ('TagNumeric') as each
-- panel's 'tag', in the unified grid's panel-enumeration order. A
-- panel's own explicit 'tag' takes priority (individual over bulk). Use
-- it like @(a <-> b) <> subplotTags TagUpper@.
subplotTags :: TagStyle -> VisualSpec
subplotTags s = mempty { vsSubplotTags = Last (Just s) }
-- | [日本語]: subplot panel を __名前 (= 子 spec の 'vsTitle') で選択 + 並べ替え__。
-- 'repeatFields' (名前リスト → panel 群) の逆方向。 列挙順がそのまま表示順に
-- なる (ggplot @scale_*_discrete(limits=)@ と同じ「選択 + 順序」 の意味論)。
-- 一致しない名前は無視、 title 無し panel は選択時には常に落ちる。
-- [English]: Selects and reorders subplot panels __by name (the child spec's 'vsTitle')__.
-- The reverse direction of 'repeatFields' (name
-- list → panel group). The enumeration order becomes the display order
-- directly (the same "select + order" semantics as ggplot's
-- @scale_*_discrete(limits=)@). Non-matching names are ignored, and
-- panels with no title are always dropped when selecting.
--
-- > subplots panels <> selectPanels ["b", "a"] <> subplotCols 2
selectPanels :: [Text] -> VisualSpec
selectPanels ws = mempty { vsPanelSel = Last (Just ws) }
-- | [日本語]: 'vsPanelSel' を適用した後の実効 subplot 列。 描画
-- ('Graphics.Hgg.Render.Layer.renderSubplots') の正本で、 HS 外 (canvas / PS codec) へ spec を送る側も
-- serialise 前にこれで解決すれば PS 非改修で選択が効く。 選択未指定
-- ('Nothing') は全 panel をそのまま返す。
-- [English]: The effective subplot list after applying 'vsPanelSel'.
-- The source of truth for rendering ('Graphics.Hgg.Render.Layer.renderSubplots'); resolving
-- through this before serializing on the side that sends the spec
-- outside HS (canvas / PS codec) makes selection work with no PS
-- changes. With no selection ('Nothing'), all panels are returned
-- as-is.
selectedSubplots :: VisualSpec -> [VisualSpec]
selectedSubplots s = case getLast (vsPanelSel s) of
Nothing -> vsSubplots s
Just ws -> [ p | nm <- ws, p <- vsSubplots s, getLast (vsTitle p) == Just nm ]
-- | [日本語]: 離散 x 軸の limits (= ggplot @scale_x_discrete(limits=)@)。
-- x encoding が ColTxt の layer のカテゴリ行を __選択 + 列挙順に並べ替え__る。
-- aes 基準なので coord_flip と直交 (flip 後も x データ軸を指す)。
-- [English]: The limits for a discrete x axis (like ggplot's
-- @scale_x_discrete(limits=)@). For layers whose x encoding is
-- ColTxt, __selects and reorders__ the category rows by enumeration
-- order. Since it is aes-based, it is orthogonal to coord_flip (still
-- refers to the x data axis even after a flip).
scaleXDiscreteLimits :: [Text] -> VisualSpec
scaleXDiscreteLimits ws = mempty { vsXDiscreteLimits = Last (Just ws) }
-- | [日本語]: 離散 y 軸の limits (= ggplot @scale_y_discrete(limits=)@)。
-- 'Graphics.Hgg.Spec.Constructors.forest' は cat ラベルが y encoding なのでこちらを使う。
-- [English]: The limits for a discrete y axis (like ggplot's
-- @scale_y_discrete(limits=)@). Since 'Graphics.Hgg.Spec.Constructors.forest' encodes its cat label
-- as y, use this one for it.
scaleYDiscreteLimits :: [Text] -> VisualSpec
scaleYDiscreteLimits ws = mempty { vsYDiscreteLimits = Last (Just ws) }
-- | [日本語]: 離散軸 limits の解決 (正本): 'vsXDiscreteLimits' /
-- 'vsYDiscreteLimits' を layer の行 filter + 並べ替えとして適用する。
-- layout / render の入口で呼ぶ (冪等)。
--
-- * 当該軸の encoding が 'ColTxt' の layer のみ対象 (数値軸 layer は不変)。
-- * 行 filter は __全 row-aligned encoding__ (encX/encY/encY2/errorX/errorY/
-- shapeBy/sizeBy/chain/linetypeBy/label/hover/color 列) を同 index で間引く
-- (整合維持)。
-- * 'ColByName' (resolver 参照) を含む spec は先に 'bakeSpec' で inline 化して
-- から filter する (limits 未指定なら bake もしない = 従来経路完全不変)。
-- * limits は当該 spec 自身の layer にのみ効く (subplot 子へは伝播しない —
-- 子は自分の limits を持てる)。
-- [English]: The resolution (source of truth) for discrete-axis
-- limits: applies 'vsXDiscreteLimits' / 'vsYDiscreteLimits' as a
-- row-filter-plus-reorder on layers. Called at the layout / render
-- entry point (idempotent).
--
-- * Only layers whose axis encoding is 'ColTxt' are affected (numeric
-- axis layers are unchanged).
-- * The row filter thins __all row-aligned encodings__
-- (encX/encY/encY2/errorX/errorY/shapeBy/sizeBy/chain/linetypeBy/
-- label/hover/color columns) using the same index (keeping them
-- consistent).
-- * A spec containing 'ColByName' (a resolver reference) is first
-- inlined via 'bakeSpec' before filtering (if limits are unspecified,
-- no baking happens either — the original path is fully unchanged).
-- * limits only affect the spec's own layers (they do not propagate to
-- subplot children — a child may have its own limits).
applyDiscreteLimits :: Resolver -> VisualSpec -> VisualSpec
applyDiscreteLimits r spec =
case (getLast (vsXDiscreteLimits spec), getLast (vsYDiscreteLimits spec)) of
(Nothing, Nothing) -> spec
(mxs, mys) ->
let b = bakeSpec r spec
limited = map (limitAxis lyEncY mys . limitAxis lyEncX mxs) (vsLayers b)
in b { vsLayers = limited }
where
limitAxis enc (Just ws) ly
| Just (ColTxt cats) <- getLast (enc ly) =
let n = V.length cats
idx = V.fromList
[ i | w <- ws
, (i, c) <- zip [0 ..] (V.toList cats), c == w ]
in reindexLayer n idx ly
limitAxis _ _ ly = ly
-- | [日本語]: layer の全 row-aligned encoding を同じ index 列で間引く
-- ('applyDiscreteLimits' 用)。 長さ @n@ (= cat 列長) と一致する inline 列のみ
-- 対象 (不一致・'ColByName' は据え置き)。
-- [English]: Thins every row-aligned encoding of a layer using the same
-- index column (used by 'applyDiscreteLimits'). Only inline columns
-- whose length matches @n@ (the cat column's length) are affected
-- (mismatched columns and 'ColByName' are left untouched).
reindexLayer :: Int -> Vector Int -> Layer -> Layer
reindexLayer n idx ly = ly
{ lyEncX = reC <$> lyEncX ly
, lyEncY = reC <$> lyEncY ly
, lyEncY2 = reC <$> lyEncY2 ly
, lyErrorX = reC <$> lyErrorX ly
, lyErrorY = reC <$> lyErrorY ly
, lyShapeBy = reC <$> lyShapeBy ly
, lySizeBy = reC <$> lySizeBy ly
, lyAlphaBy = reC <$> lyAlphaBy ly
, lyChain = reC <$> lyChain ly
, lyLinetypeBy = reC <$> lyLinetypeBy ly
, lyLabel = reC <$> lyLabel ly
, lyHover = map reC (lyHover ly)
, lyColor = reColor <$> lyColor ly
-- ★ Phase 62 A2/A6: quiver 成分 (row-aligned) + sub-mark (Phase 36 D2) 再帰。
, lyEncU = reC <$> lyEncU ly
, lyEncV = reC <$> lyEncV ly
, lyOverlay = map (reindexLayer n idx) (lyOverlay ly)
}
where
reC c = case c of
ColNum v | V.length v == n -> ColNum (V.backpermute v idx)
ColTxt v | V.length v == n -> ColTxt (V.backpermute v idx)
_ -> c
reColor ce = case ce of
ColorByCol c -> ColorByCol (reC c)
ColorByContinuous c -> ColorByContinuous (reC c)
ColorStatic t -> ColorStatic t
-- | [日本語]: Vega-Lite @repeat@ 相当: フィールド名のリストを反復し、 各
-- フィールドから 1 つの view (VisualSpec) を生成して 'subplots' に並べる
-- (= フィールド自動反復)。 @repeatFields ["a","b","c"] (\\f -> layer (hist f))@
-- は 3 パネルを作る。 列数は @<> subplotCols n@ で指定する。 Vega の @repeat@
-- が encoding 内の @{repeat: ...}@ でフィールドを差し込むのに対し、 こちらは
-- 生成関数にフィールド名を渡す明示形 (spec を値として組む方針ゆえ)。
-- [English]: The equivalent of Vega-Lite's @repeat@: iterates a list of
-- field names, generating one view (VisualSpec) per field and laying
-- them out with 'subplots' (automatic field repetition).
-- @repeatFields ["a","b","c"] (\\f -> layer (hist f))@ produces 3
-- panels. The column count is set via @<> subplotCols n@. Whereas
-- Vega's @repeat@ splices in fields via @{repeat: ...}@ inside the
-- encoding, this is an explicit form that passes the field name to a
-- generator function (following the approach of building specs as
-- values).
repeatFields :: [Text] -> (Text -> VisualSpec) -> VisualSpec
repeatFields fields mk = subplots (map mk fields)
-- 以下 3 関数は mark 構築子だが 'VisualSpec' と 'layer' に依存するため
-- 'Spec.Constructors' には置けず、 top-level setter 群と同居する。
-- | [日本語]: Wide-form histogram (P1): 複数列を __同一 plot に半透明で重ねる__。
--
-- `histogramWide [c1, c2, c3]` は 'VisualSpec' を返し、 内部で各列を独立
-- layer 化:
--
-- * layer i = `histogram cᵢ <> color (fromHex (palette i)) <> alpha 0.4 <> binCount 20`
--
-- palette は ColorBrewer Set1 (= categorical 9-class、 wong / 独自 切替は
-- 今後)。 bin 数は全列で __共通__ (= seaborn の `multiple="layer"` 同等)、
-- デフォ 20。
--
-- matplotlib との対応: `plt.hist([c1, c2, c3], alpha=0.5, label=names)`
-- 相当。
-- [English]: Wide-form histogram (P1): overlays multiple columns
-- __semi-transparently on the same plot__.
--
-- `histogramWide [c1, c2, c3]` returns a 'VisualSpec', internally
-- turning each column into an independent layer:
--
-- * layer i = `histogram cᵢ <> color (fromHex (palette i)) <> alpha 0.4 <> binCount 20`
--
-- The palette is ColorBrewer Set1 (categorical 9-class; switching to
-- wong / a dedicated scheme is a future addition). The bin count is
-- __shared__ across all columns (equivalent to seaborn's
-- `multiple="layer"`), defaulting to 20.
--
-- Corresponds to matplotlib's
-- `plt.hist([c1, c2, c3], alpha=0.5, label=names)`.
histogramWide :: [ColRef] -> VisualSpec
histogramWide cols =
let pal = ["#E41A1C", "#377EB8", "#4DAF4A", "#984EA3", "#FF7F00"
, "#FFFF33", "#A65628", "#F781BF", "#999999"]
mkLayer i c = layer
( histogram c
-- 内部 palette は Text 経路ゆえ ColorStatic 直構築で温存 (Color 型を通さない)
<> mempty { lyColor = Last (Just (ColorStatic (cycleColor pal i))) }
<> alpha 0.4
<> binCount 20
)
in mconcat [ mkLayer i c | (i, c) <- zip [0 ..] cols ]
where
cycleColor cs i = cs !! (i `mod` length cs)
-- | [日本語]: 別列・別 mark を 1 パネルに併置 (= mixed-mark)。 @<+>@ の list 版
-- (@distCols xs = layer (foldl1 (<+>) xs)@)。 各マークの値列 (encY) が別なので
-- 別 slot (列名) に横並び・y は全列の値域和・単一パネル (subplot とは別)。
-- lane は 1D 分布 mark 専用 (box/violin/strip/swarm/raincloud)。 raincloud は
-- 全マーク同一列ゆえ 1 slot に重畳する ('Graphics.Hgg.Spec.Constructors.compositeLanes' が列数を決める)。
-- [English]: Places different columns / different marks side by side in
-- a single panel (mixed-mark). A list version of @<+>@
-- (@distCols xs = layer (foldl1 (<+>) xs)@). Since each mark's value
-- column (encY) differs, they lay out in separate slots (columns); y
-- spans the union of all columns' ranges in a single panel (distinct
-- from subplot). Lanes are specific to 1D distribution marks
-- (box/violin/strip/swarm/raincloud). Since raincloud uses the same
-- column for every mark, they overlay into a single slot
-- ('Graphics.Hgg.Spec.Constructors.compositeLanes' determines the number of columns).
--
-- > distCols [ boxplot "a", violin "c", boxplot "d" ]
distCols :: [Layer] -> VisualSpec
distCols [] = mempty
distCols (l : ls) = layer (foldl (<+>) l ls)
-- | [日本語]: ★ ridge レイヤを含み coord 未指定の spec に coord_flip を自動
-- 付与する。 ridge は「値→x(連続)・群→y(カテゴリ)」だが combinator は
-- box/violin と統一 (値=encY・群=encX via groupBy)。 coord_flip で
-- encY(値)→x・encX(群)→y に回す (box-flip と同機構)。 computeLayout /
-- renderToPrimitives の入口で適用する。
-- [English]: Automatically applies coord_flip to a spec that contains
-- a ridge layer and has no coord specified. Although ridge is
-- conceptually "value→x (continuous), group→y (categorical)", its
-- combinator is unified with box/violin (value=encY, group=encX via
-- groupBy). coord_flip rotates encY (value) to x and encX (group) to y
-- (the same mechanism as box-flip). Applied at the computeLayout /
-- renderToPrimitives entry point.
ridgeAutoFlip :: VisualSpec -> VisualSpec
ridgeAutoFlip spec
| any (\l -> getFirst (lyKind l) == Just MRidge) (vsLayers spec)
, Nothing <- getLast (vsCoord spec)
= spec { vsCoord = Last (Just CoordFlip) }
| otherwise = spec
-- | [日本語]: P6: annotation 1 個を追加。
-- [English]: P6: adds a single annotation.
annotate :: Annotation -> VisualSpec
annotate a = mempty { vsAnnotations = [a] }
-- | [日本語]: P6: data 座標で text label を打つ shortcut (= 'annotTextP' の
-- PNative ラッパ)。
-- [English]: P6: a shortcut for placing a text label in data
-- coordinates (a PNative wrapper around 'annotTextP').
annotText :: Double -> Double -> Text -> VisualSpec
annotText x y t = annotTextP (PNative x) (PNative y) t
-- | [日本語]: ★ 'Pos' で text を打つ (native/npc/絶対長を軸ごと混在可)。
-- 例: @annotTextP (PNpc 0.95) (PNative 3.0) "R²"@ (右端 npc・data y)。
-- [English]: Places text using 'Pos' (native/npc/absolute length can be
-- mixed per axis). Example:
-- @annotTextP (PNpc 0.95) (PNative 3.0) "R²"@ (npc for the right edge,
-- data for y).
annotTextP :: Pos -> Pos -> Text -> VisualSpec
annotTextP x y t = annotate $ AnnText
{ anX = x, anY = y, anText = t, anColor = "", anSize = 12 }
-- | [日本語]: P6: data 座標で arrow を引く shortcut。
-- [English]: P6: a shortcut for drawing an arrow in data coordinates.
annotArrow :: Double -> Double -> Double -> Double -> VisualSpec
annotArrow x1 y1 x2 y2 =
annotArrowP (PNative x1) (PNative y1) (PNative x2) (PNative y2)
-- | [日本語]: ★ 'Pos' で arrow を引く。
-- [English]: Draws an arrow using 'Pos'.
annotArrowP :: Pos -> Pos -> Pos -> Pos -> VisualSpec
annotArrowP x1 y1 x2 y2 = annotate $ AnnArrow
{ anX1 = x1, anY1 = y1, anX2 = x2, anY2 = y2
, anColor = "#444", anWidth = 1.5 }
-- | [日本語]: P6: data 座標で rect を描く shortcut (x,y,w,h → 2 隅 Pos へ変換)。
-- [English]: P6: a shortcut for drawing a rect in data coordinates
-- (converts x,y,w,h into two corner 'Pos' values).
annotRect :: Double -> Double -> Double -> Double -> Text -> VisualSpec
annotRect x y w h col =
annotRectP (PNative x) (PNative y) (PNative (x + w)) (PNative (y + h)) col
-- | [日本語]: ★ 'Pos' 2 隅で rect を描く。
-- 例: @annotRectP (PNpc 0.0) (PNative 1.0) (PNpc 1.0) (PNative 2.0) "grey"@
-- (帯: x 全幅 npc・y は data 1..2)。
-- [English]: Draws a rect from two 'Pos' corners. Example:
-- @annotRectP (PNpc 0.0) (PNative 1.0) (PNpc 1.0) (PNative 2.0) "grey"@
-- (a band: x spans the full width in npc, y is data 1..2).
annotRectP :: Pos -> Pos -> Pos -> Pos -> Text -> VisualSpec
annotRectP x1 y1 x2 y2 col = annotate $ AnnRect
{ anX1 = x1, anY1 = y1, anX2 = x2, anY2 = y2
, anFill = col, anStroke = "", anStrokeWidth = 0, anFillOpacity = 0.2 }
-- | [日本語]: P6: data 座標で line を引く shortcut。
-- [English]: P6: a shortcut for drawing a line in data coordinates.
annotLine :: Double -> Double -> Double -> Double -> VisualSpec
annotLine x1 y1 x2 y2 =
annotLineP (PNative x1) (PNative y1) (PNative x2) (PNative y2)
-- | [日本語]: ★ 'Pos' で line を引く。
-- [English]: Draws a line using 'Pos'.
annotLineP :: Pos -> Pos -> Pos -> Pos -> VisualSpec
annotLineP x1 y1 x2 y2 = annotate $ AnnLine
{ anX1 = x1, anY1 = y1, anX2 = x2, anY2 = y2
, anColor = "#444", anWidth = 1 }
-- | [日本語]: P13: inset 1 個追加 (= デフォルト位置 右上 30%×30%)。
-- [English]: P13: adds a single inset (default position: top-right,
-- 30%×30%).
inset :: VisualSpec -> VisualSpec
inset s = insetAt 0.65 0.05 0.3 0.3 s
-- | [日本語]: P13: 位置 + サイズ (plotArea 比率 0..1) 指定で inset を追加。
-- inX/inY は __左上原点・y 下向き__ (= 描画系と同じ)。
-- [English]: P13: adds an inset with a given position + size (a
-- plotArea ratio in 0..1). inX/inY use __the top-left origin with y pointing down__
-- (matching the rendering coordinate system).
insetAt :: Double -> Double -> Double -> Double -> VisualSpec -> VisualSpec
insetAt x y w h s = mempty
{ vsInsets = [ Inset { inSpec = s, inX = x, inY = y, inW = w, inH = h } ] }
-- | [日本語]: patchwork @inset_element@ 準拠の inset 追加。 left/bottom/right/top
-- は plotArea 比率 0..1 で __左下原点・y 上向き__ (patchwork 慣例)。 内部で
-- 従来 'insetAt' (左上原点・y 下向き) へ変換するだけの薄いラッパ (非破壊)。
-- patchwork 感覚で `inset_element(p, left, bottom, right, top)` と同じ向きに
-- 置ける。
-- [English]: Adds an inset following patchwork's @inset_element@
-- convention. left/bottom/right/top are a plotArea ratio in 0..1 using
-- __the bottom-left origin with y pointing up__ (the patchwork
-- convention). A thin, non-destructive wrapper that internally converts
-- to the original 'insetAt' (top-left origin, y down). Lets you place
-- insets in the same orientation as patchwork's
-- `inset_element(p, left, bottom, right, top)`.
insetElement :: Double -> Double -> Double -> Double -> VisualSpec -> VisualSpec
insetElement left bottom right top s =
insetAt left (1 - top) (right - left) (top - bottom) s
-- | [日本語]: P17: categorical palette を指定。 default = hggMain (F-3)。
-- [English]: P17: specifies the categorical palette. Default =
-- hggMain (F-3).
palette :: [Text] -> VisualSpec
palette colors = mempty { vsPalette = Last (Just colors) }
-- | [日本語]: ggplot2 hue パレット (= @scales::hue_pal()@) を選ぶ。 群数 n は
-- 描画時に決まるため sentinel を渡し、 Layout で n 展開する
-- (= 'Graphics.Hgg.Palette.ggplotHue')。
-- [English]: Selects ggplot2's hue palette (like @scales::hue_pal()@).
-- Since the group count n is only known at draw time, a sentinel is
-- passed and expanded to n in Layout (via
-- 'Graphics.Hgg.Palette.ggplotHue').
paletteGGplot :: VisualSpec
paletteGGplot = mempty { vsPalette = Last (Just ["__ggplot_hue__"]) }
-- | [日本語]: P17: continuous (sequential) palette を指定。 default =
-- viridis5。
-- [English]: P17: specifies the continuous (sequential) palette.
-- Default = viridis5.
continuousPalette :: [Text] -> VisualSpec
continuousPalette colors = mempty { vsContinuousPal = Last (Just colors) }
-- | [日本語]: A4-e: ggplot @scale_color_manual(values=)@。 カテゴリ名→色(hex)
-- の辞書を指定。 'color' (ColorByCol) のカテゴリ名がここにあればその色を
-- 最優先で使う。 未登録名は従来の positional palette ('palette'/theme) に
-- フォールバック。
-- [English]: A4-e: ggplot's @scale_color_manual(values=)@. Specifies a
-- category-name → color(hex) dictionary. If a 'color' (ColorByCol)
-- category name is present here, its color takes top priority.
-- Unregistered names fall back to the usual positional palette
-- ('palette'/theme).
scaleColorManual :: [(Text, Text)] -> VisualSpec
scaleColorManual dict = mempty { vsColorManual = Last (Just dict) }
-- | [日本語]: A4-e: ggplot
-- @scale_color_gradient2(low,mid,high,midpoint=)@。 発散 (diverging)
-- continuous palette。 'colorContinuousBy' (ColorByContinuous) のとき、
-- midpoint を中心 (0.5) に固定し lo..mid を [0,0.5]・mid..hi を [0.5,1] へ
-- 個別正規化して 3-stop 補間。
-- [English]: A4-e: ggplot's
-- @scale_color_gradient2(low,mid,high,midpoint=)@. A diverging
-- continuous palette. For 'colorContinuousBy' (ColorByContinuous),
-- fixes midpoint at the center (0.5) and independently normalizes
-- lo..mid to [0,0.5] and mid..hi to [0.5,1] for 3-stop interpolation.
scaleColorGradient2 :: Text -> Text -> Text -> Double -> VisualSpec
scaleColorGradient2 low mid high midpoint =
mempty { vsColorGradient2 = Last (Just (low, mid, high, midpoint)) }
-- | [日本語]: A4-e: ggplot @scale_size(range=c(min,max))@。 'Graphics.Hgg.Spec.Constructors.sizeBy'
-- (continuous size aesthetic) の半径 px 範囲を指定 (default (3,10))。
-- sizeBy 未使用なら無影響。
-- [English]: A4-e: ggplot's @scale_size(range=c(min,max))@. Specifies
-- the radius range in px for 'Graphics.Hgg.Spec.Constructors.sizeBy' (the continuous size aesthetic),
-- default (3,10). Has no effect if sizeBy is unused.
scaleSize :: Double -> Double -> VisualSpec
scaleSize lo hi = mempty { vsSizeRange = Last (Just (lo, hi)) }
-- | [日本語]: P8: 凡例を有効化 (= 既定: 右側)。
-- [English]: P8: enables the legend (default: right side).
legend :: VisualSpec
legend = mempty { vsLegend = Last (Just defaultLegendSpec) }
-- | [日本語]: P8: 凡例を抑制。
-- [English]: P8: suppresses the legend.
legendOff :: VisualSpec
legendOff = mempty
{ vsLegend = Last (Just (LegendSpec LegendNone mempty)) }
-- | [日本語]: P8: 凡例位置を指定。
-- [English]: P8: specifies the legend position.
legendPos :: LegendPosition -> VisualSpec
legendPos pos = mempty { vsLegend = Last (Just (LegendSpec pos mempty)) }
-- | [日本語]: 色凡例を非表示 (= ggplot @guides(color="none")@)。 この系では
-- 凡例は色 (color/fill) のみなので 'legendOff' と同義。 ggplot 慣習名の別名
-- として提供。
-- [English]: Hides the color legend (like ggplot's
-- @guides(color="none")@). Since this system's legend is color/fill
-- only, it is synonymous with 'legendOff'. Provided as an alias using
-- ggplot's conventional name.
guideColorNone :: VisualSpec
guideColorNone = legendOff
-- | [日本語]: 凡例キーの表示順を逆に (= ggplot
-- @guide_legend(reverse=TRUE)@)。 各キーの色は固定のまま順序のみ反転。
-- 位置設定 ('legend'/'legendPos') と独立合成可。
-- [English]: Reverses the display order of legend keys (like ggplot's
-- @guide_legend(reverse=TRUE)@). Each key's color stays fixed; only the
-- order is reversed. Composes independently of the position setting
-- ('legend'/'legendPos').
legendReverse :: VisualSpec
legendReverse = mempty { vsLegendReverse = Last (Just True) }
-- | [日本語]: 縦凡例 (Right/Inside) の列数 (= ggplot
-- @guide_legend(ncol=)@)。
-- [English]: The column count for a vertical legend (Right/Inside),
-- like ggplot's @guide_legend(ncol=)@.
legendNcol :: Int -> VisualSpec
legendNcol n = mempty { vsLegendNcol = Last (Just n) }
-- | [日本語]: 横凡例 (Bottom) の行数 (= ggplot @guide_legend(nrow=)@)。
-- [English]: The row count for a horizontal legend (Bottom), like
-- ggplot's @guide_legend(nrow=)@.
legendNrow :: Int -> VisualSpec
legendNrow n = mempty { vsLegendNrow = Last (Just n) }
-- | [日本語]: 図サイズ ('Length')。 bare 数値リテラルは @Num Length@ 経由で
-- __pt__ (@width 600@ = 600pt)。 mm で書きたいときは 'widthMm' / 'heightMm'、
-- その他の単位は @width (7 *~ inch)@ / 'widthUnit' を使う。
-- [English]: The figure size ('Length'). A bare numeric literal is
-- __pt__ via @Num Length@ (@width 600@ = 600pt). To write in mm, use
-- 'widthMm' / 'heightMm'; for other units use @width (7 *~ inch)@ /
-- 'widthUnit'.
width, height :: Length -> VisualSpec
width = widthUnit
height = heightUnit
-- | [日本語]: 図サイズ (mm 直接)。 @widthMm 180@ = 180mm。 A4 で 'width' の
-- bare が pt に変わったので、 従来の mm 指定はこちらへ移行する。
-- [English]: The figure size, directly in mm. @widthMm 180@ = 180mm.
-- Since A4 changed the bare value of 'width' to pt, migrate previous mm
-- specifications to this instead.
widthMm, heightMm :: Double -> VisualSpec
widthMm w = widthUnit (w *~ mm)
heightMm h = heightUnit (h *~ mm)
-- | [日本語]: 図サイズ (単位明示)。 @widthUnit (7 *~ inch)@ /
-- @widthUnit (800 *~ px)@。
-- [English]: The figure size with an explicit unit. Examples:
-- @widthUnit (7 *~ inch)@ / @widthUnit (800 *~ px)@.
widthUnit, heightUnit :: Length -> VisualSpec
widthUnit l = mempty { vsWidth = Last (Just l) }
heightUnit l = mempty { vsHeight = Last (Just l) }
-- | [日本語]: 描画 dpi (px backend は px=pt×dpi/72)。 @plot <> dpi 300@。 既定
-- 96。 PDF は無視。
-- [English]: The rendering dpi (px backends use px = pt × dpi/72).
-- Example: @plot <> dpi 300@. Default 96; ignored by PDF.
dpi :: Double -> VisualSpec
dpi d = mempty { vsDpi = Last (Just d) }
-- | [日本語]: coord_fixed(ratio) 相当。 panel の 高/幅 比 (aspect) を固定。
-- 指定時は可用域内で aspect を保つ最大 panel を取り中央寄せ (ggplot
-- Coord$aspect)。
-- [English]: The equivalent of coord_fixed(ratio). Fixes the panel's
-- height/width ratio (aspect). When specified, takes the largest panel
-- that preserves the aspect within the available area and centers it
-- (ggplot's Coord$aspect).
aspectRatio :: Double -> VisualSpec
aspectRatio a = mempty { vsAspect = Last (Just a) }
-- | [日本語]: coord_flip。 x/y 軸を入れ替える (= 横棒グラフ等)。 ggplot
-- coord_flip() 相当。
-- [English]: coord_flip. Swaps the x/y axes (used for horizontal bar
-- charts, etc.). Equivalent to ggplot's coord_flip().
--
-- > bar "cat" "y" `layer'` purePlot <> coordFlip
coordFlip :: VisualSpec
coordFlip = mempty { vsCoord = Last (Just CoordFlip) }
-- | [日本語]: 極座標 (= ggplot @coord_polar(theta="x")@)。 データ x を角度
-- (0..2π、 上始点・時計回り)、 データ y を半径に写す。 line/point は
-- radar / spiral に。
-- [English]: Polar coordinates (like ggplot's
-- @coord_polar(theta="x")@). Maps data x to angle (0..2π, starting at
-- the top, clockwise) and data y to radius. Turns line/point marks into
-- a radar / spiral shape.
coordPolar :: VisualSpec
coordPolar = mempty { vsCoord = Last (Just (CoordPolarX defaultPolarOpts)) }
-- | [日本語]: 極座標 (= ggplot @coord_polar(theta="y")@)。 データ y を角度、
-- データ x を半径に写す。 単一カテゴリの stacked bar と併せると円グラフに
-- なる。
-- [English]: Polar coordinates (like ggplot's
-- @coord_polar(theta="y")@). Maps data y to angle and data x to radius.
-- Combined with a single-category stacked bar, it becomes a pie chart.
coordPolarY :: VisualSpec
coordPolarY = mempty { vsCoord = Last (Just (CoordPolarY defaultPolarOpts)) }
-- | [日本語]: 極座標 (theta="x") を開始角・回転方向つきで (= ggplot
-- @coord_polar(theta="x", start=, direction=)@)。 @start@ = θ=0 の向き
-- (rad、 0 = 真上)、 @direction@ = 回転方向の符号 (+1 = 時計回り \/ 既定、
-- -1 = 反時計回り)。 'coordPolar' は @coordPolarWith 0 1@ と等価。
-- [English]: Polar coordinates (theta="x") with a start angle and direction
-- (like ggplot's @coord_polar(theta="x", start=, direction=)@). @start@ is
-- the direction of theta=0 (radians, 0 = up), @direction@ the sign of the
-- rotation (+1 clockwise / default, -1 counter-clockwise). 'coordPolar'
-- equals @coordPolarWith 0 1@.
coordPolarWith :: Double -> Double -> VisualSpec
coordPolarWith start dir =
mempty { vsCoord = Last (Just (CoordPolarX (PolarOpts start dir))) }
-- | [日本語]: 極座標 (theta="y") を開始角・回転方向つきで。 'coordPolarWith' の
-- theta="y" 版 (= ggplot @coord_polar(theta="y", start=, direction=)@)。
-- [English]: Polar coordinates (theta="y") with a start angle and direction;
-- the theta="y" counterpart of 'coordPolarWith' (like ggplot's
-- @coord_polar(theta="y", start=, direction=)@).
coordPolarYWith :: Double -> Double -> VisualSpec
coordPolarYWith start dir =
mempty { vsCoord = Last (Just (CoordPolarY (PolarOpts start dir))) }
-- | [日本語]: 三角座標 (= 組成データ用の ternary plot)。 3 成分 (a,b,c) を正
-- 三角形の 3 頂点へ写す。 ★ Phase 64 §3 (A11-A13) で投影/grid を実装する。
-- A10 時点では 'Coord' の枝と JSON codec のみが揃った状態 (render は未接続)。
-- [English]: Ternary coordinates (a ternary plot for compositional data),
-- mapping three components (a,b,c) to the corners of an equilateral
-- triangle. Projection/grid are implemented in Phase 64 §3 (A11-A13); at A10
-- only the 'Coord' constructor and JSON codec exist (rendering not wired up).
coordTernary :: VisualSpec
coordTernary = mempty { vsCoord = Last (Just (CoordTernary defaultTernaryOpts)) }
-- | [日本語]: ★ Phase 69 A4: 三角座標を向きつきで指定する ('coordPolarWith' の対)。
-- @coordTernaryWith clockwise rotate@ で、 clockwise=True なら左下↔右下 を反転
-- (巡回方向を逆に)、 rotate=0/120/240 でどの成分を上頂点に置くかを回す。
-- 'coordTernary' は @coordTernaryWith False 0@ と等価。 通常は encZ から coord が
-- 推論されるので、 本 setter は向きを変えたいときだけ足せばよい。
-- [English]: ★ Phase 69 A4: ternary coordinates with an explicit orientation
-- (the counterpart of 'coordPolarWith'). @coordTernaryWith clockwise rotate@:
-- clockwise=True flips bottom-left ↔ bottom-right (reversing the precession),
-- rotate 0/120/240 cycles which component sits at the top vertex. 'coordTernary'
-- equals @coordTernaryWith False 0@. Since the coord is normally inferred from
-- encZ, add this setter only when you want to change the orientation.
coordTernaryWith :: Bool -> Int -> VisualSpec
coordTernaryWith clockwise rotate =
mempty { vsCoord = Last (Just (CoordTernary (TernaryOpts clockwise rotate))) }
-- | [日本語]: X 軸反転 (= ggplot @scale_x_reverse()@)。 大値が左、 小値が右へ。
-- coord_flip と独立合成可。
-- [English]: Reverses the X axis (like ggplot's @scale_x_reverse()@):
-- large values move to the left, small values to the right. Composes
-- independently of coord_flip.
--
-- > scatter "x" "y" `layer'` purePlot <> reverseX
reverseX :: VisualSpec
reverseX = mempty { vsReverseX = Last (Just True) }
-- | [日本語]: Y 軸反転 (= ggplot @scale_y_reverse()@)。 大値が下、 小値が上へ。
-- [English]: Reverses the Y axis (like ggplot's @scale_y_reverse()@):
-- large values move to the bottom, small values to the top.
reverseY :: VisualSpec
reverseY = mempty { vsReverseY = Last (Just True) }
-- | [日本語]: X 軸 zoom (= ggplot @coord_cartesian(xlim=c(lo,hi))@)。
-- 'Graphics.Hgg.Spec.Axis.axisRange' (= scale limits、 範囲外データを切る) と異なり __データを落とさず__
-- 表示範囲だけを [lo,hi] に上書きする。 stat (regression/density
-- 等) は全データから計算され、 範囲外の glyph は panel に clip される。
-- numeric 軸のみ有効。
-- [English]: X-axis zoom (like ggplot's
-- @coord_cartesian(xlim=c(lo,hi))@). Unlike 'Graphics.Hgg.Spec.Axis.axisRange' (scale limits,
-- which drops out-of-range data), this __keeps all data__ and only
-- overrides the display range to [lo,hi]. Stats (regression/density,
-- etc.) are still computed from the full data, and out-of-range glyphs
-- are clipped to the panel. Only effective for numeric axes.
coordCartesianX :: Double -> Double -> VisualSpec
coordCartesianX lo hi = mempty { vsCoordXLim = Last (Just (lo, hi)) }
-- | [日本語]: Y 軸 zoom (= ggplot @coord_cartesian(ylim=c(lo,hi))@)。
-- [English]: Y-axis zoom (like ggplot's
-- @coord_cartesian(ylim=c(lo,hi))@).
coordCartesianY :: Double -> Double -> VisualSpec
coordCartesianY lo hi = mempty { vsCoordYLim = Last (Just (lo, hi)) }
-- | [日本語]: X/Y 同時 zoom (= ggplot @coord_cartesian(xlim=,ylim=)@)。
-- 'coordCartesianX' と 'coordCartesianY' の合成。
-- [English]: Simultaneous X/Y zoom (like ggplot's
-- @coord_cartesian(xlim=,ylim=)@). A combination of 'coordCartesianX'
-- and 'coordCartesianY'.
coordCartesian :: Double -> Double -> Double -> Double -> VisualSpec
coordCartesian xlo xhi ylo yhi = coordCartesianX xlo xhi <> coordCartesianY ylo yhi
-- | [日本語]: 軸 (X / Y) 設定の合成 helper。
-- [English]: A helper for composing axis (X / Y) settings.
--
-- > example = ... <> xAxis logAxis <> yAxis (linearAxis <> ...)
xAxis, yAxis :: AxisSpec -> VisualSpec
xAxis a = mempty { vsXAxis = Last (Just a) }
yAxis a = mempty { vsYAxis = Last (Just a) }
-- | [日本語]: P5: 右側 Y 軸の AxisSpec (= dual Y を有効化)。
-- [English]: P5: the AxisSpec for the right-side Y axis (enables dual
-- Y).
yAxisRight :: AxisSpec -> VisualSpec
yAxisRight a = mempty { vsYAxisRight = Last (Just a) }
-- | [日本語]: P5: layer を右側 Y 軸に紐付ける。
-- [English]: P5: binds a layer to the right-side Y axis.
toRightY :: Layer
toRightY = mempty { lyYAxisSide = Last (Just YAxisRight) }
-- | [日本語]: P5: layer を左側 Y 軸に紐付ける (= default なので通常不要)。
-- [English]: P5: binds a layer to the left-side Y axis (usually
-- unnecessary since it is the default).
toLeftY :: Layer
toLeftY = mempty { lyYAxisSide = Last (Just YAxisLeft) }
-- | [日本語]: 参照線を 1 本追加 (= 重ねがけで複数本)。
-- [English]: Adds a single reference line (stack multiple by layering).
--
-- > example = ... <> refLine RefIdentity <> refLine (RefHorizontalAt 0)
refLine :: ReferenceLine -> VisualSpec
refLine rl = mempty { vsRefLines = [rl] }
-- | [日本語]: shortcut。
-- [English]: A shortcut.
refIdentity :: VisualSpec
refIdentity = refLine RefIdentity
refHorizontal :: Double -> VisualSpec
refHorizontal y = refLine (RefHorizontalAt y)
refVertical :: Double -> VisualSpec
refVertical x = refLine (RefVerticalAt x)
-- | [日本語]: scatter の周辺に X/Y 両方の histogram。
-- [English]: Adds both X and Y marginal histograms around a scatter.
marginal :: VisualSpec
marginal = mempty { vsMarginal = Last (Just (defaultMarginalSpec { msShowX = True, msShowY = True })) }
-- | [日本語]: 周辺 histogram X 軸のみ。
-- [English]: Only the X-axis marginal histogram.
marginalX :: VisualSpec
marginalX = mempty { vsMarginal = Last (Just (defaultMarginalSpec { msShowX = True })) }
-- | [日本語]: 周辺 histogram Y 軸のみ。
-- [English]: Only the Y-axis marginal histogram.
marginalY :: VisualSpec
marginalY = mempty { vsMarginal = Last (Just (defaultMarginalSpec { msShowY = True })) }
-- ===========================================================================
-- Font customization setter (= hgg-frontend-settings-spec v0.1 §1.3)
-- ===========================================================================
titleFont :: FontSpec -> VisualSpec
titleFont f = mempty { vsTitleFont = Last (Just f) }
axisLabelFont :: FontSpec -> VisualSpec
axisLabelFont f = mempty { vsAxisLabelFont = Last (Just f) }
tickFont :: FontSpec -> VisualSpec
tickFont f = mempty { vsTickFont = Last (Just f) }
legendFont :: FontSpec -> VisualSpec
legendFont f = mempty { vsLegendFont = Last (Just f) }