이 제출물을 팔로우합니다
- 팔로우하는 게시물 피드에서 업데이트를 확인할 수 있습니다
- 정보 수신 기본 설정에 따라 이메일을 받을 수 있습니다
classdef BestCornerLegend < handle
%BESTCORNERLEGEND Keep a legend in the emptiest interior corner of its axes.
% Restricts a legend to the four corners (NE/NW/SE/SW) and parks it in the one
% whose legend-sized box covers the fewest plotted vertices, re-choosing on its
% own as the figure is resized. Works on linear and log axes.
%
% Two usage patterns:
%
% 1) NEW FIGURES -- register explicitly while building the plot, by axes (a legend
% is created for you) or by axes + a legend you already made. This is the form
% the plotting code uses:
%
% BestCornerLegend(ax) % create a legend on AX, then manage it
% BestCornerLegend(ax, lgd) % manage the legend you already made
%
% 2) EXISTING / LEGACY FIGURES -- plot and call legend() the normal way, then hand
% the figure over for interactive use with a single call:
%
% BestCornerLegend.attach % manage every legend in the current figure
% BestCornerLegend.attach(fig) % ... in figure FIG
% BestCornerLegend.attach('all') % ... in every open figure
%
% Either way the legend then tracks the data automatically, and Ctrl+L (Cmd+L on
% macOS) forces a re-snap on demand -- the key is the class Constant Hotkey,
% editable in the source. The full static API shares one TARGET scope argument
% (optional, defaults to gcf):
%
% objs = BestCornerLegend.attach(target) % manage existing legends in TARGET
% objs = BestCornerLegend.reposition(target) % re-snap managed legends in TARGET
% n = BestCornerLegend.detach(target) % stop managing; leave legends put
% tf = BestCornerLegend.isAttached(target) % which candidate axes are managed
%
% TARGET omitted / [] -> the current figure (never creates one)
% figure handle(s) -> those figures' axes
% axes handle(s) -> those axes
% 'all' -> every open figure (incl. hidden-handle figures)
%
% ATTACH manages only axes that ALREADY have a legend -- it never creates one (use
% the constructor for that). It reuses an existing placer rather than stacking a
% second one on the same axes, and it always repositions immediately after wiring:
% construction snaps once, but that snap is a no-op until the figure is realized,
% so re-attaching an already-managed axes forces an authoritative re-snap (which
% also recovers a legend that was dragged, or a missed resize). REPOSITION and
% DETACH act only on axes that are currently managed.
%
% DETACH deletes the listeners, removes the Ctrl+L handler (once no managed legend
% remains in the figure), clears the axes appdata, and LEAVES THE LEGEND WHERE IT
% SITS. It is the same teardown that runs automatically when the axes is destroyed.
%
% The placer reasons in normalized axes coordinates. The legend footprint is a
% fraction of the axes box that depends on the figure's *realized* pixel size
% (a legend is text-sized in points while the axes scales with the figure), so
% it is re-measured whenever the figure is actually laid out:
%
% * SizeChanged - first paint, docking/undocking, manual resize. Handled
% automatically: resizing genuinely changes the footprint.
%
% View changes (zoom/pan, or new data) are NOT tracked automatically, and a
% resize event could in principle be missed. Press Ctrl+L (Cmd+L on macOS) to
% force a re-evaluation of the best corner for every managed legend in the
% current figure -- a one-shot safety net, not a toggle.
%
% Redraw debounce (timer-free): a resize fires SizeChanged repeatedly. The
% plotted vertices are measured once and cached - re-read only when the data,
% limits, or scale change, or on a forced re-snap (Ctrl+L / reposition /
% attach) - and Location is only reassigned, the part that forces a legend
% relayout and a redraw, when the chosen corner actually changes. Nested events
% during our own work are coalesced by a reentrancy guard. A true trailing-edge
% time debounce would need a timer, which is deliberately avoided here.
%
% Lifetime: the placer is stored in the axes appdata and deleted (with its
% listeners) when the axes OR its legend is destroyed, so nothing leaks. Use
% detach() to stop managing without destroying either.
%
% Assumptions / limitations:
% * Construct after the series are plotted; the placement reflects the data
% present at construction. Changing the plotted data later is NOT tracked
% (the resize cache is intentionally cheap and value-blind) -- reconstruct
% the manager, or press Ctrl+L, after editing the data.
% * Only VISIBLE children with equal-length XData/YData are counted (lines,
% scatter, stairs, ...). Hidden series (Visible='off'), patches, surfaces,
% images, and other geometry are ignored.
% * Overlap is scored by counting VERTICES inside each candidate box, not by
% segment-box intersection; a sparsely sampled curve can pass through a
% corner with no vertex inside it. The box is inflated by Padding (default
% 1.15) so a corner is chosen only when data clears it with margin.
% * On a log axis, non-positive data are treated as off-axis (dropped).
% * Reversed axes are handled: XDir/YDir='reverse' still maps the legend to
% the emptiest on-screen corner.
% * The placer must stay in its original figure. Reparenting the axes into a
% different figure leaves the resize listener and the Ctrl+L shortcut bound
% to the old figure; detach() and re-create after such a move.
% * Editing this classdef while instances exist in open figures requires
% "close all; clear classes"; the old instances cannot be hot-reloaded.
인용 양식
Andrew Harmon (2026). Best Corner Legend (https://kr.mathworks.com/matlabcentral/fileexchange/184023-best-corner-legend), MATLAB Central File Exchange. 검색 날짜: .
| 버전 | 퍼블리시됨 | 릴리스 정보 | Action |
|---|---|---|---|
| 1.0.0 |
