Bond graph ports and drawing added
This commit is contained in:
@@ -1,8 +1,12 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import Literal
|
||||
|
||||
from PySide6.QtCore import Qt
|
||||
|
||||
|
||||
ArrowStyle = Literal["open", "half", "filled"]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ConnectionStyle:
|
||||
color: str = "#285f9e"
|
||||
@@ -13,11 +17,17 @@ class ConnectionStyle:
|
||||
arrow_at_source: bool = False
|
||||
arrow_at_target: bool = True
|
||||
arrow_size: float = 10.0
|
||||
arrow_style: ArrowStyle = "filled"
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if self.arrow_style not in {"open", "half", "filled"}:
|
||||
raise ValueError(f"Unknown connection arrow style: {self.arrow_style}")
|
||||
|
||||
|
||||
# This is the intentional code-level styling point for every port/connection type.
|
||||
CONNECTION_STYLES: dict[str, ConnectionStyle] = {
|
||||
"signal": ConnectionStyle(),
|
||||
"power": ConnectionStyle(width=3.0, arrow_style="half", color="#000000"),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from copy import deepcopy
|
||||
|
||||
from PySide6.QtCore import QPointF, QRectF, QSizeF, Qt
|
||||
from PySide6.QtCore import QPointF, QRectF, QSizeF, Qt, QTimer
|
||||
from PySide6.QtGui import QColor, QPainter, QPainterPath, QPen, QPolygonF
|
||||
from PySide6.QtWidgets import (
|
||||
QColorDialog,
|
||||
@@ -487,7 +487,13 @@ class IconEditorDialog(QDialog):
|
||||
self.scene.addItem(ShapeItem(element))
|
||||
self._add_ports(self.inputs, "input", 0.0)
|
||||
self._add_ports(self.outputs, "output", self.icon.width)
|
||||
self.view.center_icon()
|
||||
self._initial_fit_pending = True
|
||||
|
||||
def showEvent(self, event) -> None: # noqa: N802 (Qt API name)
|
||||
super().showEvent(event)
|
||||
if self._initial_fit_pending:
|
||||
self._initial_fit_pending = False
|
||||
QTimer.singleShot(0, self.view.center_icon)
|
||||
|
||||
def _add_ports(self, ports: list[Port], direction: str, default_x: float) -> None:
|
||||
spacing = self.icon.height / (len(ports) + 1)
|
||||
|
||||
@@ -485,23 +485,39 @@ class ConnectionGraphicsItem(QGraphicsPathItem):
|
||||
handle.setVisible(self.isSelected())
|
||||
|
||||
@staticmethod
|
||||
def _arrow(end: QPointF, direction: QPointF, size: float) -> QPolygonF:
|
||||
def _arrow_points(
|
||||
end: QPointF, direction: QPointF, size: float
|
||||
) -> tuple[QPointF, QPointF, QPointF]:
|
||||
length = max(0.001, (direction.x() ** 2 + direction.y() ** 2) ** 0.5)
|
||||
unit = QPointF(direction.x() / length, direction.y() / length)
|
||||
normal = QPointF(-unit.y(), unit.x())
|
||||
base = end - unit * size
|
||||
return QPolygonF([end, base + normal * size * 0.45, base - normal * size * 0.45])
|
||||
return end, base + normal * size * 0.45, base - normal * size * 0.45
|
||||
|
||||
def _draw_arrow(
|
||||
self, painter: QPainter, end: QPointF, direction: QPointF
|
||||
) -> None:
|
||||
tip, left, right = self._arrow_points(
|
||||
end, direction, self.style.arrow_size
|
||||
)
|
||||
color = self.pen().color()
|
||||
if self.style.arrow_style == "filled":
|
||||
painter.setPen(Qt.PenStyle.NoPen)
|
||||
painter.setBrush(color)
|
||||
painter.drawPolygon(QPolygonF([tip, left, right]))
|
||||
return
|
||||
painter.setPen(QPen(color, self.pen().widthF(), Qt.PenStyle.SolidLine))
|
||||
painter.setBrush(Qt.BrushStyle.NoBrush)
|
||||
painter.drawLine(tip, left)
|
||||
if self.style.arrow_style == "open":
|
||||
painter.drawLine(tip, right)
|
||||
|
||||
def paint(self, painter: QPainter, option, widget=None) -> None:
|
||||
super().paint(painter, option, widget)
|
||||
painter.setPen(Qt.PenStyle.NoPen)
|
||||
painter.setBrush(self.pen().color())
|
||||
if self.style.arrow_at_target and not self.target_is_junction:
|
||||
painter.drawPolygon(self._arrow(self.end, self.end_direction, self.style.arrow_size))
|
||||
self._draw_arrow(painter, self.end, self.end_direction)
|
||||
if self.style.arrow_at_source and not self.source_is_junction:
|
||||
painter.drawPolygon(
|
||||
self._arrow(self.start, -self.start_direction, self.style.arrow_size)
|
||||
)
|
||||
self._draw_arrow(painter, self.start, -self.start_direction)
|
||||
|
||||
|
||||
class WaypointHandle(QGraphicsEllipseItem):
|
||||
@@ -1207,9 +1223,22 @@ class GraphScene(QGraphicsScene):
|
||||
def add_pairs(
|
||||
source_item: ComponentGraphicsItem, target_item: ComponentGraphicsItem
|
||||
) -> None:
|
||||
for output in source_item.component.outputs:
|
||||
source_ports = [
|
||||
*source_item.component.outputs,
|
||||
*(
|
||||
port
|
||||
for port in source_item.component.inputs
|
||||
if port.orientation == "indifferent"
|
||||
),
|
||||
]
|
||||
for output in source_ports:
|
||||
for input_port in target_item.component.inputs:
|
||||
if not PortTypeRegistry.compatible(output.type, input_port.type):
|
||||
if not PortTypeRegistry.compatible(
|
||||
output.type,
|
||||
input_port.type,
|
||||
output.domain,
|
||||
input_port.domain,
|
||||
):
|
||||
continue
|
||||
source = Endpoint(block=source_item.component_id, port=output.id)
|
||||
target = Endpoint(block=target_item.component_id, port=input_port.id)
|
||||
@@ -1240,7 +1269,9 @@ class GraphScene(QGraphicsScene):
|
||||
if terminal.direction == "input":
|
||||
for port in component.component.inputs:
|
||||
target = Endpoint(block=component.component_id, port=port.id)
|
||||
if not PortTypeRegistry.compatible(terminal.port.type, port.type):
|
||||
if not PortTypeRegistry.compatible(
|
||||
terminal.port.type, port.type, terminal.port.domain, port.domain
|
||||
):
|
||||
continue
|
||||
if not self.controller.endpoint_accepts_connection(interface, "source"):
|
||||
continue
|
||||
@@ -1254,9 +1285,19 @@ class GraphScene(QGraphicsScene):
|
||||
)
|
||||
)
|
||||
else:
|
||||
for port in component.component.outputs:
|
||||
ports = [
|
||||
*component.component.outputs,
|
||||
*(
|
||||
port
|
||||
for port in component.component.inputs
|
||||
if port.orientation == "indifferent"
|
||||
),
|
||||
]
|
||||
for port in ports:
|
||||
source = Endpoint(block=component.component_id, port=port.id)
|
||||
if not PortTypeRegistry.compatible(port.type, terminal.port.type):
|
||||
if not PortTypeRegistry.compatible(
|
||||
port.type, terminal.port.type, port.domain, terminal.port.domain
|
||||
):
|
||||
continue
|
||||
if not self.controller.endpoint_accepts_connection(source, "source"):
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user