Skip to content

Commit

Permalink
conditionally require FreeType, FileIO, Unitful and `ImageInTer…
Browse files Browse the repository at this point in the history
…minal` through weak deps or `Requires` (#332)

* weak deps

* make FileIO weak

* consistency

* update

* add `UnitfulExt`

* rework `import / using` when using weak exts

* rework docs

* change to minor bump

* add compat `save_image` for Plots

* change notes
  • Loading branch information
t-bltg authored Feb 8, 2023
1 parent e221cee commit 51e71dd
Show file tree
Hide file tree
Showing 19 changed files with 328 additions and 185 deletions.
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

0 comments on commit 51e71dd

Please sign in to comment.