From 84be7a1667921334da8fe88b9882d110f439bcf7 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sat, 4 Feb 2023 19:45:08 +0100 Subject: [PATCH 01/10] weak deps --- .github/workflows/format-check.yml | 2 +- Project.toml | 12 ++- src/freetype.jl => ext/FreeTypeExt.jl | 15 ++- ext/ImageInTerminalExt.jl | 24 +++++ src/UnicodePlots.jl | 14 ++- src/graphics/imagegraphics.jl | 21 ++--- src/show.jl | 4 + test/runtests.jl | 2 +- test/tst_freetype.jl | 126 +++++++++++++++++--------- 9 files changed, 146 insertions(+), 74 deletions(-) rename src/freetype.jl => ext/FreeTypeExt.jl (98%) create mode 100644 ext/ImageInTerminalExt.jl diff --git a/.github/workflows/format-check.yml b/.github/workflows/format-check.yml index 0743b872..fe0a72fd 100644 --- a/.github/workflows/format-check.yml +++ b/.github/workflows/format-check.yml @@ -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 ' diff --git a/Project.toml b/Project.toml index d513c9e8..67e5c1b8 100644 --- a/Project.toml +++ b/Project.toml @@ -12,7 +12,6 @@ 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" @@ -40,9 +39,18 @@ StatsBase = "0.33" Unitful = "1" julia = "1.6" +[weakdeps] +FreeType = "b38be410-82b0-50bf-ab77-7b57e271db43" +ImageInTerminal = "d8c32880-2388-543b-8c61-d9f865259254" + +[extensions] +FreeTypeExt = "FreeType" +ImageInTerminalExt = "ImageInTerminal" + [extras] Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595" DataFrames = "a93c6f00-e57d-5684-b7b6-d8193f3e46c0" +FreeType = "b38be410-82b0-50bf-ab77-7b57e271db43" ImageInTerminal = "d8c32880-2388-543b-8c61-d9f865259254" ImageMagick = "6218d12a-5da1-5696-b52f-db25d2ecc6d1" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" @@ -53,4 +61,4 @@ TestImages = "5e47fb64-e119-507b-a336-dd2b206d9990" TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" [targets] -test = ["Aqua", "DataFrames", "ImageInTerminal", "ImageMagick", "Random", "ReferenceTests", "StableRNGs", "Test", "TestImages", "TimerOutputs"] +test = ["Aqua", "DataFrames", "FreeType", "ImageInTerminal", "ImageMagick", "Random", "ReferenceTests", "StableRNGs", "Test", "TestImages", "TimerOutputs"] diff --git a/src/freetype.jl b/ext/FreeTypeExt.jl similarity index 98% rename from src/freetype.jl rename to ext/FreeTypeExt.jl index d5024f69..c37e68a2 100644 --- a/src/freetype.jl +++ b/ext/FreeTypeExt.jl @@ -1,13 +1,12 @@ # 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 +isdefined(Base, :get_extension) ? (using FreeType) : (using ..FreeType) +using StaticArrays +using ColorTypes const REGULAR_STYLES = "regular", "normal", "medium", "standard", "roman", "book" const FT_LIB = FT_Library[C_NULL] @@ -85,7 +84,7 @@ fallback_fonts() = const FT_FONTS = Dict{String,FTFont}() -function get_font_face(font = nothing, fallback = fallback_fonts()) +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 @@ -287,7 +286,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, diff --git a/ext/ImageInTerminalExt.jl b/ext/ImageInTerminalExt.jl new file mode 100644 index 00000000..79a07936 --- /dev/null +++ b/ext/ImageInTerminalExt.jl @@ -0,0 +1,24 @@ +module ImageInTerminalExt + +import UnicodePlots +isdefined(Base, :get_extension) ? (import ImageInTerminal) : (import ..ImageInTerminal) +using ColorTypes + +UnicodePlots.sixel_encode(args...; kw...) = ImageInTerminal.sixel_encode(args...; kw...) +UnicodePlots.imshow(args...; kw...) = ImageInTerminal.imshow(args...; kw...) + +function UnicodePlots.terminal_specs(img) + 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) + return char_h ≢ nothing && char_w ≢ nothing, char_h, char_w + end + end + false, nothing, nothing +end + +UnicodePlots.imageplot(img::AbstractArray{<:Colorant}; kw...) = + UnicodePlots.Plot(UnicodePlots.ImageGraphics(img); border = :corners, kw...) + +end diff --git a/src/UnicodePlots.jl b/src/UnicodePlots.jl index c3bb3810..cf4849d2 100644 --- a/src/UnicodePlots.jl +++ b/src/UnicodePlots.jl @@ -95,8 +95,6 @@ include("canvas/heatmapcanvas.jl") include("description.jl") include("volume.jl") -include("freetype.jl") -using .FreeTypeRendering include("plot.jl") include("show.jl") @@ -115,6 +113,8 @@ include("interface/boxplot.jl") include("interface/polarplot.jl") include("interface/imageplot.jl") +isdefined(Base, :get_extension) || import Requires + function __init__() if (terminal_24bit() || forced_24bit()) && !forced_8bit() truecolors!() @@ -123,9 +123,13 @@ 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) + Requires.@require ImageInTerminal = "d8c32880-2388-543b-8c61-d9f865259254" include( + "../ext/ImageInTerminalExt.jl", + ) + Requires.@require FreeType = "b38be410-82b0-50bf-ab77-7b57e271db43" include( + "../ext/FreeTypeExt.jl", + ) end nothing end diff --git a/src/graphics/imagegraphics.jl b/src/graphics/imagegraphics.jl index b4c770cf..ff905375 100644 --- a/src/graphics/imagegraphics.jl +++ b/src/graphics/imagegraphics.jl @@ -24,19 +24,14 @@ end @inline nrows(c::ImageGraphics) = first(c.encoded_size) @inline ncols(c::ImageGraphics) = last(c.encoded_size) +# `ImageInTerminalExt` placeholders +function terminal_specs end +function sixel_encode end +function imshow end + function preprocess!(io::IO, c::ImageGraphics) ctx = IOContext(PipeBuffer(), :displaysize => displaysize(io)) - c.sixel[] = false - char_h = char_w = nothing # determine the terminal caret size, in pixels - # COV_EXCL_START - if ImageInTerminal.choose_sixel(c.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) - c.sixel[] = char_h ≢ nothing && char_w ≢ nothing - end - end - # COV_EXCL_STOP + c.sixel[], char_h, char_w = terminal_specs(c.img) postprocess = c -> begin c.encoded_size .= (0, 0) empty!(c.chars) @@ -49,7 +44,7 @@ function preprocess!(io::IO, c::ImageGraphics) # COV_EXCL_START # it is better to encode the whole image in a single pass # otherwise, issues with the last line (see previous implementation) - ImageInTerminal.sixel_encode(ctx, c.img) + sixel_encode(ctx, c.img) push!(c.chars, read(ctx, String) |> collect) length(1:char_h:img_h), ceil(Int, img_w / char_w) # COV_EXCL_STOP @@ -69,7 +64,7 @@ function preprocess!(io::IO, c::ImageGraphics) end nothing end - ImageInTerminal.imshow(ctx, c.img; callback) + imshow(ctx, c.img; callback) length(c.chars), length(c.chars |> first) end postprocess diff --git a/src/show.jl b/src/show.jl index 3aa26508..9f160674 100644 --- a/src/show.jl +++ b/src/show.jl @@ -398,6 +398,10 @@ function _show(end_io::IO, print_nocol, print_color, p::Plot) ) end +# `FreeTypeExt` placeholders +function get_font_face end +function render_string! end + """ png_image(p::Plot, font = nothing, pixelsize = 32, transparent = true, foreground = nothing, background = nothing, bounding_box = nothing, bounding_box_glyph = nothing) diff --git a/test/runtests.jl b/test/runtests.jl index f6501fad..7a841eda 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,4 +1,4 @@ -using ImageInTerminal +using ImageInTerminal, FreeType using UnicodePlots, Test import UnicodePlots: lines!, points!, pixel!, nrows, ncols diff --git a/test/tst_freetype.jl b/test/tst_freetype.jl index fe782d71..495cff33 100644 --- a/test/tst_freetype.jl +++ b/test/tst_freetype.jl @@ -1,67 +1,80 @@ -const FR = UnicodePlots.FreeTypeRendering -push!(FR.VALID_FONTPATHS, joinpath(@__DIR__, "fonts")) +const FTE = if isdefined(Base, :get_extension) + Base.get_extension(UnicodePlots, :FreeTypeExt) +else + UnicodePlots.FreeTypeExt +end +push!(FTE.VALID_FONTPATHS, joinpath(@__DIR__, "fonts")) @testset "init and done" begin - @test_throws ErrorException FR.ft_init() - @test FR.ft_done() - @test_throws ErrorException FR.ft_done() - @test FR.ft_init() + @test_throws ErrorException FTE.ft_init() + @test FTE.ft_done() + @test_throws ErrorException FTE.ft_done() + @test FTE.ft_init() end -face = FR.find_font("hack") # must occur after `ft_init` and `ft_done` ! +face = FTE.find_font("hack") # must occur after `ft_init` and `ft_done` ! @testset "basics" begin @test face ≢ nothing @test :size in propertynames(face) @test repr(face) == "FTFont (family = Hack, style = Regular)" - FR.set_pixelsize(face, 64) # should be the default - img, extent = FR.render_face(face, 'C', 64) + FTE.set_pixelsize(face, 64) # should be the default + img, extent = FTE.render_face(face, 'C', 64) @test size(img) == (30, 49) @test typeof(img) == Array{UInt8,2} - @test FR.hadvance(extent) == 39 - @test FR.vadvance(extent) == 62 - @test FR.inkheight(extent) == 49 - @test FR.hbearing_ori_to_left(extent) == 4 - @test FR.hbearing_ori_to_top(extent) == 48 - @test FR.leftinkbound(extent) == 4 - @test FR.rightinkbound(extent) == 34 - @test FR.bottominkbound(extent) == -1 - @test FR.topinkbound(extent) == 48 + @test FTE.hadvance(extent) == 39 + @test FTE.vadvance(extent) == 62 + @test FTE.inkheight(extent) == 49 + @test FTE.hbearing_ori_to_left(extent) == 4 + @test FTE.hbearing_ori_to_top(extent) == 48 + @test FTE.leftinkbound(extent) == 4 + @test FTE.rightinkbound(extent) == 34 + @test FTE.bottominkbound(extent) == -1 + @test FTE.topinkbound(extent) == 48 - a = FR.render_string!(zeros(UInt8, 20, 100), "helgo", face, 10, 10, 10) + a = UnicodePlots.render_string!(zeros(UInt8, 20, 100), "helgo", face, 10, 10, 10) @test any(a[3:12, :] .≠ 0) @test all(a[vcat(1:2, 13:20), :] .== 0) @test any(a[:, 11:40] .≠ 0) @test all(a[:, vcat(1:10, 41:100)] .== 0) - a = FR.render_string!(zeros(UInt8, 20, 100), "helgo", face, 10, 15, 70) + a = UnicodePlots.render_string!(zeros(UInt8, 20, 100), "helgo", face, 10, 15, 70) @test any(a[8:17, :] .≠ 0) @test all(a[vcat(1:7, 18:20), :] .== 0) @test any(a[:, 71:100] .≠ 0) @test all(a[:, 1:70] .== 0) - a = FR.render_string!(zeros(Float32, 20, 100), "helgo", face, 10, 10, 50) + a = UnicodePlots.render_string!(zeros(Float32, 20, 100), "helgo", face, 10, 10, 50) @test maximum(a) ≤ 1.0 - a = FR.render_string!(zeros(Float64, 20, 100), "helgo", face, 10, 10, 50) + a = UnicodePlots.render_string!(zeros(Float64, 20, 100), "helgo", face, 10, 10, 50) @test maximum(a) ≤ 1.0 - @test FR.render_string!(zeros(UInt8, 20, 100), "helgo", face, 10, 25, 80) isa Matrix + @test UnicodePlots.render_string!(zeros(UInt8, 20, 100), "helgo", face, 10, 25, 80) isa + Matrix end @testset "ways to access glyphs" begin - i = FR.glyph_index(face, 'A') - @test FR.glyph_index(face, i) == i - @test FR.glyph_index(face, "A") == i + i = FTE.glyph_index(face, 'A') + @test FTE.glyph_index(face, i) == i + @test FTE.glyph_index(face, "A") == i end @testset "alignements" begin - a = FR.render_string!(zeros(UInt8, 20, 100), "helgo", face, 10, 10, 50, valign = :vtop) + a = UnicodePlots.render_string!( + zeros(UInt8, 20, 100), + "helgo", + face, + 10, + 10, + 50, + valign = :vtop, + ) @test all(a[1:10, :] .== 0) @test any(a[11:20, :] .≠ 0) - a = FR.render_string!( + a = UnicodePlots.render_string!( zeros(UInt8, 20, 100), "helgo", face, @@ -72,7 +85,7 @@ end ) @test all(a[vcat(1:5, 16:end), :] .== 0) @test any(a[6:15, :] .≠ 0) - a = FR.render_string!( + a = UnicodePlots.render_string!( zeros(UInt8, 20, 100), "helgo", face, @@ -83,7 +96,7 @@ end ) @test all(a[vcat(1:2, 13:end), :] .== 0) @test any(a[3:12, :] .≠ 0) - a = FR.render_string!( + a = UnicodePlots.render_string!( zeros(UInt8, 20, 100), "helgo", face, @@ -94,10 +107,18 @@ end ) @test any(a[1:10, :] .≠ 0) @test all(a[11:20, :] .== 0) - a = FR.render_string!(zeros(UInt8, 20, 100), "helgo", face, 10, 10, 50, halign = :hleft) + a = UnicodePlots.render_string!( + zeros(UInt8, 20, 100), + "helgo", + face, + 10, + 10, + 50, + halign = :hleft, + ) @test all(a[:, 1:50] .== 0) @test any(a[:, 51:100] .≠ 0) - a = FR.render_string!( + a = UnicodePlots.render_string!( zeros(UInt8, 20, 100), "helgo", face, @@ -108,7 +129,7 @@ end ) @test all(a[:, vcat(1:35, 66:end)] .== 0) @test any(a[:, 36:65] .≠ 0) - a = FR.render_string!( + a = UnicodePlots.render_string!( zeros(UInt8, 20, 100), "helgo", face, @@ -120,7 +141,7 @@ end @test any(a[:, 1:50] .≠ 0) @test all(a[:, 51:100] .== 0) - FR.render_string!( + UnicodePlots.render_string!( zeros(UInt8, 20, 100), "helgo", face, @@ -133,9 +154,17 @@ end end @testset "foreground / background colors" begin - a = FR.render_string!(zeros(UInt8, 20, 100), "helgo", face, 10, 10, 50, fcolor = 0x80) + a = UnicodePlots.render_string!( + zeros(UInt8, 20, 100), + "helgo", + face, + 10, + 10, + 50, + fcolor = 0x80, + ) @test maximum(a) ≤ 0x80 - a = FR.render_string!( + a = UnicodePlots.render_string!( zeros(UInt8, 20, 100), "helgo", face, @@ -146,13 +175,22 @@ end bcolor = 0x40, ) @test any(a .== 0x40) - a = FR.render_string!(fill(0x01, 20, 100), "helgo", face, 10, 10, 50, bcolor = nothing) + a = UnicodePlots.render_string!( + fill(0x01, 20, 100), + "helgo", + face, + 10, + 10, + 50, + bcolor = nothing, + ) @test !any(a .== 0x00) end @testset "array of grays" begin - @test FR.render_string!(zeros(Gray, 20, 100), "helgo", face, 10, 10, 50) isa Matrix - @test FR.render_string!( + @test UnicodePlots.render_string!(zeros(Gray, 20, 100), "helgo", face, 10, 10, 50) isa + Matrix + @test UnicodePlots.render_string!( zeros(Gray{Float64}, 20, 100), "helgo", face, @@ -166,7 +204,7 @@ end @testset "per char background / colors" begin for str ∈ ("helgo", collect("helgo")) fcolor = [RGB{Float32}(rand(3)...) for _ ∈ 1:length(str)] - @test FR.render_string!( + @test UnicodePlots.render_string!( zeros(RGB{Float32}, 20, 100), str, face, @@ -177,7 +215,7 @@ end ) isa Matrix gcolor = [RGB{Float32}(rand(3)...) for _ ∈ 1:length(str)] gstr = fill('█', length(str)) - @test FR.render_string!( + @test UnicodePlots.render_string!( zeros(RGB{Float32}, 20, 100), str, face, @@ -192,7 +230,7 @@ end end @testset "draw bounding boxes" begin - @test FR.render_string!( + @test UnicodePlots.render_string!( zeros(RGB{Float32}, 20, 100), "helgo", face, @@ -206,7 +244,7 @@ end end @testset "misc" begin - @test FR.fallback_fonts() isa Tuple + @test FTE.fallback_fonts() isa Tuple end -pop!(FR.VALID_FONTPATHS) +pop!(FTE.VALID_FONTPATHS) From ef905969fe83b4719dc3b995a8e8e10fc938160e Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sat, 4 Feb 2023 19:56:54 +0100 Subject: [PATCH 02/10] make FileIO weak --- Project.toml | 7 ++++--- ext/FreeTypeExt.jl | 8 +++++++- ext/ImageInTerminalExt.jl | 2 +- src/UnicodePlots.jl | 9 +++++---- src/show.jl | 3 ++- test/runtests.jl | 2 +- test/tst_quality.jl | 2 +- 7 files changed, 21 insertions(+), 12 deletions(-) diff --git a/Project.toml b/Project.toml index 67e5c1b8..eafc4473 100644 --- a/Project.toml +++ b/Project.toml @@ -11,7 +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" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" MarchingCubes = "299715c1-40a9-479a-aaf9-4a633d36f717" NaNMath = "77ba4419-2d1f-58cd-9bb1-8ffee604a2e3" @@ -40,16 +39,18 @@ Unitful = "1" julia = "1.6" [weakdeps] +FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" FreeType = "b38be410-82b0-50bf-ab77-7b57e271db43" ImageInTerminal = "d8c32880-2388-543b-8c61-d9f865259254" [extensions] -FreeTypeExt = "FreeType" +FreeTypeExt = ["FileIO", "FreeType"] ImageInTerminalExt = "ImageInTerminal" [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" @@ -61,4 +62,4 @@ TestImages = "5e47fb64-e119-507b-a336-dd2b206d9990" TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f" [targets] -test = ["Aqua", "DataFrames", "FreeType", "ImageInTerminal", "ImageMagick", "Random", "ReferenceTests", "StableRNGs", "Test", "TestImages", "TimerOutputs"] +test = ["Aqua", "DataFrames", "FileIO", "FreeType", "ImageInTerminal", "ImageMagick", "Random", "ReferenceTests", "StableRNGs", "Test", "TestImages", "TimerOutputs"] diff --git a/ext/FreeTypeExt.jl b/ext/FreeTypeExt.jl index c37e68a2..7dba8cd9 100644 --- a/ext/FreeTypeExt.jl +++ b/ext/FreeTypeExt.jl @@ -4,7 +4,11 @@ module FreeTypeExt import UnicodePlots -isdefined(Base, :get_extension) ? (using FreeType) : (using ..FreeType) +@static if isdefined(Base, :get_extension) + using FreeType, FileIO +else + using ..FreeType, ..FileIO +end using StaticArrays using ColorTypes @@ -99,6 +103,8 @@ function UnicodePlots.get_font_face(font = nothing, fallback = fallback_fonts()) face end +UnicodePlots.save_png(args...; kw...) = FileIO.save(args...; kw...) + """ 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 diff --git a/ext/ImageInTerminalExt.jl b/ext/ImageInTerminalExt.jl index 79a07936..8dd193ec 100644 --- a/ext/ImageInTerminalExt.jl +++ b/ext/ImageInTerminalExt.jl @@ -1,7 +1,7 @@ module ImageInTerminalExt import UnicodePlots -isdefined(Base, :get_extension) ? (import ImageInTerminal) : (import ..ImageInTerminal) +isdefined(Base, :get_extension) ? (import ImageInTerminal) : (import ..ImageInTerminal) using ColorTypes UnicodePlots.sixel_encode(args...; kw...) = ImageInTerminal.sixel_encode(args...; kw...) diff --git a/src/UnicodePlots.jl b/src/UnicodePlots.jl index cf4849d2..5f363d99 100644 --- a/src/UnicodePlots.jl +++ b/src/UnicodePlots.jl @@ -18,7 +18,6 @@ import ColorSchemes import Requires import NaNMath import Contour -import FileIO # composite types export Plot, @@ -127,9 +126,11 @@ function __init__() Requires.@require ImageInTerminal = "d8c32880-2388-543b-8c61-d9f865259254" include( "../ext/ImageInTerminalExt.jl", ) - Requires.@require FreeType = "b38be410-82b0-50bf-ab77-7b57e271db43" include( - "../ext/FreeTypeExt.jl", - ) + Requires.@require FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" begin + Requires.@require FreeType = "b38be410-82b0-50bf-ab77-7b57e271db43" begin + include("../ext/FreeTypeExt.jl") + end + end end nothing end diff --git a/src/show.jl b/src/show.jl index 9f160674..cfbf7972 100644 --- a/src/show.jl +++ b/src/show.jl @@ -401,6 +401,7 @@ end # `FreeTypeExt` placeholders function get_font_face end function render_string! end +function save_png end """ png_image(p::Plot, font = nothing, pixelsize = 32, transparent = true, foreground = nothing, background = nothing, bounding_box = nothing, bounding_box_glyph = nothing) @@ -596,7 +597,7 @@ function savefig(p::Plot, filename::AbstractString; color::Bool = false, kw...) elseif ext == ".png" # `png_image` can fail if fonts are not found: a warning has already been # thrown there, so just bail out at this stage - (img = png_image(p; kw...)) ≢ nothing && FileIO.save(filename, img) + (img = png_image(p; kw...)) ≢ nothing && save_png(filename, img) else "extension \"$ext\" is unsupported: `savefig` only supports writing to `txt` or `png` files" |> ArgumentError |> diff --git a/test/runtests.jl b/test/runtests.jl index 7a841eda..d6a0071b 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,4 +1,4 @@ -using ImageInTerminal, FreeType +using ImageInTerminal, FreeType, FileIO # weak deps, or @require using UnicodePlots, Test import UnicodePlots: lines!, points!, pixel!, nrows, ncols diff --git a/test/tst_quality.jl b/test/tst_quality.jl index c0f73eb1..c4b1616a 100644 --- a/test/tst_quality.jl +++ b/test/tst_quality.jl @@ -1,5 +1,5 @@ @testset "Aqua" begin # JuliaTesting/Aqua.jl/issues/77 - Aqua.test_all(UnicodePlots; ambiguities = false) + Aqua.test_all(UnicodePlots; ambiguities = false, project_toml_formatting = false) Aqua.test_ambiguities(UnicodePlots) end From 7ba7c46c324897204ac731e3d8bd2b2c339055fe Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sat, 4 Feb 2023 20:18:46 +0100 Subject: [PATCH 03/10] consistency --- Project.toml | 1 + ext/ImageInTerminalExt.jl | 6 +++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index eafc4473..51693086 100644 --- a/Project.toml +++ b/Project.toml @@ -29,6 +29,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" diff --git a/ext/ImageInTerminalExt.jl b/ext/ImageInTerminalExt.jl index 8dd193ec..a0c6c1e6 100644 --- a/ext/ImageInTerminalExt.jl +++ b/ext/ImageInTerminalExt.jl @@ -1,7 +1,11 @@ module ImageInTerminalExt import UnicodePlots -isdefined(Base, :get_extension) ? (import ImageInTerminal) : (import ..ImageInTerminal) +@static if isdefined(Base, :get_extension) + import ImageInTerminal +else + import ..ImageInTerminal +end using ColorTypes UnicodePlots.sixel_encode(args...; kw...) = ImageInTerminal.sixel_encode(args...; kw...) From 3f92541a1916adf20e3df9b07a1c6b23654f0ba1 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sat, 4 Feb 2023 21:49:28 +0100 Subject: [PATCH 04/10] update --- ext/FreeTypeExt.jl | 2 +- ext/ImageInTerminalExt.jl | 8 +++++--- src/UnicodePlots.jl | 2 +- src/graphics/imagegraphics.jl | 2 +- src/show.jl | 6 +++--- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/ext/FreeTypeExt.jl b/ext/FreeTypeExt.jl index 7dba8cd9..edbcace5 100644 --- a/ext/FreeTypeExt.jl +++ b/ext/FreeTypeExt.jl @@ -103,7 +103,7 @@ function UnicodePlots.get_font_face(font = nothing, fallback = fallback_fonts()) face end -UnicodePlots.save_png(args...; kw...) = FileIO.save(args...; kw...) +UnicodePlots.save_image(args...; kw...) = FileIO.save(args...; kw...) """ Match a font using the user-specified search string. Each part of the search string diff --git a/ext/ImageInTerminalExt.jl b/ext/ImageInTerminalExt.jl index a0c6c1e6..01f72755 100644 --- a/ext/ImageInTerminalExt.jl +++ b/ext/ImageInTerminalExt.jl @@ -8,18 +8,20 @@ else end using ColorTypes -UnicodePlots.sixel_encode(args...; kw...) = ImageInTerminal.sixel_encode(args...; kw...) +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) - return char_h ≢ nothing && char_w ≢ nothing, char_h, char_w end end - false, nothing, nothing + # COV_EXCL_STOP + char_h ≢ nothing && char_w ≢ nothing, char_h, char_w end UnicodePlots.imageplot(img::AbstractArray{<:Colorant}; kw...) = diff --git a/src/UnicodePlots.jl b/src/UnicodePlots.jl index 5f363d99..0d2533c6 100644 --- a/src/UnicodePlots.jl +++ b/src/UnicodePlots.jl @@ -122,7 +122,7 @@ function __init__() colors256!() faintcolors!() end - @static if !isdefined(Base, :get_extension) + @static if !isdefined(Base, :get_extension) # COV_EXCL_LINE Requires.@require ImageInTerminal = "d8c32880-2388-543b-8c61-d9f865259254" include( "../ext/ImageInTerminalExt.jl", ) diff --git a/src/graphics/imagegraphics.jl b/src/graphics/imagegraphics.jl index ff905375..c4d68d80 100644 --- a/src/graphics/imagegraphics.jl +++ b/src/graphics/imagegraphics.jl @@ -24,7 +24,7 @@ end @inline nrows(c::ImageGraphics) = first(c.encoded_size) @inline ncols(c::ImageGraphics) = last(c.encoded_size) -# `ImageInTerminalExt` placeholders +# # generic functions for `ImageInTerminalExt` function terminal_specs end function sixel_encode end function imshow end diff --git a/src/show.jl b/src/show.jl index cfbf7972..b38b6ac3 100644 --- a/src/show.jl +++ b/src/show.jl @@ -398,10 +398,10 @@ function _show(end_io::IO, print_nocol, print_color, p::Plot) ) end -# `FreeTypeExt` placeholders +# generic functions for `FreeTypeExt` function get_font_face end function render_string! end -function save_png end +function save_image end """ png_image(p::Plot, font = nothing, pixelsize = 32, transparent = true, foreground = nothing, background = nothing, bounding_box = nothing, bounding_box_glyph = nothing) @@ -597,7 +597,7 @@ function savefig(p::Plot, filename::AbstractString; color::Bool = false, kw...) elseif ext == ".png" # `png_image` can fail if fonts are not found: a warning has already been # thrown there, so just bail out at this stage - (img = png_image(p; kw...)) ≢ nothing && save_png(filename, img) + (img = png_image(p; kw...)) ≢ nothing && save_image(filename, img) else "extension \"$ext\" is unsupported: `savefig` only supports writing to `txt` or `png` files" |> ArgumentError |> From adc63dbedaddb71e27885a078bb230e727c32096 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 5 Feb 2023 11:13:28 +0100 Subject: [PATCH 05/10] add `UnitfulExt` --- CHANGELOG.md | 4 ++ Project.toml | 6 ++- ext/FreeTypeExt.jl | 34 +++++++-------- ext/ImageInTerminalExt.jl | 2 +- ext/UnitfulExt.jl | 84 ++++++++++++++++++++++++++++++++++++ src/UnicodePlots.jl | 17 ++++---- src/common.jl | 16 ------- src/interface/lineplot.jl | 29 ------------- src/interface/scatterplot.jl | 29 ------------- test/runtests.jl | 2 +- test/tst_common.jl | 22 ++++++---- test/tst_quality.jl | 7 ++- 12 files changed, 140 insertions(+), 112 deletions(-) create mode 100644 ext/UnitfulExt.jl diff --git a/CHANGELOG.md b/CHANGELOG.md index 8d95330b..ac4ad9f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## [4.0] - 2023-02-xx +### Changed +- Rework conditional 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`). diff --git a/Project.toml b/Project.toml index 51693086..a1916217 100644 --- a/Project.toml +++ b/Project.toml @@ -20,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" @@ -43,10 +42,12 @@ julia = "1.6" 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" @@ -61,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", "FileIO", "FreeType", "ImageInTerminal", "ImageMagick", "Random", "ReferenceTests", "StableRNGs", "Test", "TestImages", "TimerOutputs"] +test = ["Aqua", "DataFrames", "FileIO", "FreeType", "ImageInTerminal", "ImageMagick", "Random", "ReferenceTests", "StableRNGs", "Test", "TestImages", "TimerOutputs", "Unitful"] diff --git a/ext/FreeTypeExt.jl b/ext/FreeTypeExt.jl index edbcace5..6cb1c259 100644 --- a/ext/FreeTypeExt.jl +++ b/ext/FreeTypeExt.jl @@ -88,23 +88,6 @@ fallback_fonts() = const FT_FONTS = Dict{String,FTFont}() -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(args...; kw...) = FileIO.save(args...; kw...) - """ 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 @@ -492,4 +475,21 @@ 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(args...; kw...) = FileIO.save(args...; kw...) + +end # module diff --git a/ext/ImageInTerminalExt.jl b/ext/ImageInTerminalExt.jl index 01f72755..2ce73e87 100644 --- a/ext/ImageInTerminalExt.jl +++ b/ext/ImageInTerminalExt.jl @@ -27,4 +27,4 @@ end UnicodePlots.imageplot(img::AbstractArray{<:Colorant}; kw...) = UnicodePlots.Plot(UnicodePlots.ImageGraphics(img); border = :corners, kw...) -end +end # module diff --git a/ext/UnitfulExt.jl b/ext/UnitfulExt.jl new file mode 100644 index 00000000..164a1f3e --- /dev/null +++ b/ext/UnitfulExt.jl @@ -0,0 +1,84 @@ +module UnitfulExt + +import UnicodePlots +import UnicodePlots: KEYWORDS, Plot, Canvas +@static if isdefined(Base, :get_extension) + import Unitful: Quantity, RealOrRealQuantity, ustrip, unit +else + import ..Unitful: Quantity, RealOrRealQuantity, ustrip, unit +end + +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 diff --git a/src/UnicodePlots.jl b/src/UnicodePlots.jl index 0d2533c6..d5ae1e3a 100644 --- a/src/UnicodePlots.jl +++ b/src/UnicodePlots.jl @@ -8,14 +8,12 @@ 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 @@ -112,7 +110,7 @@ include("interface/boxplot.jl") include("interface/polarplot.jl") include("interface/imageplot.jl") -isdefined(Base, :get_extension) || import Requires +isdefined(Base, :get_extension) || import Requires: @require function __init__() if (terminal_24bit() || forced_24bit()) && !forced_8bit() @@ -123,14 +121,17 @@ function __init__() faintcolors!() end @static if !isdefined(Base, :get_extension) # COV_EXCL_LINE - Requires.@require ImageInTerminal = "d8c32880-2388-543b-8c61-d9f865259254" include( - "../ext/ImageInTerminalExt.jl", + @require ImageInTerminal = "d8c32880-2388-543b-8c61-d9f865259254" include( + normpath(@__DIR__, "..", "ext", "ImageInTerminalExt.jl"), ) - Requires.@require FileIO = "5789e2e9-d7fb-5bc7-8068-2c6fae9b9549" begin - Requires.@require FreeType = "b38be410-82b0-50bf-ab77-7b57e271db43" begin - include("../ext/FreeTypeExt.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 diff --git a/src/common.jl b/src/common.jl index 9b16a8d1..1cd9ccc4 100644 --- a/src/common.jl +++ b/src/common.jl @@ -377,22 +377,6 @@ ceil_base(x, b) = round_base(x, b, RoundUp) round_base(x::T, b, ::RoundingMode{:Down}) where {T} = T(b^floor(log(b, x))) round_base(x::T, b, ::RoundingMode{:Up}) where {T} = T(b^ceil(log(b, x))) -function unit_str(x, fancy) - io = IOContext(PipeBuffer(), :fancy_exponent => fancy) - show(io, unit(x)) - read(io, String) -end - -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) -number_unit(x::AbstractVector, args...) = x, nothing -number_unit(x::Number, args...) = x, nothing - -unit_label(label::AbstractString, unit::AbstractString) = - (lab_strip = rstrip(label)) |> isempty ? unit : "$lab_strip ($unit)" -unit_label(label::AbstractString, unit::Nothing) = rstrip(label) - function superscript(s::AbstractString)::String v = collect(s) for (i, k) ∈ enumerate(v) diff --git a/src/interface/lineplot.jl b/src/interface/lineplot.jl index e8b211d9..038ffee5 100644 --- a/src/interface/lineplot.jl +++ b/src/interface/lineplot.jl @@ -183,35 +183,6 @@ end lineplot!(plot::Plot{<:Canvas}, x::AbstractVector{<:TimeType}, y::AbstractVector; kw...) = lineplot!(plot, Dates.value.(x), y; kw...) -# ---------------------------------------------------------------------------- # -# Unitful -function 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) - lineplot( - ustrip.(x), - ustrip.(y); - xlabel = unit_label(xlabel, ux), - ylabel = unit_label(ylabel, uy), - unicode_exponent, - kw..., - ) -end - -lineplot!( - plot::Plot{<:Canvas}, - x::AbstractVector{<:RealOrRealQuantity}, - y::AbstractVector{<:Quantity}; - kw..., -) = lineplot!(plot, ustrip.(x), ustrip.(y); kw...) - # ---------------------------------------------------------------------------- # # slope and intercept function lineplot!(plot::Plot{<:Canvas}, intercept::Number, slope::Number; kw...) diff --git a/src/interface/scatterplot.jl b/src/interface/scatterplot.jl index 97c61575..03ab6286 100644 --- a/src/interface/scatterplot.jl +++ b/src/interface/scatterplot.jl @@ -132,32 +132,3 @@ function scatterplot!(plot::Plot{<:Canvas}, x::AbstractVector, y::AbstractMatrix end plot end - -# ---------------------------------------------------------------------------- # -# Unitful -function 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) - scatterplot( - ustrip.(x), - ustrip.(y); - xlabel = unit_label(xlabel, ux), - ylabel = unit_label(ylabel, uy), - unicode_exponent, - kw..., - ) -end - -scatterplot!( - plot::Plot{<:Canvas}, - x::AbstractVector{<:RealOrRealQuantity}, - y::AbstractVector{<:Quantity}; - kw..., -) = scatterplot!(plot, ustrip.(x), ustrip.(y); kw...) diff --git a/test/runtests.jl b/test/runtests.jl index d6a0071b..d8dc0f8a 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,4 +1,4 @@ -using ImageInTerminal, FreeType, FileIO # weak deps, or @require +using ImageInTerminal, FreeType, FileIO, Unitful # weak deps, or @require using UnicodePlots, Test import UnicodePlots: lines!, points!, pixel!, nrows, ncols diff --git a/test/tst_common.jl b/test/tst_common.jl index e0fc6382..c7fd257f 100644 --- a/test/tst_common.jl +++ b/test/tst_common.jl @@ -285,14 +285,20 @@ end @test UnicodePlots.keywords() isa String end +const UE = if isdefined(Base, :get_extension) + Base.get_extension(UnicodePlots, :UnitfulExt) +else + UnicodePlots.UnitfulExt +end + @testset "units" begin x = [1.0, 2.0, 3.0] - @test UnicodePlots.number_unit(x) == (x, nothing) - @test UnicodePlots.number_unit(1) == (1, nothing) - @test UnicodePlots.number_unit(x * u"°C") == (x, "°C") - @test UnicodePlots.number_unit(1u"°C") == (1, "°C") - - @test UnicodePlots.unit_label(" fancy label ", "Hz") == " fancy label (Hz)" - @test UnicodePlots.unit_label(" ", "dB") == "dB" - @test UnicodePlots.unit_label(" no units ", nothing) == " no units" + @test UE.number_unit(x) == (x, nothing) + @test UE.number_unit(1) == (1, nothing) + @test UE.number_unit(x * u"°C") == (x, "°C") + @test UE.number_unit(1u"°C") == (1, "°C") + + @test UE.unit_label(" fancy label ", "Hz") == " fancy label (Hz)" + @test UE.unit_label(" ", "dB") == "dB" + @test UE.unit_label(" no units ", nothing) == " no units" end diff --git a/test/tst_quality.jl b/test/tst_quality.jl index c4b1616a..3bd7b010 100644 --- a/test/tst_quality.jl +++ b/test/tst_quality.jl @@ -1,5 +1,10 @@ @testset "Aqua" begin # JuliaTesting/Aqua.jl/issues/77 - Aqua.test_all(UnicodePlots; ambiguities = false, project_toml_formatting = false) + Aqua.test_all( + UnicodePlots; + ambiguities = false, + project_toml_formatting = false, # issues since weak deps + stale_deps = !isdefined(Base, :get_extension), # issue with `Requires` not used when weak deps are enabled + ) Aqua.test_ambiguities(UnicodePlots) end From e55237aca254bab5ee5ab6add888584af14d44df Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 5 Feb 2023 12:05:05 +0100 Subject: [PATCH 06/10] rework `import / using` when using weak exts --- ext/FreeTypeExt.jl | 7 ++----- ext/ImageInTerminalExt.jl | 6 +----- ext/UnitfulExt.jl | 6 +----- src/common.jl | 23 +++++++++++++++++++++++ 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/ext/FreeTypeExt.jl b/ext/FreeTypeExt.jl index 6cb1c259..9e025b48 100644 --- a/ext/FreeTypeExt.jl +++ b/ext/FreeTypeExt.jl @@ -4,11 +4,8 @@ module FreeTypeExt import UnicodePlots -@static if isdefined(Base, :get_extension) - using FreeType, FileIO -else - using ..FreeType, ..FileIO -end +UnicodePlots.@ext_imp_use :using FreeType +UnicodePlots.@ext_imp_use :import FileIO using StaticArrays using ColorTypes diff --git a/ext/ImageInTerminalExt.jl b/ext/ImageInTerminalExt.jl index 2ce73e87..fcc5f926 100644 --- a/ext/ImageInTerminalExt.jl +++ b/ext/ImageInTerminalExt.jl @@ -1,11 +1,7 @@ module ImageInTerminalExt import UnicodePlots -@static if isdefined(Base, :get_extension) - import ImageInTerminal -else - import ..ImageInTerminal -end +UnicodePlots.@ext_imp_use :import ImageInTerminal using ColorTypes UnicodePlots.sixel_encode(args...; kw...) = ImageInTerminal.sixel_encode(args...; kw...) # COV_EXCL_LINE diff --git a/ext/UnitfulExt.jl b/ext/UnitfulExt.jl index 164a1f3e..450b95ce 100644 --- a/ext/UnitfulExt.jl +++ b/ext/UnitfulExt.jl @@ -2,11 +2,7 @@ module UnitfulExt import UnicodePlots import UnicodePlots: KEYWORDS, Plot, Canvas -@static if isdefined(Base, :get_extension) - import Unitful: Quantity, RealOrRealQuantity, ustrip, unit -else - import ..Unitful: Quantity, RealOrRealQuantity, ustrip, unit -end +UnicodePlots.@ext_imp_use :import Unitful Quantity RealOrRealQuantity ustrip unit function unit_str(x, fancy) io = IOContext(PipeBuffer(), :fancy_exponent => fancy) diff --git a/src/common.jl b/src/common.jl index 1cd9ccc4..2b32ee95 100644 --- a/src/common.jl +++ b/src/common.jl @@ -198,6 +198,29 @@ const BORDER_COLOR = Ref(:dark_gray) ############################################################################################ # misc + +""" + @ext_imp_use :import Unitful Quantity RealOrRealQuantity + +Equivalent to the following, for `Requires` or weak deps: +``` +@static if isdefined(Base, :get_extension) + import Unitful: Quantity, RealOrRealQuantity +else + import ..Unitful: Quantity, RealOrRealQuantity +end +``` +""" +macro ext_imp_use(imp_use::QuoteNode, mod::Symbol, args...) + dots = ntuple(_ -> :., isdefined(Base, :get_extension) ? 1 : 3) + ex = if length(args) > 0 + Expr(:(:), Expr(dots..., mod), Expr.(:., args)...) + else + Expr(dots..., mod) + end + Expr(imp_use.value, ex) |> esc +end + const FSCALES = (identity = identity, ln = log, log2 = log2, log10 = log10) # forward const ISCALES = (identity = identity, ln = exp, log2 = exp2, log10 = exp10) # inverse const BASES = (identity = nothing, ln = "ℯ", log2 = "2", log10 = "10") From 5a9f4c009e6477e7abe81c96a87cd079b06ea7b7 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 5 Feb 2023 12:47:03 +0100 Subject: [PATCH 07/10] rework docs --- README.md | 4 ++-- docs/gen_docs.jl | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index bacf0129..2263e87c 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ Here is a list of the main high-level functions for common scenarios:
- 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`. @@ -405,7 +405,7 @@ julia> Pkg.add("UnicodePlots")
...
- 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 `using FreeType, FileIO` before loading `UnicodePlots`). To recover the plot as a string with ansi color codes use `string(p; color=true)`.
diff --git a/docs/gen_docs.jl b/docs/gen_docs.jl index dd22bc00..9416aa6f 100644 --- a/docs/gen_docs.jl +++ b/docs/gen_docs.jl @@ -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`. @@ -559,7 +559,7 @@ $(indent(examples.isosurface))
$(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 `using FreeType, FileIO` before loading `UnicodePlots`). To recover the plot as a string with ansi color codes use `string(p; color=true)`.
From b12e041e7a892c8da9c0cc9a7ec92407098c8a6e Mon Sep 17 00:00:00 2001 From: t-bltg Date: Sun, 5 Feb 2023 14:53:52 +0100 Subject: [PATCH 08/10] change to minor bump --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac4ad9f3..ba49f69f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ # Changelog -## [4.0] - 2023-02-xx +## [3.4] - 2023-02-xx ### Changed -- Rework conditional dependencies (`Unitful`, `FreeType`, `FileIO`) through weak deps to improve latency +- Rework conditional glue dependencies (`Unitful`, `FreeType`, `FileIO`) through weak deps to improve latency ## [3.3] - 2022-11-17 ### Added From e8c79285d5a8cf551db6a96574447fa678b09bf3 Mon Sep 17 00:00:00 2001 From: t-bltg Date: Wed, 8 Feb 2023 15:38:16 +0100 Subject: [PATCH 09/10] add compat `save_image` for Plots --- ext/FreeTypeExt.jl | 7 ++++++- test/tst_io.jl | 10 ++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/ext/FreeTypeExt.jl b/ext/FreeTypeExt.jl index 9e025b48..93e2735e 100644 --- a/ext/FreeTypeExt.jl +++ b/ext/FreeTypeExt.jl @@ -487,6 +487,11 @@ function UnicodePlots.get_font_face(font = nothing, fallback = fallback_fonts()) face end -UnicodePlots.save_image(args...; kw...) = FileIO.save(args...; kw...) +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 diff --git a/test/tst_io.jl b/test/tst_io.jl index 458dfd7e..25911fab 100644 --- a/test/tst_io.jl +++ b/test/tst_io.jl @@ -44,6 +44,16 @@ end @test_throws ArgumentError savefig(p, tempname() * ".jpg") end + + # for Plots + if font_found + p = lineplot(1:2) + tmp = tempname() * ".png" + open(tmp, "w") do io + UnicodePlots.save_image(io, UnicodePlots.png_image(p)) + end + @test filesize(tmp) > 1_000 + end end @testset "stringify plot - performance regression" begin From 85314e3fccff98702d41b969f3dd2577e67b243b Mon Sep 17 00:00:00 2001 From: t-bltg Date: Wed, 8 Feb 2023 16:10:24 +0100 Subject: [PATCH 10/10] change notes --- CHANGELOG.md | 4 ++-- README.md | 6 +++--- docs/gen_docs.jl | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ba49f69f..1ae5716b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,8 @@ # Changelog -## [3.4] - 2023-02-xx +## [3.4] - 2023-02-08 ### Changed -- Rework conditional glue dependencies (`Unitful`, `FreeType`, `FileIO`) through weak deps to improve latency +- Rework conditional glue dependencies (`Unitful`, `FreeType`, `FileIO`) through weak deps to improve latency. ## [3.3] - 2022-11-17 ### Added diff --git a/README.md b/README.md index 2263e87c..373a1ffb 100644 --- a/README.md +++ b/README.md @@ -333,10 +333,10 @@ Here is a list of the main high-level functions for common scenarios:
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`). ```julia - using ImageInTerminal # mandatory + import ImageInTerminal # mandatory (triggers glue code loading) using TestImages imageplot(testimage("monarch_color_256"), title="monarch") ``` @@ -405,7 +405,7 @@ julia> Pkg.add("UnicodePlots")
...
- Saving plots as `png` or `txt` files using the `savefig` command is supported (saving as `png` is experimental and requires `using FreeType, FileIO` before loading `UnicodePlots`). + 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)`.
diff --git a/docs/gen_docs.jl b/docs/gen_docs.jl index 9416aa6f..b0172e23 100644 --- a/docs/gen_docs.jl +++ b/docs/gen_docs.jl @@ -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") """), @@ -516,7 +516,7 @@ $(indent(examples.heatmap2))
$(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))
@@ -559,7 +559,7 @@ $(indent(examples.isosurface))
$(summary(nothing)) - Saving plots as `png` or `txt` files using the `savefig` command is supported (saving as `png` is experimental and requires `using FreeType, FileIO` before loading `UnicodePlots`). + 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)`.