Skip to content

Widget cartesian update line bugfix #62

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
Dec 23, 2021
Merged
189 changes: 151 additions & 38 deletions adafruit_displayio_layout/widgets/cartesian.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ class Cartesian(Widget):
:param int nudge_y: movement in pixels in the y direction to move the origin.
Defaults to 0

:param bool verbose: print debugging information in some internal functions. Default to False

**Quickstart: Importing and using Cartesian**

Expand Down Expand Up @@ -181,11 +182,14 @@ def __init__(
subticks: bool = False,
nudge_x: int = 0,
nudge_y: int = 0,
verbose: bool = False,
**kwargs,
) -> None:

super().__init__(**kwargs)

self._verbose = verbose

self._background_color = background_color

self._axes_line_color = axes_color
Expand Down Expand Up @@ -310,8 +314,6 @@ def __init__(
self.append(self._screen_tilegrid)
self.append(self._corner_tilegrid)

self._update_line = True

self._pointer = None
self._circle_palette = None
self.plot_line_point = None
Expand Down Expand Up @@ -389,10 +391,12 @@ def _draw_ticks(self) -> None:

if self._subticks:
if i in subticks:
# calc subtick_line_height; force min lineheigt to 1.
subtick_line_height = max(1, self._tick_line_height // 2)
rectangle_helper(
text_dist,
self._axes_line_thickness,
self._tick_line_height // 2,
subtick_line_height,
1,
self._axesx_bitmap,
1,
Expand Down Expand Up @@ -457,6 +461,130 @@ def _draw_pointers(self, x: int, y: int) -> None:

self.append(self._pointer)

def _calc_local_xy(self, x: int, y: int) -> (int, int):
local_x = (
int((x - self._xrange[0]) * self._factorx * self._valuex) + self._nudge_x
)
# details on `+ (self.height - 1)` :
# the bitmap is set to self.width & self.height
# but we are only allowed to draw to pixels 0..height-1 and 0..width-1
local_y = (
int((self._yrange[0] - y) * self._factory * self._valuey)
+ (self.height - 1)
+ self._nudge_y
)
return (local_x, local_y)

def _check_local_x_in_range(self, local_x):
return 0 <= local_x < self.width

def _check_local_y_in_range(self, local_y):
return 0 <= local_y < self.height

def _check_local_xy_in_range(self, local_x, local_y):
return self._check_local_x_in_range(local_x) and self._check_local_y_in_range(
local_y
)

def _check_x_in_range(self, x):
return self._xrange[0] <= x <= self._xrange[1]

def _check_y_in_range(self, y):
return self._yrange[0] <= y <= self._yrange[1]

def _check_xy_in_range(self, x, y):
return self._check_x_in_range(x) and self._check_y_in_range(y)

def _add_point(self, x: int, y: int) -> None:
"""_add_point function
helper function to add a point to the graph in the plane
:param int x: ``x`` coordinate in the local plane
:param int y: ``y`` coordinate in the local plane
:return: None
rtype: None
"""
local_x, local_y = self._calc_local_xy(x, y)
if self._verbose:
print("")
print(
"xy: ({: >4}, {: >4}) "
"_xrange: ({: >4}, {: >4}) "
"_yrange: ({: >4}, {: >4}) "
"".format(
x,
y,
self._xrange[0],
self._xrange[1],
self._yrange[0],
self._yrange[1],
)
)
print(
"local_*: ({: >4}, {: >4}) "
" width: ({: >4}, {: >4}) "
" height: ({: >4}, {: >4}) "
"".format(
local_x,
local_y,
0,
self.width,
0,
self.height,
)
)
if self._check_xy_in_range(x, y):
if self._check_local_xy_in_range(local_x, local_y):
if self.plot_line_point is None:
self.plot_line_point = []
self.plot_line_point.append((local_x, local_y))
else:
# for better error messages we check in detail what failed...
# this should never happen:
# we already checked the range of the input values.
# but in case our calculation is wrong we handle this case to..
if not self._check_local_x_in_range(local_x):
raise ValueError(
"local_x out of range: "
"local_x:{: >4}; _xrange({: >4}, {: >4})"
"".format(
local_x,
0,
self.width,
)
)
if not self._check_local_y_in_range(local_y):
raise ValueError(
"local_y out of range: "
"local_y:{: >4}; _yrange({: >4}, {: >4})"
"".format(
local_y,
0,
self.height,
)
)
else:
# for better error messages we check in detail what failed...
if not self._check_x_in_range(x):
raise ValueError(
"x out of range: "
"x:{: >4}; xrange({: >4}, {: >4})"
"".format(
x,
self._xrange[0],
self._xrange[1],
)
)
if not self._check_y_in_range(y):
raise ValueError(
"y out of range: "
"y:{: >4}; yrange({: >4}, {: >4})"
"".format(
y,
self._yrange[0],
self._yrange[1],
)
)

def update_pointer(self, x: int, y: int) -> None:
"""updater_pointer function
helper function to update pointer in the plane
Expand All @@ -465,46 +593,31 @@ def update_pointer(self, x: int, y: int) -> None:
:return: None
rtype: None
"""
local_x = int((x - self._xrange[0]) * self._factorx) + self._nudge_x
local_y = (
int((self._yrange[0] - y) * self._factory) + self.height + self._nudge_y
)

if local_x >= 0 or local_y <= 100:
if self._update_line:
self._draw_pointers(local_x, local_y)
self._update_line = False
else:
self._pointer.x = local_x
self._pointer.y = local_y

def _set_plotter_line(self) -> None:
self.plot_line_point = []
self._add_point(x, y)
if not self._pointer:
self._draw_pointers(
self.plot_line_point[-1][0],
self.plot_line_point[-1][1],
)
else:
self._pointer.x = self.plot_line_point[-1][0]
self._pointer.y = self.plot_line_point[-1][1]

def update_line(self, x: int, y: int) -> None:
"""updater_line function
helper function to update pointer in the plane
helper function to update line in the plane
:param int x: ``x`` coordinate in the local plane
:param int y: ``y`` coordinate in the local plane
:return: None
rtype: None
"""
local_x = int((x - self._xrange[0]) * self._factorx) + self._nudge_x
local_y = (
int((self._yrange[0] - y) * self._factory) + self.height + self._nudge_y
)
if x < self._xrange[1] and y < self._yrange[1]:
if local_x > 0 or local_y < 100:
if self._update_line:
self._set_plotter_line()
self.plot_line_point.append((local_x, local_y))
self._update_line = False
else:
bitmaptools.draw_line(
self._screen_bitmap,
self.plot_line_point[-1][0],
self.plot_line_point[-1][1],
local_x,
local_y,
1,
)
self._add_point(x, y)
if len(self.plot_line_point) > 1:
bitmaptools.draw_line(
self._screen_bitmap,
self.plot_line_point[-2][0],
self.plot_line_point[-2][1],
self.plot_line_point[-1][0],
self.plot_line_point[-1][1],
1,
)
69 changes: 69 additions & 0 deletions examples/displayio_layout_cartesian_dev.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# SPDX-FileCopyrightText: 2021 Stefan Krüger
#
# SPDX-License-Identifier: MIT
#############################
"""
This is a basic demonstration of a Cartesian widget for line-ploting
"""

import time
import board
import displayio
from adafruit_displayio_layout.widgets.cartesian import Cartesian

# create the display on the PyPortal or Clue or PyBadge(for example)
display = board.DISPLAY
# otherwise change this to setup the display
# for display chip driver and pinout you have (e.g. ILI9341)

# pybadge display: 160x128
# Create a Cartesian widget
# https://circuitpython.readthedocs.io/projects/displayio-layout/en/latest/api.html#module-adafruit_displayio_layout.widgets.cartesian
my_plane = Cartesian(
x=11, # x position for the plane
y=0, # y plane position
width=140, # display width
height=110, # display height
xrange=(0, 10), # x range
yrange=(0, 10), # y range
major_tick_stroke=1, # ticks width in pixels
major_tick_length=2, # ticks length in pixels
axes_stroke=1, # axes lines width in pixels
axes_color=0x10A0A0, # axes line color
subticks=True,
)

my_group = displayio.Group()
my_group.append(my_plane)
display.show(my_group) # add high level Group to the display

data = [
# (0, 0),
(1, 1),
# (1, 15), # create out of range error
(2, 1),
(2, 2),
(3, 3),
(4, 3),
(4, 4),
(5, 5),
(6, 5),
(6, 6),
(7, 7),
(8, 7),
(8, 8),
(9, 9),
(10, 9),
(10, 10),
]

print("examples/displayio_layout_cartesian_lineplot.py")

my_plane.update_line(0, 0)
for x, y in data:
my_plane.update_line(x, y)
my_plane.update_pointer(x, y)
time.sleep(0.5)

while True:
pass
52 changes: 52 additions & 0 deletions examples/displayio_layout_cartesian_lineplot.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# SPDX-FileCopyrightText: 2021 Stefan Krüger
#
# SPDX-License-Identifier: MIT
#############################
"""
This is a basic demonstration of a Cartesian widget for line-ploting
"""

import time
import board
import displayio
from adafruit_displayio_layout.widgets.cartesian import Cartesian

# create the display on the PyPortal or Clue or PyBadge(for example)
display = board.DISPLAY
# otherwise change this to setup the display
# for display chip driver and pinout you have (e.g. ILI9341)

# pybadge display: 160x128
# Create a Cartesian widget
# https://circuitpython.readthedocs.io/projects/displayio-layout/en/latest/api.html#module-adafruit_displayio_layout.widgets.cartesian
my_plane = Cartesian(
x=20, # x position for the plane
y=0, # y plane position
width=130, # display width
height=105, # display height
xrange=(0, 100), # x range
yrange=(0, 100), # y range
)

my_group = displayio.Group()
my_group.append(my_plane)
display.show(my_group) # add high level Group to the display

data = [
(0, 0),
(10, 10),
(30, 10),
(50, 50),
(70, 30),
(90, 80),
(95, 80),
(100, 100),
]

print("examples/displayio_layout_cartesian_lineplot.py")
for x, y in data:
my_plane.update_line(x, y)
time.sleep(1)

while True:
pass