Skip to content

Save port positions in the elk graph - #20

Open
LudoBroche wants to merge 8 commits into
mainfrom
19-feature-save-port-positions-in-the-elk-graph
Open

Save port positions in the elk graph#20
LudoBroche wants to merge 8 commits into
mainfrom
19-feature-save-port-positions-in-the-elk-graph

Conversation

@LudoBroche

@LudoBroche LudoBroche commented Jul 24, 2026

Copy link
Copy Markdown
Member

Tip

The Feature

  • As a: Developer integrating ewoksdraw with pyelk for automatic graph layout
  • I want to: Include computed task input/output positions in the ELK graph
  • So that: SVG links can use the actual I/O anchors instead of generic box edges

Note

Scope & Boundaries

  • In Scope: Extracting TaskIOPosition values and converting them into fixed WEST input and EAST output ports with x/y coordinates
  • Out of Scope: map_all_data, mappings without source_output, layout algorithm changes, and manual port repositioning

Important

Acceptance Criteria

  • Every task input/output becomes an ELK port with its computed x/y position
  • Supported data-mapping edges connect the corresponding output and input ports
  • Unsupported mappings emit a warning and are not drawn
  • SVG and ELK conversion tests pass

@LudoBroche LudoBroche added this to the ewoksdraw 1.0 release milestone Jul 24, 2026
@LudoBroche LudoBroche self-assigned this Jul 24, 2026
@codecov

codecov Bot commented Jul 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 98.64865% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/ewoksdraw/svg/svg_task_io.py 83.33% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@LudoBroche
LudoBroche force-pushed the 19-feature-save-port-positions-in-the-elk-graph branch from 7f0ce2c to 0e8326c Compare July 29, 2026 12:56
Base automatically changed from 17-feature-flat-converter-from-ewoks-execution-graph-to-elk-layout-graph to main August 5, 2026 12:55
@LudoBroche
LudoBroche force-pushed the 19-feature-save-port-positions-in-the-elk-graph branch from 0e8326c to 9752500 Compare August 5, 2026 14:33
@LudoBroche LudoBroche linked an issue Aug 6, 2026 that may be closed by this pull request
Comment on lines +18 to 27
task_io_positions: TaskIOPositions = svg_task_group.extract_io_positions()
elk_graph: ElkGraph = convert_ewoks_to_elk_graph(
ewoks_graph, task_sizes, task_io_positions
)

pprint(dict(ewoks_graph.graph.nodes(data=True)))
pprint(list(ewoks_graph.graph.edges(data=True)))
pprint(task_sizes)
pprint(task_io_positions)
pprint(elk_graph)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New method to extract io_positions returning a TaskIOPositions.
Example:

{
'task1': [
           TaskIOPosition(name='a', io_type='input', x=0.0, y=20.0),
           TaskIOPosition(name='b', io_type='input', x=0.0, y=28.0),
           TaskIOPosition(name='delay', io_type='input', x=0.0, y=36.0),
           TaskIOPosition(name='result', io_type='output', x=39.56, y=47.0)],
 'task2': [
           TaskIOPosition(name='a', io_type='input', x=0.0, y=20.0),
           TaskIOPosition(name='b', io_type='input', x=0.0, y=28.0),
           TaskIOPosition(name='delay', io_type='input', x=0.0, y=36.0),
           TaskIOPosition(name='result', io_type='output', x=39.56, y=47.0)]
}

The elk_graph representation now includes the TaskIOPositions (called "Ports" in ELK semantic).
Example:

{'children': [{'height': 55.0,
               'id': 'task1',
               'layoutOptions': {'org.eclipse.elk.portConstraints': 'FIXED_POS'},
               'ports': [{'height': 0,
                          'id': 'task1.input.a',
                          'layoutOptions': {'org.eclipse.elk.port.borderOffset': 0,
                                            'org.eclipse.elk.port.index': 0,
                                            'org.eclipse.elk.port.side': 'WEST'},
                          'width': 0,
                          'x': 0.0,
                          'y': 20.0},

                          ....


                         {'height': 0,
                          'id': 'task1.output.result',
                          'layoutOptions': {'org.eclipse.elk.port.borderOffset': 0,
                                            'org.eclipse.elk.port.index': 0,
                                            'org.eclipse.elk.port.side': 'EAST'},
                          'width': 0,
                          'x': 39.56,
                          'y': 47.0}],
  ....
 'edges': [{'id': 'edge_0_task1_task3',
            'sources': ['task1.output.result'],
            'targets': ['task3.input.a']},
           {'id': 'edge_1_task2_task4',
            'sources': ['task2.output.result'],
            'targets': ['task4.input.a']},
           {'id': 'edge_2_task3_task5',
            'sources': ['task3.output.result'],
            'targets': ['task5.input.a']},
           {'id': 'edge_3_task4_task5',
            'sources': ['task4.output.result'],
            'targets': ['task5.input.b']},
           {'id': 'edge_4_task5_task6',
            'sources': ['task5.output.result'],
            'targets': ['task6.input.a']}],

Comment thread src/ewoksdraw/layout/elk_converter.py
Comment thread src/ewoksdraw/layout/elk_converter.py
for mapping in link_attrs.get("data_mapping", []):
source_output = mapping.get("source_output")
if not source_output:
warnings.warn(

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm also ignoring links that have source_input but no source_output in the mapping.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is source_input? Did you mean target_input?

How is it possible to have a link that does not have a target and a source?

@LudoBroche LudoBroche Aug 10, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I meant the mapping has a target_input but no source_ouput
From the doc : ewokscore :

data_mapping (optional): Describe data transfer from source outputs to target input arguments. For example:

{
    "data_mapping": [{"source_output": "result",
                      "target_input": "a"}]
}

If "source_output" is None or missing, the complete output of the source will be passed to the corresponding "target_input" or the target.

Comment thread src/ewoksdraw/layout/elk_converter.py Outdated
Comment thread src/ewoksdraw/layout/elk_converter.py
Comment thread src/ewoksdraw/svg/svg_group.py
Comment thread src/ewoksdraw/svg/svg_task.py Outdated
Comment thread src/ewoksdraw/svg/svg_task_io.py
Comment thread src/ewoksdraw/tests/test_svg_task.py
Comment thread CHANGELOG.md Outdated
@LudoBroche
LudoBroche marked this pull request as ready for review August 6, 2026 11:57
@LudoBroche
LudoBroche requested a review from a team August 6, 2026 11:59

@loichuder loichuder left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like some code would read better if we deal with inputs and outputs separately (rather than keeping a io_type flag). I identified already one place where it would simplify the code.

What do think?

Comment thread src/ewoksdraw/layout/elk_converter.py Outdated
for mapping in link_attrs.get("data_mapping", []):
source_output = mapping.get("source_output")
if not source_output:
warnings.warn(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is source_input? Did you mean target_input?

How is it possible to have a link that does not have a target and a source?

Comment thread src/ewoksdraw/layout/elk_converter.py
Comment thread src/ewoksdraw/layout/elk_converter.py Outdated
Comment thread src/ewoksdraw/layout/elk_converter.py
Comment thread src/ewoksdraw/layout/elk_converter.py Outdated
Comment thread CHANGELOG.md Outdated
Comment on lines 134 to +151
def _convert_io_positions_to_elk_ports(
task_id: str, io_positions: list[TaskIOPosition]
task_id: str, io_positions: IOPositions
) -> list[ElkPort]:
ports = _convert_positions_to_elk_ports(
io_positions.inputs,
id_prefix=f"{task_id}.input",
elk_port_side="WEST",
)
ports.extend(
_convert_positions_to_elk_ports(
io_positions.outputs,
id_prefix=f"{task_id}.output",
elk_port_side="EAST",
)
)
return ports


@LudoBroche LudoBroche Aug 10, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A new split between input/output allows us to separate the two calls and avoid the constants

y: float


class IOPositions(NamedTuple):

@LudoBroche LudoBroche Aug 10, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of having a flag for input/output, we just contain both directly in a single structure

Comment on lines +147 to +161
def get_io_positions(self) -> IOPositions:
"""Return the task-relative positions of the task inputs and outputs."""
return IOPositions(
inputs=self._get_group_io_positions(self._inputs),
outputs=self._get_group_io_positions(self._outputs),
)

@staticmethod
def _get_group_io_positions(group: SvgTaskIOGroup) -> list[TaskIOPosition]:
positions: list[TaskIOPosition] = []
for io in group.elements:
x = group.translation.x + io.translation.x
y = group.translation.y + io.translation.y
positions.append(TaskIOPosition(name=io.name, x=x, y=y))
return positions

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New version of the get_io_position with the I/O splits.
Two simple method calls.

Co-authored-by: Loïc Huder <42204205+loichuder@users.noreply.github.com>
@LudoBroche
LudoBroche requested a review from loichuder August 10, 2026 11:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Save port positions in the elk graph

2 participants