Skip to content

Commit d4f61e1

Browse files
Add .rgb properties + tests for Color, Sprite, and BasicSprite (#2060)
* FriendlyGecko's initial Color RGB PR * Updated the color setter so that it does not change the current alpha * Updated it to check for use arcade.Color * Added an isintance check to the len3 check and moved those before the len4 check. So now if the user does a tuple of 3 (ie (255,255,255)) or uses the arcade.Color type (i.e. arcade.Color.BLACK) it will just reuse the old alpha. * Demo 1 * Added deeper RGB class * cleaned up RGB * Imports + clean up .rgb property * Revert BasicSprite.color changes * Revert RGB type since 0 tests + conflicts w/ existing type alias * Revert whitespace change * Rephrase BasicSprite.rgb docstring * Revert sys removal * Revert typing import noise * Revert BufferProtocol change * Add Color.rgb property * Add quick tests for Color.rgb property * Add tests for BasicSprite / Sprite .rgb property * Bug/typo fix + test tweaks * Fix typo in color type tests --------- Co-authored-by: FriendlyGecko <[email protected]>
1 parent 42c5419 commit d4f61e1

File tree

4 files changed

+117
-1
lines changed

4 files changed

+117
-1
lines changed

arcade/sprite/base.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from __future__ import annotations
22

3-
from typing import TYPE_CHECKING, Iterable, List, TypeVar, Any
3+
from typing import TYPE_CHECKING, Iterable, List, TypeVar, Any, Tuple
44

55
import arcade
66
from arcade.types import Point, Color, RGBA255, RGBOrA255, PointList
@@ -337,6 +337,44 @@ def visible(self, value: bool):
337337
for sprite_list in self.sprite_lists:
338338
sprite_list._update_color(self)
339339

340+
@property
341+
def rgb(self) -> Tuple[int, int, int]:
342+
"""Get or set only the sprite's RGB color components.
343+
344+
If a 4-color RGBA tuple is passed:
345+
346+
* The new color's alpha value will be ignored
347+
* The old alpha value will be preserved
348+
349+
"""
350+
return self._color[:3]
351+
352+
@rgb.setter
353+
def rgb(self, color: RGBOrA255):
354+
355+
# Fast validation of size by unpacking channel values
356+
try:
357+
r, g, b, *_a = color
358+
if len(_a) > 1: # Alpha's only used to validate here
359+
raise ValueError()
360+
361+
except ValueError as _: # It's always a length issue
362+
raise ValueError((
363+
f"{self.__class__.__name__},rgb takes 3 or 4 channel"
364+
f" colors, but got {len(color)} channels"))
365+
366+
# Unpack to avoid index / . overhead & prep for repack
367+
current_r, current_b, current_g, a = self._color
368+
369+
# Do nothing if equivalent to current color
370+
if current_r == r and current_g == g and current_b == b:
371+
return
372+
373+
# Preserve the current alpha value & update sprite lists
374+
self._color = Color(r, g, b, a)
375+
for sprite_list in self.sprite_lists:
376+
sprite_list._update_color(self)
377+
340378
@property
341379
def color(self) -> Color:
342380
"""

arcade/types.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,26 @@ def b(self) -> int:
159159
def a(self) -> int:
160160
return self[3]
161161

162+
@property
163+
def rgb(self) -> Tuple[int, int, int]:
164+
"""Return only a color's RGB components.
165+
166+
This is syntactic sugar for slice indexing as below:
167+
168+
.. code-block:: python
169+
170+
>>> from arcade.color import WHITE
171+
>>> WHITE[:3]
172+
(255, 255, 255)
173+
# Equivalent but slower than the above
174+
>>> (WHITE.r, WHITE.g, WHITE.b)
175+
(255, 255, 255)
176+
177+
To reorder the channels as you retrieve them, see
178+
:meth:`.swizzle`.
179+
"""
180+
return self[:3]
181+
162182
@classmethod
163183
def from_iterable(cls, iterable: Iterable[int]) -> Self:
164184
"""

tests/unit/color/test_color_type.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,16 @@ def test_color_normalized_property():
190190
assert colors.GRAY.normalized == (128 / 255, 128 / 255, 128 / 255, 1.0)
191191

192192

193+
def test_color_rgb_property():
194+
# Try some bounds
195+
assert colors.WHITE.rgb == (255, 255, 255)
196+
assert colors.BLACK.rgb == (0, 0, 0)
197+
198+
# Spot check unique colors
199+
assert colors.COBALT.rgb == (0, 71, 171)
200+
assert Color(1,3,5,7).rgb == (1, 3, 5)
201+
202+
193203
def test_deepcopy_color_values():
194204
expected_color = Color(255, 255, 255, 255)
195205
assert deepcopy(expected_color) == expected_color

tests/unit/sprite/test_sprite.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,6 +336,54 @@ def test_visible():
336336
assert sprite.alpha == 100
337337

338338

339+
def test_sprite_rgb_property_basics():
340+
sprite = arcade.Sprite(":resources:images/animated_characters/female_person/femalePerson_idle.png")
341+
342+
# Initial multiply tint is white
343+
assert sprite.rgb == (255, 255, 255)
344+
345+
# Values which are too short are not allowed
346+
with pytest.raises(ValueError):
347+
sprite.rgb = (1,2)
348+
with pytest.raises(ValueError):
349+
sprite.rgb = (0,)
350+
351+
# Nor are values which are too long
352+
with pytest.raises(ValueError):
353+
sprite.rgb = (100,100,100,100,100)
354+
355+
# Test color setting + .rgb report when .visible == True
356+
sprite.rgb = (1, 3, 5, 7)
357+
assert sprite.color.r == 1
358+
assert sprite.color.g == 3
359+
assert sprite.color.b == 5
360+
assert sprite.rgb[0] == 1
361+
assert sprite.rgb[1] == 3
362+
assert sprite.rgb[2] == 5
363+
364+
# Test alpha preservation
365+
assert sprite.color.a == 255
366+
assert sprite.alpha == 255
367+
368+
# Test .rgb sets rgb chanels when visible == False as with .color,
369+
# but also still preserves original alpha values.
370+
sprite.visible = False
371+
sprite.color = (9, 11, 13, 15)
372+
sprite.rgb = (17, 21, 23, 25)
373+
374+
# Check the color channels
375+
assert sprite.color.r == 17
376+
assert sprite.color.g == 21
377+
assert sprite.color.b == 23
378+
assert sprite.rgb[0] == 17
379+
assert sprite.rgb[1] == 21
380+
assert sprite.rgb[2] == 23
381+
382+
# Alpha preserved?
383+
assert sprite.color.a == 15
384+
assert sprite.alpha == 15
385+
386+
339387
def test_sprite_scale_xy(window):
340388
sprite = arcade.SpriteSolidColor(20, 20, color=arcade.color.WHITE)
341389

0 commit comments

Comments
 (0)