Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

conditionally require FreeType, FileIO, Unitful and ImageInTerminal through weak deps or Requires #332

Merged
merged 10 commits into from
Feb 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/format-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
- name: Install JuliaFormatter and format
run: |
julia -e 'using Pkg; Pkg.add(PackageSpec(name="JuliaFormatter"))'
julia -e 'using JuliaFormatter; format(["src", "test"], verbose=true)'
julia -e 'using JuliaFormatter; format(["src", "ext", "test"], verbose=true)'
- name: Format check
run: |
julia -e '
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## [3.4] - 2023-02-08
### Changed
- Rework conditional glue dependencies (`Unitful`, `FreeType`, `FileIO`) through weak deps to improve latency.

## [3.3] - 2022-11-17
### Added
- Group digits on integer values (thousands) for readability (configured using `thousands_separator`).
Expand Down
20 changes: 16 additions & 4 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ ColorTypes = "3da002f7-5984-5a60-b8a6-cbb66c0b333f"
Contour = "d38c429a-6771-53c6-b99e-75d170b6e991"
Crayons = "a8cc5b0e-0ffa-5ad4-8c14-923d3ee1735f"
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
FreeType = "b38be410-82b0-50bf-ab77-7b57e271db43"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
MarchingCubes = "299715c1-40a9-479a-aaf9-4a633d36f717"
NaNMath = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3"
Expand All @@ -22,7 +20,6 @@ SnoopPrecompile = "66db9d55-30c0-4569-8b51-7e840670fc0c"
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"

[compat]
ColorSchemes = "^3.19"
Expand All @@ -31,6 +28,7 @@ Contour = "0.5 - 0.6"
Crayons = "^4.1"
FileIO = "1"
FreeType = "4"
ImageInTerminal = "0.5"
MarchingCubes = "0.1"
NaNMath = "0.3, 1"
Requires = "1"
Expand All @@ -40,9 +38,22 @@ StatsBase = "0.33"
Unitful = "1"
julia = "1.6"

[weakdeps]
FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
FreeType = "b38be410-82b0-50bf-ab77-7b57e271db43"
ImageInTerminal = "d8c32880-2388-543b-8c61-d9f865259254"
Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"

[extensions]
FreeTypeExt = ["FileIO", "FreeType"]
ImageInTerminalExt = "ImageInTerminal"
UnitfulExt = "Unitful"

[extras]
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0"
FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549"
FreeType = "b38be410-82b0-50bf-ab77-7b57e271db43"
ImageInTerminal = "d8c32880-2388-543b-8c61-d9f865259254"
ImageMagick = "6218d12a-5da1-5696-b52f-db25d2ecc6d1"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
Expand All @@ -51,6 +62,7 @@ StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
TestImages = "5e47fb64-e119-507b-a336-dd2b206d9990"
TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f"
Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d"

[targets]
test = ["Aqua", "DataFrames", "ImageInTerminal", "ImageMagick", "Random", "ReferenceTests", "StableRNGs", "Test", "TestImages", "TimerOutputs"]
test = ["Aqua", "DataFrames", "FileIO", "FreeType", "ImageInTerminal", "ImageMagick", "Random", "ReferenceTests", "StableRNGs", "Test", "TestImages", "TimerOutputs", "Unitful"]
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ Here is a list of the main high-level functions for common scenarios:
<img src="https://github.com/JuliaPlots/UnicodePlots.jl/raw/unicodeplots-docs/2.10/lineplot3.png" width="450"><br>


Physical quantities of [`Unitful.jl`](https://github.com/PainterQubits/Unitful.jl) are supported on a subset of plotting methods.
Physical quantities of [`Unitful.jl`](https://github.com/PainterQubits/Unitful.jl) are supported on a subset of plotting methods (supported through [package extensions - weak dependencies](https://pkgdocs.julialang.org/dev/creating-packages/#Conditional-loading-of-code-in-packages-(Extensions)).

One can adjust the plot `height` and `width` to the current terminal size by using `height=:auto` and/or `width=:auto`.

Expand Down Expand Up @@ -333,10 +333,10 @@ Here is a list of the main high-level functions for common scenarios:
<details open>
<summary><a name=image-plot></a><b>Image Plot</b></summary><br>

Draws an image, surround it with decorations. `Sixel` are supported (experimental) under a compatible terminal through [`ImageInTerminal`](https://github.com/JuliaImages/ImageInTerminal.jl) (which must be loaded before `UnicodePlots`).
Draws an image, surround it with decorations. `Sixel` are supported (experimental) under a compatible terminal through [`ImageInTerminal`](https://github.com/JuliaImages/ImageInTerminal.jl) (which must be imported before `UnicodePlots`).

```julia
using ImageInTerminal # mandatory
import ImageInTerminal # mandatory (triggers glue code loading)
using TestImages
imageplot(testimage("monarch_color_256"), title="monarch")
```
Expand Down Expand Up @@ -405,7 +405,7 @@ julia> Pkg.add("UnicodePlots")
<details>
<summary></a><b>...</b></summary><br>

Saving plots as `png` or `txt` files using the `savefig` command is supported (saving as `png` is experimental and resulting images might slightly change without warnings).
Saving plots as `png` or `txt` files using the `savefig` command is supported (saving as `png` is experimental and requires `import FreeType, FileIO` before loading `UnicodePlots`).

To recover the plot as a string with ansi color codes use `string(p; color=true)`.
</details>
Expand Down
8 changes: 4 additions & 4 deletions docs/gen_docs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ main() = begin
heatmap1 = ("Heatmap", "heatmap(repeat(collect(0:10)', outer=(11, 1)), zlabel=\"z\")"),
heatmap2 = ("Heatmap", "heatmap(collect(0:30) * collect(0:30)', xfact=.1, yfact=.1, xoffset=-1.5, colormap=:inferno)"),
imageplot1 = ("ImagePlot", """
using ImageInTerminal # mandatory
import ImageInTerminal # mandatory (triggers glue code loading)
using TestImages
imageplot(testimage("monarch_color_256"), title="monarch")
"""),
Expand Down Expand Up @@ -363,7 +363,7 @@ $(indent(examples.lineplot2))

$(indent(examples.lineplot3))

Physical quantities of [`Unitful.jl`](https://github.com/PainterQubits/Unitful.jl) are supported on a subset of plotting methods.
Physical quantities of [`Unitful.jl`](https://github.com/PainterQubits/Unitful.jl) are supported on a subset of plotting methods (supported through [package extensions - weak dependencies](https://pkgdocs.julialang.org/dev/creating-packages/#Conditional-loading-of-code-in-packages-(Extensions)).

One can adjust the plot `height` and `width` to the current terminal size by using `height=:auto` and/or `width=:auto`.

Expand Down Expand Up @@ -516,7 +516,7 @@ $(indent(examples.heatmap2))
<details open>
$(summary("Image Plot"))

Draws an image, surround it with decorations. `Sixel` are supported (experimental) under a compatible terminal through [`ImageInTerminal`](https://github.com/JuliaImages/ImageInTerminal.jl) (which must be loaded before `UnicodePlots`).
Draws an image, surround it with decorations. `Sixel` are supported (experimental) under a compatible terminal through [`ImageInTerminal`](https://github.com/JuliaImages/ImageInTerminal.jl) (which must be imported before `UnicodePlots`).

$(indent(examples.imageplot1))
</details>
Expand Down Expand Up @@ -559,7 +559,7 @@ $(indent(examples.isosurface))
<details>
$(summary(nothing))

Saving plots as `png` or `txt` files using the `savefig` command is supported (saving as `png` is experimental and resulting images might slightly change without warnings).
Saving plots as `png` or `txt` files using the `savefig` command is supported (saving as `png` is experimental and requires `import FreeType, FileIO` before loading `UnicodePlots`).

To recover the plot as a string with ansi color codes use `string(p; color=true)`.
</details>
Expand Down
51 changes: 29 additions & 22 deletions src/freetype.jl → ext/FreeTypeExt.jl
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# adapted from github.com/JuliaGraphics/FreeTypeAbstraction.jl/blob/master/src/rendering.jl
# credits to the `FreeTypeAbstraction` authors

module FreeTypeRendering
module FreeTypeExt

using ..StaticArrays
using ..ColorTypes
using FreeType

export get_font_face, render_string!
import UnicodePlots
UnicodePlots.@ext_imp_use :using FreeType
UnicodePlots.@ext_imp_use :import FileIO
using StaticArrays
using ColorTypes

const REGULAR_STYLES = "regular", "normal", "medium", "standard", "roman", "book"
const FT_LIB = FT_Library[C_NULL]
Expand Down Expand Up @@ -85,21 +85,6 @@ fallback_fonts() =

const FT_FONTS = Dict{String,FTFont}()

function get_font_face(font = nothing, fallback = fallback_fonts())
face = nothing
for name ∈ filter(!isnothing, (font, "JuliaMono", fallback...))
if (face = get(FT_FONTS, name, nothing)) ≡ nothing
if (ft = find_font(name)) ≢ nothing
face = FT_FONTS[name] = ft
break # found new font, cache and return it
end
else
break # found in cache
end
end
face
end

"""
Match a font using the user-specified search string. Each part of the search string
is searched in the family name first which has to match once to include the font
Expand Down Expand Up @@ -287,7 +272,7 @@ Uses the conventions of freetype.org/freetype2/docs/glyphs/glyphs-3.html
* `gstr`: background string or array of chars (for background sizing)
* `incx`: extra x spacing
"""
function render_string!(
function UnicodePlots.render_string!(
img::AbstractMatrix{T},
fstr::Union{AbstractVector{Char},String},
face::FTFont,
Expand Down Expand Up @@ -487,4 +472,26 @@ function __init__()
nothing
end

function UnicodePlots.get_font_face(font = nothing, fallback = fallback_fonts())
face = nothing
for name ∈ filter(!isnothing, (font, "JuliaMono", fallback...))
if (face = get(FT_FONTS, name, nothing)) ≡ nothing
if (ft = find_font(name)) ≢ nothing
face = FT_FONTS[name] = ft
break # found new font, cache and return it
end
else
break # found in cache
end
end
face
end

UnicodePlots.save_image(fn::AbstractString, args...; kw...) =
FileIO.save(fn, args...; kw...)

# compat for Plots
UnicodePlots.save_image(io::IO, args...; kw...) =
FileIO.save(FileIO.Stream{FileIO.format"PNG"}(io), args...; kw...)

end # module
26 changes: 26 additions & 0 deletions ext/ImageInTerminalExt.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module ImageInTerminalExt

import UnicodePlots
UnicodePlots.@ext_imp_use :import ImageInTerminal
using ColorTypes

UnicodePlots.sixel_encode(args...; kw...) = ImageInTerminal.sixel_encode(args...; kw...) # COV_EXCL_LINE
UnicodePlots.imshow(args...; kw...) = ImageInTerminal.imshow(args...; kw...)

function UnicodePlots.terminal_specs(img)
char_h = char_w = nothing
# COV_EXCL_START
if ImageInTerminal.choose_sixel(img)
ans = ImageInTerminal.Sixel.TerminalTools.query_terminal("\e[16t", stdout)
if ans isa String && (m = match(r"\e\[6;(\d+);(\d+)t", ans)) ≢ nothing
char_h, char_w = tryparse.(Int, m.captures)
end
end
# COV_EXCL_STOP
char_h ≢ nothing && char_w ≢ nothing, char_h, char_w
end

UnicodePlots.imageplot(img::AbstractArray{<:Colorant}; kw...) =
UnicodePlots.Plot(UnicodePlots.ImageGraphics(img); border = :corners, kw...)

end # module
80 changes: 80 additions & 0 deletions ext/UnitfulExt.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
module UnitfulExt

import UnicodePlots
import UnicodePlots: KEYWORDS, Plot, Canvas
UnicodePlots.@ext_imp_use :import Unitful Quantity RealOrRealQuantity ustrip unit

function unit_str(x, fancy)
io = IOContext(PipeBuffer(), :fancy_exponent => fancy)
show(io, unit(x))
read(io, String)
end

unit_label(label::AbstractString, unit::AbstractString) =
(lab_strip = rstrip(label)) |> isempty ? unit : "$lab_strip ($unit)"
unit_label(label::AbstractString, unit::Nothing) = rstrip(label)

number_unit(x::Union{AbstractVector,Number}, args...) = x, nothing
number_unit(x::AbstractVector{<:Quantity}, fancy = true) =
ustrip.(x), unit_str(first(x), fancy)
number_unit(x::Quantity, fancy = true) = ustrip(x), unit_str(x, fancy)

# ---------------------------------------------------------------------------- #
# lineplot
function UnicodePlots.lineplot(
x::AbstractVector{<:RealOrRealQuantity},
y::AbstractVector{<:Quantity};
unicode_exponent::Bool = KEYWORDS.unicode_exponent,
xlabel = KEYWORDS.xlabel,
ylabel = KEYWORDS.ylabel,
kw...,
)
x, ux = number_unit(x, unicode_exponent)
y, uy = number_unit(y, unicode_exponent)
UnicodePlots.lineplot(
ustrip.(x),
ustrip.(y);
xlabel = unit_label(xlabel, ux),
ylabel = unit_label(ylabel, uy),
unicode_exponent,
kw...,
)
end

UnicodePlots.lineplot!(
plot::Plot{<:Canvas},
x::AbstractVector{<:RealOrRealQuantity},
y::AbstractVector{<:Quantity};
kw...,
) = UnicodePlots.lineplot!(plot, ustrip.(x), ustrip.(y); kw...)

# ---------------------------------------------------------------------------- #
# scatterplot
function UnicodePlots.scatterplot(
x::AbstractVector{<:RealOrRealQuantity},
y::AbstractVector{<:Quantity};
unicode_exponent::Bool = KEYWORDS.unicode_exponent,
xlabel = KEYWORDS.xlabel,
ylabel = KEYWORDS.ylabel,
kw...,
)
x, ux = number_unit(x, unicode_exponent)
y, uy = number_unit(y, unicode_exponent)
UnicodePlots.scatterplot(
ustrip.(x),
ustrip.(y);
xlabel = unit_label(xlabel, ux),
ylabel = unit_label(ylabel, uy),
unicode_exponent,
kw...,
)
end

UnicodePlots.scatterplot!(
plot::Plot{<:Canvas},
x::AbstractVector{<:RealOrRealQuantity},
y::AbstractVector{<:Quantity};
kw...,
) = UnicodePlots.scatterplot!(plot, ustrip.(x), ustrip.(y); kw...)

end # module
22 changes: 14 additions & 8 deletions src/UnicodePlots.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,14 @@ using Crayons
using Printf
using Dates

import Unitful: Quantity, RealOrRealQuantity, ustrip, unit
import StatsBase: Histogram, fit, percentile, sturges
import SparseArrays: AbstractSparseMatrix, findnz
import Base: RefValue

import MarchingCubes
import ColorSchemes
import Requires
import NaNMath
import Contour
import FileIO

# composite types
export Plot,
Expand Down Expand Up @@ -95,8 +92,6 @@ include("canvas/heatmapcanvas.jl")
include("description.jl")
include("volume.jl")

include("freetype.jl")
using .FreeTypeRendering
include("plot.jl")
include("show.jl")

Expand All @@ -115,6 +110,8 @@ include("interface/boxplot.jl")
include("interface/polarplot.jl")
include("interface/imageplot.jl")

isdefined(Base, :get_extension) || import Requires: @require

function __init__()
if (terminal_24bit() || forced_24bit()) && !forced_8bit()
truecolors!()
Expand All @@ -123,9 +120,18 @@ function __init__()
colors256!()
faintcolors!()
end
Requires.@require ImageInTerminal = "d8c32880-2388-543b-8c61-d9f865259254" begin
imageplot(img::AbstractArray{<:Colorant}; kw...) =
Plot(ImageGraphics(img); border = :corners, kw...)
@static if !isdefined(Base, :get_extension) # COV_EXCL_LINE
@require ImageInTerminal = "d8c32880-2388-543b-8c61-d9f865259254" include(
normpath(@__DIR__, "..", "ext", "ImageInTerminalExt.jl"),
)
@require FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" begin
@require FreeType = "b38be410-82b0-50bf-ab77-7b57e271db43" begin
include(normpath(@__DIR__, "..", "ext", "FreeTypeExt.jl"))
end
end
@require Unitful = "1986cc42-f94f-5a68-af5c-568840ba703d" include(
normpath(@__DIR__, "..", "ext", "UnitfulExt.jl"),
)
end
nothing
end
Expand Down
Loading