Skip to content

Commit

Permalink
Upgraded dependencies; AbstractPlotting -> Makie (#21)
Browse files Browse the repository at this point in the history
* AbstractPlotting -> Makie, bumped compats

* Allow passing args and kwargs to Plots.jl recipe

* Make Makie an optional dependency via package extensions

* Added example for Makie.jl and plots

* Updated install instructions

Closes #12

* Updated book link

Closes #20

* Drop Julia < 1.9 to support package extensions

* Added tests of plot recipes

* Updated README

* Bumped version
jagot authored Nov 8, 2024
1 parent dc5979b commit 68d9571
Showing 8 changed files with 171 additions and 63 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/CI.yml
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ jobs:
fail-fast: false
matrix:
version:
- '1.3'
- '1.9'
- '1'
- 'nightly'
os:
28 changes: 21 additions & 7 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,21 +1,35 @@
name = "ComplexPhasePortrait"
uuid = "38ac1a67-8e16-5889-9a62-b2c9995eb50f"
version = "0.2.1"
version = "0.2.2"

[deps]
AbstractPlotting = "537997a7-5e4e-5d89-9595-2241ea00577e"
Colors = "5ae59095-9a9b-59fe-a467-6f913c188581"
Images = "916415d5-f1e6-5110-898d-aaa5f9f070e0"
IntervalSets = "8197267c-284f-5f27-9208-e0e47529a953"
Reactive = "a223df75-4e93-5b7c-acf9-bdd599c0f4de"
RecipesBase = "3cdcf5f2-1ef4-517c-9805-6587b60abb01"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[weakdeps]
Makie = "ee78f7c6-11fb-53f2-987a-cfe4a2b5a57a"

[extensions]
ComplexPhasePortraitMakieExt = "Makie"

[compat]
AbstractPlotting = "0.17.1"
CairoMakie = "0.11"
Colors = "0.12"
Images = "0.22,0.23"
IntervalSets = "0.3.1, 0.4, 0.5"
Images = "0.22,0.23,0.26"
IntervalSets = "0.3.1, 0.4, 0.5, 0.7"
Makie = "0.20"
Plots = "1.40"
Reactive = "0.8.2"
RecipesBase = "1"
julia = "1.3"
julia = "1.9"

[extras]
CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["CairoMakie", "Plots", "Test"]
56 changes: 54 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -3,13 +3,13 @@
[![Build Status](https://github.com/JuliaHolomorphic/ComplexPhasePortrait.jl/workflows/CI/badge.svg)](https://github.com/JuliaHolomorphic/ComplexPhasePortrait.jl/actions)
[![Coverage](https://codecov.io/gh/JuliaHolomorphic/ComplexPhasePortrait.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/JuliaHolomorphic/ComplexPhasePortrait.jl)

This package is a [Julia](http://julialang.org) implementation of the phase portrait ideas presented in Elias Wegert's book "[Visual Complex Functions](http://www.visual.wegert.com)".
This package is a [Julia](http://julialang.org) implementation of the phase portrait ideas presented in Elias Wegert's book "[Visual Complex Functions](https://doi.org/10.1007/978-3-0348-0180-5)".

## Installation

From the Julia command prompt:
```julia
Pkg.clone("git://github.com/JuliaHolomorphic/ComplexPhasePortrait.jl.git")
] add ComplexPhasePortrait
```

## Examples
@@ -55,3 +55,55 @@ Finally, a conformal grid is given by
img = portrait(fz, PTcgrid)
```
![conformal grid](doc/figures/cgrid.png)

## Plot recipes

ComplexPhasePortrait.jl has support for plotting recipes for
[Plots.jl](https://github.com/JuliaPlots/Plots.jl) and
[Makie.jl](https://github.com/MakieOrg/Makie.jl).

### Plots.jl:
```julia
using Plots
using LaTeXStrings

using ComplexPhasePortrait
using IntervalSets

f = z -> (z - 0.5im)^2 * (z + 0.5+0.5im)/z

phaseplot(-1..1, -1..1, f, PTcgrid, :ctype=>"nist";
xlabel=L"\Re\{z\}", ylabel=L"\Im\{z\}")
```
![Plots.jl example](doc/figures/plots.jl.png)

### Makie.jl

Makie.jl is an optional dependency via package extensions, and the
functionality to plot phase portraits becomes available if Makie.jl or
one of its front-end packages is loaded before
ComplexPhasePortrait.jl:

```julia
using GLMakie

using ComplexPhasePortrait
using IntervalSets

f = z -> (z - 0.5im)^2 * (z + 0.5+0.5im)/z

fig = Figure()
ax = Axis(fig[1, 1], aspect=1)
phase!(ax, -1..1, -1..1, f, portrait_type=PTcgrid, ctyle="nist")
display(fig)
```
![Makie.jl example](doc/figures/makie.jl.png)

Alternatively, one can use the function `phase`:
```julia
phase(x, y, f; kwargs...)
```

`x` and `y` can be vectors or `ClosedInterval`s; in the former case
`f` can be a matrix of appropriate size or a `Function`, in the latter
case, only a `Function` is possible.
Binary file added doc/figures/makie.jl.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/figures/plots.jl.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
43 changes: 43 additions & 0 deletions ext/ComplexPhasePortraitMakieExt.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
module ComplexPhasePortraitMakieExt
using IntervalSets

import Makie
import Makie: plot!, image!, Rectf

using ComplexPhasePortrait
import ComplexPhasePortrait: phase, phase!, _range

Makie.@recipe(Phase) do scene
Makie.Theme(
portrait_type = PTproper,
ctype = "standard"
)
end

function plot!(plot::Phase{Tuple{X,Y,F}}) where {X<:AbstractVector,Y<:AbstractVector,F<:AbstractMatrix}
x,y,f = plot[1:3]
c = portrait(convert(AbstractMatrix{ComplexF64}, f[]),
plot.portrait_type[], ctype=plot.ctype[])

a_x,b_x = first(x[]),last(x[])
a_y,b_y = first(y[]),last(y[])

image!(plot, a_x .. b_x, a_y .. b_y, c, limits = Rectf(a_x,a_y,b_x-a_x,b_y-a_y))
end

function plot!(plot::Phase{Tuple{X,Y,F}}) where {X<:Any,Y<:Any,F<:Function}
x,y,f = plot[1:3]

xv = _range(x[])
yv = _range(y[])

Z = xv .+ im*yv'
v = f[].(Z)[end:-1:1,:] # Why do we need to flip it?

phase!(plot, xv, yv, v,
portrait_type=plot.portrait_type,
ctype=plot.ctype)

plot
end
end
56 changes: 18 additions & 38 deletions src/ComplexPhasePortrait.jl
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
module ComplexPhasePortrait
using Reactive, IntervalSets, RecipesBase
import AbstractPlotting
import Images
import Colors
import Colors: RGB, HSL
import AbstractPlotting: plot!, Scene, AbstractScene, ScenePlot
export portrait,
PTproper, PTcgrid, PTstepphase, PTstepmod, phaseplot
PTproper, PTcgrid, PTstepphase, PTstepmod, phaseplot,
phase, phase!

export (..)

@@ -73,6 +72,8 @@ function portrait(fval::Array{Complex{Float64},2}, ::Type{PTstepphase};
end

function baseArgs(fval::Array{Complex{Float64},2}; kwargs...)
fval = copy(fval)
fval[isnan.(fval)] .= 0
(farg, nphase, cm) = setupPhase(fval; kwargs...)
(m, n) = size(nphase)
img = Array{RGB{Float64}}(undef, m, n)
@@ -148,49 +149,28 @@ portrait(x::ClosedInterval, y::ClosedInterval, f::Function) =

@userplot PhasePlot



@recipe function f(c::PhasePlot)
xin, yin, ff = c.args

na = length(c.args)
args,kwargs = if na > 3
rest = c.args[4:na]
ikw = findall(a -> a isa Pair{Symbol,<:Any}, rest)
rest[filter((ikw), (4:na) .- 3)], rest[ikw]
else
(), (;)
end

xx, yy = _range(xin), _range(yin)
Z = ff.(xx' .+ im.*yy)
yflip := false
@series xx, yy, portrait(Matrix{ComplexF64}(Z[end:-1:1,:]))
@series xx, yy, portrait(Matrix{ComplexF64}(Z[end:-1:1,:]), args...; kwargs...)
end

## Recipe for Makie

@AbstractPlotting.recipe(Phase) do scene
default_theme(scene)
end



function plot!(plot::Phase{Tuple{X,Y,F}}) where {X<:AbstractVector,Y<:AbstractVector,F<:AbstractMatrix}
x, y, f = value.(plot[1:3])::Tuple{X,Y,F}
image!(plot, x, y, portrait(convert(AbstractMatrix{ComplexF64}, f)))
end

# avoid compile time issues
function phase(x::AbstractVector, y::AbstractVector, f::Function; kwds...)
z = x' .+ im.*y
a_x,b_x = first(x),last(x)
a_y,b_y = first(y),last(y)
r = (b_y-a_y)/(b_x-a_x)
N = 400
s = Scene(resolution=(max(N,floor(Int,N/r)), max(N,floor(Int, r *N))))
phase!(s, x, y, f.(z); limits = FRect(a_x,a_y,b_x-a_x,b_y-a_y), kwds...)
end
function phase!(plot::Union{AbstractScene,ScenePlot}, x::AbstractVector, y::AbstractVector, f::Function; kwds...)
z = x' .+ im.*y
phase!(plot, x, y, f.(z); kwds...)
end


phase(x::ClosedInterval, y::ClosedInterval, f::Function; kwds...) =
phase(_range(x), _range(y), f; kwds...)

phase!(plot::Union{AbstractScene,AbstractPlotting.ScenePlot}, x::ClosedInterval, y::ClosedInterval, f::Function; kwds...) =
phase!(plot, _range(x), _range(y), r; kwds...)
# Actual functionality in extension module ComplexPhasePortraitMakieExt
function phase end
function phase! end

end # module
49 changes: 34 additions & 15 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,24 +1,43 @@
using ComplexPhasePortrait, Colors
using Test

nx = 1000
x = range(-1, stop=1, length=nx)
Z = x' .+ reverse(x)*im
using Plots
using CairoMakie

f = z -> (z - 0.5im)^2 * (z + 0.5+0.5im)/z
fz = f.(Z)
@testset "ComplexPhasePortrait" begin
nx = 1000
x = range(-1, stop=1, length=nx)
Z = x' .+ reverse(x)*im

img = portrait(fz)
@test img[1,5] RGB{Float64}(0.11686143572621054,1.0,0.0)
f = z -> (z - 0.5im)^2 * (z + 0.5+0.5im)/z
fz = f.(Z)

img = portrait(fz, ctype="nist")
@test img[1,5] RGB{Float64}(0.4916573971078975,1.0,0.0)
img = portrait(fz)
@test img[1,5] RGB{Float64}(0.11686143572621054,1.0,0.0)

img = portrait(fz, PTstepphase)
@test img[1,5] RGB{Float64}(0.09621043816546014,0.8232864637301505,0.0)
img = portrait(fz, ctype="nist")
@test img[1,5] RGB{Float64}(0.4916573971078975,1.0,0.0)

img = portrait(fz, PTstepmod)
@test img[1,5] RGB{Float64}(0.10275080341985139,0.8792533035498697,0.0)
img = portrait(fz, PTstepphase)
@test img[1,5] RGB{Float64}(0.09621043816546014,0.8232864637301505,0.0)

img = portrait(fz, PTcgrid)
@test img[1,5] RGB{Float64}(0.08803468544171197,0.7533253797083627,0.0)
img = portrait(fz, PTstepmod)
@test img[1,5] RGB{Float64}(0.10275080341985139,0.8792533035498697,0.0)

img = portrait(fz, PTcgrid)
@test img[1,5] RGB{Float64}(0.08803468544171197,0.7533253797083627,0.0)

@testset "Plot recipees" begin
@testset "Plots.jl" begin
for ctype in ["standard", "nist"]
phaseplot(-1..1, -1..1, f, PTcgrid, ctype=ctype; dpi=300)
end
end

@testset "Makie.jl" begin
for ctype in ["standard", "nist"]
phase(-1..1, -1..1, f, portrait_type=PTcgrid, ctype=ctype)
end
end
end
end

4 comments on commit 68d9571

@MikaelSlevinsky
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error while trying to register: Register Failed
@MikaelSlevinsky, it looks like you are not a publicly listed member/owner in the parent organization (JuliaHolomorphic).
If you are a member/owner, you will need to change your membership to public. See GitHub Help

@MikaelSlevinsky
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/123516

Tip: Release Notes

Did you know you can add release notes too? Just add markdown formatted text underneath the comment after the text
"Release notes:" and it will be added to the registry PR, and if TagBot is installed it will also be added to the
release that TagBot creates. i.e.

@JuliaRegistrator register

Release notes:

## Breaking changes

- blah

To add them here just re-invoke and the PR will be updated.

Tagging

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.2.2 -m "<description of version>" 68d9571498d2e9ae37f60b99a4305bfe95056995
git push origin v0.2.2

Please sign in to comment.