Skip to content

Commit 3497f53

Browse files
authored
Merge pull request #1037 from JuliaControl/pzgrid
add pz grid option to makie pzmap
2 parents 73518ce + b3c4b78 commit 3497f53

File tree

1 file changed

+49
-6
lines changed

1 file changed

+49
-6
lines changed

lib/ControlSystemsBase/ext/ControlSystemsBaseMakieExt.jl

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,10 @@ function CSMakie.pzmap(args...; kwargs...)
2727
fig = Figure()
2828
CSMakie.pzmap!(fig, args...; kwargs...)
2929
end
30-
function CSMakie.pzmap!(fig, systems::Union{LTISystem, AbstractVector{<:LTISystem}};
31-
hz=false, kwargs...)
30+
function CSMakie.pzmap!(fig, systems::Union{LTISystem, AbstractVector{<:LTISystem}};
31+
hz=false, grid=true,
32+
ζ = [0.1:0.1:0.6; 1/sqrt(2); 0.82; 0.9; 0.96],
33+
ωn=Float64[], kwargs...)
3234
systems_vec = systems isa AbstractVector ? systems : [systems]
3335

3436
ax = Axis(fig[1,1], aspect = DataAspect(),
@@ -50,8 +52,7 @@ function CSMakie.pzmap!(fig, systems::Union{LTISystem, AbstractVector{<:LTISyste
5052
if !isempty(z)
5153
scatter!(ax, real(z), imag(z),
5254
marker=:circle, markersize=15,
53-
color=(:transparent, 0.5), strokewidth=2,
54-
strokecolor=Cycled(i), label="Zeros")
55+
color=Cycled(i), strokewidth=2, label="Zeros")
5556
end
5657

5758
# Plot poles as x's
@@ -65,11 +66,53 @@ function CSMakie.pzmap!(fig, systems::Union{LTISystem, AbstractVector{<:LTISyste
6566
if isdiscrete(system) && i == 1 # Only draw once
6667
θ = range(0, 2π, length=100)
6768
circle_scale = scale_factor
68-
lines!(ax, circle_scale .* cos.(θ), circle_scale .* sin.(θ),
69+
lines!(ax, circle_scale .* cos.(θ), circle_scale .* sin.(θ),
6970
color=:gray, linestyle=:dash, alpha=0.5)
7071
end
7172
end
72-
73+
74+
# Draw damping/frequency grid for continuous-time systems
75+
if grid
76+
has_continuous = any(iscontinuous, systems_vec)
77+
if has_continuous
78+
# Compute extent from all poles for scaling
79+
all_poles = vcat([poles(s) .* scale_factor for s in systems_vec if iscontinuous(s)]...)
80+
if !isempty(all_poles)
81+
max_extent = maximum(abs.(all_poles))
82+
r_max = max_extent * 1.3
83+
84+
# Auto-compute ωn values if not provided
85+
ωn_vals = isempty(ωn) ? collect(range(0, r_max, length=6)[2:end]) : ωn .* scale_factor
86+
87+
# Draw constant ζ lines (rays from origin into left half-plane)
88+
for ζi in ζ
89+
θ_zeta = acos(clamp(ζi, 0, 1))
90+
# Upper ray (positive imaginary)
91+
lines!(ax, [-r_max * cos(θ_zeta), 0], [r_max * sin(θ_zeta), 0],
92+
color=:gray, linestyle=:dash, alpha=0.4, linewidth=0.5)
93+
# Lower ray (negative imaginary)
94+
lines!(ax, [-r_max * cos(θ_zeta), 0], [-r_max * sin(θ_zeta), 0],
95+
color=:gray, linestyle=:dash, alpha=0.4, linewidth=0.5)
96+
# Label at upper ray endpoint
97+
text!(ax, -r_max * cos(θ_zeta) * 0.95, r_max * sin(θ_zeta) * 0.95,
98+
text = "ζ=$(ζi 1/sqrt(2) ? "1/√2" : round(ζi, digits=2))", fontsize=8,
99+
color=:gray, align=(:right, :bottom))
100+
end
101+
102+
# Draw constant ωn semicircles (left half-plane only)
103+
θ_arc = range/2, 3π/2, length=50)
104+
for ωni in ωn_vals
105+
lines!(ax, ωni .* cos.(θ_arc), ωni .* sin.(θ_arc),
106+
color=:gray, linestyle=:dot, alpha=0.4, linewidth=0.5)
107+
# Label at top of semicircle
108+
text!(ax, 0, ωni * 1.02,
109+
text = "ωn=$(round(ωni, digits=1))", fontsize=8,
110+
color=:gray, align=(:left, :bottom))
111+
end
112+
end
113+
end
114+
end
115+
73116
return fig
74117
end
75118

0 commit comments

Comments
 (0)