Skip to content

Commit

Permalink
tests for linearity and vertical lines
Browse files Browse the repository at this point in the history
  • Loading branch information
hbmartin committed Jul 5, 2024
1 parent 54861ed commit e285485
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 9 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ python -m graphviz2drawio test/directed/hello.gv.txt
- [ ] Text on edge alignment #59
- [ ] Text alignment inside of shape
- [ ] Support for node with `path` shape #47
- [ ] Ensure undirected graphs are not drawn with arrows
- [ ] Run ruff in CI
- [ ] Publish api docs to GH pages
- [ ] Restore codecov to test GHA
Expand Down
2 changes: 1 addition & 1 deletion graphviz2drawio/graphviz2drawio.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from pygraphviz import AGraph

from .models import parse_nodes_edges_clusters
from .models.SvgParser import parse_nodes_edges_clusters
from .mx.MxGraph import MxGraph


Expand Down
1 change: 0 additions & 1 deletion graphviz2drawio/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# flake8: noqa: F401
from .Arguments import Arguments
from .Rect import Rect
from .SvgParser import parse_nodes_edges_clusters
49 changes: 42 additions & 7 deletions graphviz2drawio/mx/Curve.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import math
from typing import Callable, Any

from svg.path import CubicBezier

from graphviz2drawio.models.Errors import InvalidCbError
from ..models.Errors import InvalidCbError

linear_min_r2 = 0.9

Expand All @@ -24,15 +25,22 @@ def __str__(self) -> str:
return f"{self.start} , {control}, {self.end}"

@staticmethod
def is_linear(cb: CubicBezier) -> bool:
m, b = Curve._linear_regression(cb.start, cb.end)
control1_prediction = m * cb.control1.real + b
control2_prediction = m * cb.control2.real + b
def is_linear(cb: CubicBezier, is_rotated: bool = False) -> bool:
line = _line(cb.start, cb.end)

if line is None:
if is_rotated: # Prevent infinite recursion
return False
return Curve.is_linear(_rotate_bezier(cb), True)

return math.isclose(
control1_prediction, cb.control1.imag, abs_tol=threshold
line(cb.control1.real),
cb.control1.imag,
rel_tol=0.1,
) and math.isclose(
control2_prediction, cb.control2.imag, abs_tol=threshold
line(cb.control2.real),
cb.control2.imag,
rel_tol=0.1,
)

def cubic_bezier_coordinates(self, t: float) -> complex:
Expand Down Expand Up @@ -88,3 +96,30 @@ def _derivative_of_cubic_bezier(p: list, t: float):
+ (6.0 * t * (1.0 - t) * (p[2] - p[1]))
+ (3.0 * (t**2) * (p[3] - p[2]))
)


def _line(start: complex, end: complex) -> Callable[[float], float] | None:
"""Calculate the slope and y-intercept of a line."""
denom = end.real - start.real
if denom == 0:
return None
# Linearity is used to determine if a cubic Bézier is actually a line
# BUT we need to check for vertical lines or vertically oriented Beziers
# Maybe caller should flip x/y and call again?
m = (end.imag - start.imag) / denom
b = start.imag - (m * start.real)

def y(x: float) -> float:
return (m * x) + b

return y


def _rotate_bezier(cb):
"""Reverse imaginary and real parts for all components."""
return CubicBezier(
complex(cb.start.imag, cb.start.real),
complex(cb.control1.imag, cb.control1.real),
complex(cb.control2.imag, cb.control2.real),
complex(cb.end.imag, cb.end.real),
)
13 changes: 13 additions & 0 deletions test/test_curve.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from graphviz2drawio.mx.Curve import _line


def test_line():
line = _line(complex(-5, 10), complex(-3, 4))
assert line(0) == -5
assert line(1) == -8
assert line(2) == -11


def test_line_vertical():
line = _line(complex(1, 10), complex(1, 4))
assert line is None
10 changes: 10 additions & 0 deletions test/test_graphs.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,16 @@ def test_polylines() -> None:

root = ElementTree.fromstring(xml)
check_xml_top(root)
assert False


def test_polylines_curved() -> None:
file = "test/undirected/polylines_curved.gv.txt"
xml = graphviz2drawio.convert(file)

root = ElementTree.fromstring(xml)
check_xml_top(root)
# assert False


def test_cluster() -> None:
Expand Down

0 comments on commit e285485

Please sign in to comment.