Skip to content

Commit 4e39011

Browse files
authored
Add error hints for unsuported operations (#164)
This also adds the note for `abs`/`abs2` into README.md.
1 parent 81c9ada commit 4e39011

File tree

2 files changed

+47
-5
lines changed

2 files changed

+47
-5
lines changed

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,20 @@ RGBRGB{Float64}:
110110
0.0 0.0 0.0
111111
-0.03 0.0 0.02
112112
```
113+
114+
### `abs` and `abs2`
115+
116+
To begin with, there is no general and straightforward definition of the
117+
absolute value of a vector.
118+
There are roughly two possible definitions of `abs`/`abs2`: as a channel-wise
119+
operator or as a function which returns a real number based on the norm.
120+
For the latter, there are also variations in the definition of norm.
121+
122+
In ColorVectorSpace v0.9 and later, `abs` is defined as a channel-wise operator
123+
and `abs2` is undefined.
124+
The following are alternatives for the definitions in ColorVectorSpace v0.8 and
125+
earlier.
126+
```julia
127+
_abs(c) = mapreducec(v->abs(float(v)), +, 0, c)
128+
_abs2(c) = mapreducec(v->float(v)^2, +, 0, c)
129+
```

src/ColorVectorSpace.jl

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -188,11 +188,15 @@ copy(c::MathTypes) = c
188188
abs(c::MathTypes) = mapc(abs, c)
189189
norm(c::MathTypes, p::Real=2) = (cc = channels(c); norm(cc, p)/(p == 0 ? length(cc) : length(cc)^(1/p)))
190190
()(a::C, b::C) where {C<:MathTypes} = mapc(*, a, b)
191+
()(a::C, b::C) where {C<:MathTypes} = throw(MethodError(dot, (a, b)))
192+
()(a::C, b::C) where {C<:MathTypes} = throw(MethodError(tensor, (a, b)))
191193

192194
## Mixed types
193195
(+)(a::MathTypes, b::MathTypes) = (+)(promote(a, b)...)
194196
(-)(a::MathTypes, b::MathTypes) = (-)(promote(a, b)...)
195197
()(a::MathTypes, b::MathTypes) = ()(promote(a, b)...)
198+
()(a::MathTypes, b::MathTypes) = ()(promote(a, b)...) # not fully supported, but used for error hints
199+
()(a::MathTypes, b::MathTypes) = ()(promote(a, b)...) # not fully supported, but used for error hints
196200

197201

198202
# Scalar RGB
@@ -220,8 +224,10 @@ end
220224
(-)(a::TransparentRGB, b::TransparentRGB) = rettype(-, a, b)(red(a)-red(b), green(a)-green(b), blue(a)-blue(b), alpha(a)-alpha(b))
221225

222226
# New multiplication operators
223-
()(x::AbstractRGB, y::AbstractRGB) = (T = acctype(eltype(x), eltype(y)); T(red(x))*T(red(y)) + T(green(x))*T(green(y)) + T(blue(x))*T(blue(y)))/3
224-
()(x::Union{AbstractRGB,AbstractGray}, y::Union{AbstractRGB,AbstractGray}) = (promote(x, y)...)
227+
function ()(x::AbstractRGB, y::AbstractRGB)
228+
T = acctype(eltype(x), eltype(y))
229+
(T(red(x))*T(red(y)) + T(green(x))*T(green(y)) + T(blue(x))*T(blue(y)))/3
230+
end
225231
# ⊗ defined below
226232

227233

@@ -277,8 +283,8 @@ middle(x::C, y::C) where {C<:AbstractGray} = arith_colorant_type(C)(middle(gray(
277283
(-)(a::AbstractGray, b::Number) = base_color_type(a)(gray(a)-b)
278284
(-)(a::Number, b::AbstractGray) = base_color_type(b)(a-gray(b))
279285

280-
()(x::AbstractGray, y::AbstractGray) = gray(x)*gray(y)
281-
()(x::AbstractGray, y::AbstractGray) = x y
286+
()(x::C, y::C) where {C<:AbstractGray} = gray(x)*gray(y)
287+
()(x::C, y::C) where {C<:AbstractGray} = x y
282288

283289
max(a::T, b::T) where {T<:AbstractGray} = T(max(gray(a),gray(b)))
284290
max(a::AbstractGray, b::AbstractGray) = max(promote(a,b)...)
@@ -384,7 +390,6 @@ function ⊗(a::AbstractRGB, b::AbstractRGB)
384390
agbr, abbg, arbb, abbr, arbg, agbb = ag*br, ab*bg, ar*bb, ab*br, ar*bg, ag*bb
385391
return RGBRGB(ar*br, agbr, abbr, arbg, ag*bg, abbg, arbb, agbb, ab*bb)
386392
end
387-
(a::Union{AbstractRGB,AbstractGray}, b::Union{AbstractRGB,AbstractGray}) = (promote(a, b)...)
388393

389394
"""
390395
varmult(op, itr; corrected::Bool=true, mean=Statistics.mean(itr), dims=:)
@@ -432,6 +437,26 @@ function __init__()
432437
# Color is not necessary, this is just to show it's possible.
433438
A, B = argtypes
434439
A !== B && print(io, "\nIn binary operation with $A and $B, the return type is ambiguous")
440+
elseif exc.f === dot && length(argtypes) == 2
441+
if any(C -> C <: Union{TransparentGray, TransparentRGB}, argtypes)
442+
print(io, "\n`dot` (or `⋅`) for transparent colors is not supported ")
443+
print(io, "because the normalization is designed for opaque grays and RGBs.")
444+
print(io, "\nYou can use `reducec(+, 0, mapc(float, a) ⊙ mapc(float, b))` ")
445+
print(io, "to get the dot product without normalization.")
446+
end
447+
elseif exc.f === tensor && length(argtypes) == 2
448+
if any(C -> C <: Union{TransparentGray, TransparentRGB}, argtypes)
449+
print(io, "\n`tensor` (or `⊗`) for transparent colors is not supported ")
450+
print(io, "due to the non-high demand.")
451+
end
452+
elseif exc.f === abs2 && length(argtypes) == 1
453+
C = argtypes[1]
454+
if C <: MathTypes
455+
print(io, "\nIt is ambiguous whether `abs2(::$C)` should return a real number ")
456+
print(io, "or a color object.")
457+
print(io, "\nYou can use `_abs2(c) = mapreducec(v->float(v)^2, +, 0, c)` ")
458+
print(io, "to get the value compatible with ColorVectorSpace v0.8 or earlier.")
459+
end
435460
end
436461
end
437462
end

0 commit comments

Comments
 (0)