# How Edge Curves and Bézier Paths Are Generated for Draw.io Diagrams

> Learn how graphviz2drawio generates edge curves and bezier paths for Draw.io diagrams by parsing SVG segments and encoding them as mxGraph styles. Convert your Graphviz diagrams seamlessly.

- Repository: [Harold Martin/graphviz2drawio](https://github.com/hbmartin/graphviz2drawio)
- Tags: internals
- Published: 2026-03-03

---

**The graphviz2drawio library converts Graphviz SVG paths into Draw.io-compatible Bézier curves by parsing cubic and quadratic segments, subdividing non-linear curves at inflection points, and encoding the results as mxGraph style strings.**

The `hbmartin/graphviz2drawio` repository bridges Graphviz DOT diagrams and Draw.io's editable format. When generating **edge curves and bezier paths for Draw.io diagrams**, the library transforms Graphviz's SVG vector output into the quadratic Bézier approximation that Draw.io's mxGraph engine expects.

## Extracting SVG Path Data from Graphviz Output

The conversion pipeline begins in [`mx/EdgeFactory.py`](https://github.com/hbmartin/graphviz2drawio/blob/main/mx/EdgeFactory.py) where the `from_svg` method processes the SVG group element representing a Graphviz edge. The factory locates the `<path>` element and extracts its `d` attribute, which contains the raw SVG path description.

```python
if (path := SVG.get_first(g, "path")) is not None:
    if "d" in path.attrib:
        curve = self.curve_factory.from_svg(path.attrib["d"])

```

If the path data exists, the factory delegates parsing to **`CurveFactory.from_svg`** in [`mx/CurveFactory.py`](https://github.com/hbmartin/graphviz2drawio/blob/main/mx/CurveFactory.py). This method uses the **svg.path** library to tokenize the SVG path string into a sequence of segment objects that represent the edge geometry.

## Parsing Bézier Segments with CurveFactory

Once parsed, `CurveFactory.from_svg` iterates through each segment to build a `Curve` object. The factory distinguishes between **quadratic** and **cubic Bézier curves**, applying specialized transformation logic to ensure Draw.io compatibility.

### Handling Quadratic Bézier Curves

For `QuadraticBezier` segments encountered in the SVG path, the factory directly extracts the control point and converts it to diagram coordinates using the coordinate translation system.

```python
if isinstance(segment, QuadraticBezier):
    points.append(self.coords.complex_translate(segment.control))

```

These control points are stored in the `Curve` object's `points` list for direct use in the final Draw.io output.

### Converting Cubic Bézier Curves

Cubic Bézier segments require additional processing to match Draw.io's quadratic curve expectations. The factory first checks if the segment is effectively linear using **`Curve.is_linear`** from [`mx/Curve.py`](https://github.com/hbmartin/graphviz2drawio/blob/main/mx/Curve.py). This function compares both control points against the line defined by the segment's start and end points using the tolerance defined in `LINE_TOLERANCE`. If linear, the start point is appended as a simple coordinate.

For non-linear cubic Béziers, the factory invokes **inflection subdivision** to break complex curves into manageable quadratic approximations:

```python
elif isinstance(segment, CubicBezier):
    if Curve.is_linear(segment):
        points.append(self.coords.complex_translate(segment.start))
    else:
        split_cubes = subdivide_inflections(
            segment.start, segment.control1, segment.control2, segment.end,
        )
        split_controls = [
            self.coords.complex_translate(
                approximate_cubic_bezier_as_quadratic(*cube)[1],
            )
            for cube in split_cubes
            if cube
        ]
        points.extend(split_controls)

```

The **`subdivide_inflections`** function in [`mx/bezier.py`](https://github.com/hbmartin/graphviz2drawio/blob/main/mx/bezier.py) calculates inflection points by finding the *t* parameters where the cubic's derivative cross-product indicates a change in concavity. It splits the curve at those points, then **`approximate_cubic_bezier_as_quadratic`** converts each resulting sub-curve into a quadratic Bézier.

The resulting `Curve` object stores the start point, end point, a boolean `is_bezier` flag (set to **True** when Bézier handling was required), and the list of quadratic control points.

## Generating Draw.io Style Strings

The final transformation occurs in [`mx/Edge.py`](https://github.com/hbmartin/graphviz2drawio/blob/main/mx/Edge.py) within the **`get_edge_style`** method. This method constructs the mxGraph style string that Draw.io interprets to render the edge properly.

```python
if self.curve is not None:
    style = Styles.EDGE.format(... ) + (MxConst.CURVED if self.curve.is_bezier else MxConst.SHARP)

```

When `is_bezier` is true, the method appends `MxConst.CURVED` (encoding `curved=1`) to the style string, producing smooth edges. If false, it uses `MxConst.SHARP` for straight-line segments.

If source and target node geometries are available, the method calculates precise connection points along the node perimeters:

```python
exit_x, exit_y = source_geo.relative_location_along_perimeter(self.curve.start)
entry_x, entry_y = target_geo.relative_location_along_perimeter(self.curve.end)
style += f"exitX={exit_x:.4f};exitY={exit_y:.4f};"
style += f"entryX={entry_x:.4f};entryY={entry_y:.4f};"

```

The complete style string (e.g., `"edgeStyle=elbowEdgeStyle;curved=1;strokeColor=#000000;exitX=0.5;exitY=1.0;..."`) is embedded in the generated `.drawio` XML, instructing Draw.io to render the **edge curves and bezier paths** with proper geometric alignment.

## Summary

- **[`mx/EdgeFactory.py`](https://github.com/hbmartin/graphviz2drawio/blob/main/mx/EdgeFactory.py)** extracts the SVG `d` attribute from Graphviz edge output and initiates curve parsing via `CurveFactory`.
- **[`mx/CurveFactory.py`](https://github.com/hbmartin/graphviz2drawio/blob/main/mx/CurveFactory.py)** converts SVG path segments into `Curve` objects, handling quadratic curves directly while subdividing cubic Béziers at inflection points via `subdivide_inflections`.
- **[`mx/bezier.py`](https://github.com/hbmartin/graphviz2drawio/blob/main/mx/bezier.py)** provides the mathematical utilities for inflection detection (`subdivide_inflections`) and cubic-to-quadratic approximation (`approximate_cubic_bezier_as_quadratic`).
- **[`mx/Curve.py`](https://github.com/hbmartin/graphviz2drawio/blob/main/mx/Curve.py)** defines the `Curve` data class and implements linearity detection through `Curve.is_linear`.
- **[`mx/Edge.py`](https://github.com/hbmartin/graphviz2drawio/blob/main/mx/Edge.py)** generates the final mxGraph style string via `get_edge_style`, setting `curved=1` for Bézier edges and calculating precise `exitX`/`exitY` and `entryX`/`entryY` coordinates relative to node geometries.

## Frequently Asked Questions

### How does graphviz2drawio handle complex cubic Bézier curves from Graphviz?

The library subdivides complex cubic Bézier curves at their inflection points using the `subdivide_inflections` function in [`mx/bezier.py`](https://github.com/hbmartin/graphviz2drawio/blob/main/mx/bezier.py). It finds the *t* parameters where the curve changes concavity by analyzing the derivative cross-product, splits the curve at those points, then approximates each resulting sub-curve as a quadratic Bézier using `approximate_cubic_bezier_as_quadratic`. This produces smooth curves that Draw.io can render while maintaining mathematical fidelity to the original Graphviz output.

### What is the difference between curved and sharp edges in the generated Draw.io output?

According to [`mx/Edge.py`](https://github.com/hbmartin/graphviz2drawio/blob/main/mx/Edge.py), the `get_edge_style` method checks the `Curve.is_bezier` boolean flag. If true, it appends `MxConst.CURVED` (translating to `curved=1` in the style string) to produce smooth Bézier edges. If false, it uses `MxConst.SHARP` to create straight-line segments. This determination happens during the SVG parsing phase when `CurveFactory` detects whether the path contains actual curve segments or linear connections.

### Why does the library convert cubic Bézier curves to quadratic approximations?

Draw.io's mxGraph engine primarily supports quadratic Bézier curves for its curved edge rendering, while Graphviz outputs cubic Bézier curves in its SVG paths. The [`mx/CurveFactory.py`](https://github.com/hbmartin/graphviz2drawio/blob/main/mx/CurveFactory.py) implementation converts these cubic segments to quadratic approximations through the `approximate_cubic_bezier_as_quadratic` function, ensuring compatibility with Draw.io's rendering capabilities while preserving the visual trajectory of the original diagram.

### Where does the library calculate connection points for edges on node boundaries?

The `get_edge_style` method in [`mx/Edge.py`](https://github.com/hbmartin/graphviz2drawio/blob/main/mx/Edge.py) calculates exit and entry coordinates using `source_geo.relative_location_along_perimeter(self.curve.start)` and `target_geo.relative_location_along_perimeter(self.curve.end)`. These calls determine the proportional positions along the source and target node perimeters where the edge should attach, then encode them as `exitX`, `exitY`, `entryX`, and `entryY` parameters in the mxGraph style string.