Start with causality inference
This commit is contained in:
@@ -458,8 +458,7 @@ class IconEditorDialog(QDialog):
|
||||
self.ui.setupUi(self)
|
||||
self.setWindowTitle(f"Icon Editor — {component.name}")
|
||||
self.icon = Icon.from_dict(component.icon.to_dict())
|
||||
self.inputs = deepcopy(component.inputs)
|
||||
self.outputs = deepcopy(component.outputs)
|
||||
self.ports = deepcopy(component.ports)
|
||||
self.tool_group = QButtonGroup(self)
|
||||
self.tool_group.setExclusive(True)
|
||||
self.tool_group.addButton(self.ui.pointerButton)
|
||||
@@ -485,8 +484,10 @@ class IconEditorDialog(QDialog):
|
||||
self.scene.addRect(self.scene.sceneRect(), QPen(QColor("#64748b"), 0)).setZValue(-100)
|
||||
for element in self.icon.elements:
|
||||
self.scene.addItem(ShapeItem(element))
|
||||
self._add_ports(self.inputs, "input", 0.0)
|
||||
self._add_ports(self.outputs, "output", self.icon.width)
|
||||
input_side = [port for port in self.ports if port.orientation != "output"]
|
||||
output_side = [port for port in self.ports if port.orientation == "output"]
|
||||
self._add_ports(input_side, "input", 0.0)
|
||||
self._add_ports(output_side, "output", self.icon.width)
|
||||
self._initial_fit_pending = True
|
||||
|
||||
def showEvent(self, event) -> None: # noqa: N802 (Qt API name)
|
||||
|
||||
@@ -173,8 +173,16 @@ class ComponentGraphicsItem(QGraphicsObject):
|
||||
| QGraphicsItem.GraphicsItemFlag.ItemIsSelectable
|
||||
| QGraphicsItem.GraphicsItemFlag.ItemSendsGeometryChanges
|
||||
)
|
||||
self.input_ports = self._create_ports(component.inputs, "target", 0.0)
|
||||
self.output_ports = self._create_ports(component.outputs, "source", self.WIDTH)
|
||||
self.input_ports = self._create_ports(
|
||||
[port for port in component.ports if port.orientation != "output"],
|
||||
"target",
|
||||
0.0,
|
||||
)
|
||||
self.output_ports = self._create_ports(
|
||||
[port for port in component.ports if port.orientation == "output"],
|
||||
"source",
|
||||
self.WIDTH,
|
||||
)
|
||||
self.setTransformOriginPoint(self.hitbox.center())
|
||||
self.setRotation(component.rotation)
|
||||
self.name_label: NameLabelItem | None = None
|
||||
@@ -371,8 +379,10 @@ class ConnectionGraphicsItem(QGraphicsPathItem):
|
||||
super().__init__()
|
||||
self.connection_id = connection.id
|
||||
self.name = connection.name
|
||||
self.connection_type = connection.type
|
||||
self.source_is_junction = connection.source.junction is not None
|
||||
self.target_is_junction = connection.target.junction is not None
|
||||
self.causality = connection.causality
|
||||
self.controller = controller
|
||||
self.style = style or ConnectionStyle()
|
||||
self.start = QPointF()
|
||||
@@ -417,7 +427,11 @@ class ConnectionGraphicsItem(QGraphicsPathItem):
|
||||
self.setSelected(True)
|
||||
menu = QMenu()
|
||||
add_node_action = menu.addAction("Add Node")
|
||||
add_junction_action = menu.addAction("Add Junction")
|
||||
add_junction_action = (
|
||||
menu.addAction("Add Junction")
|
||||
if self.connection_type != "power"
|
||||
else None
|
||||
)
|
||||
menu.addSeparator()
|
||||
options_action = menu.addAction("Connection Options…")
|
||||
selected = menu.exec(event.screenPos())
|
||||
@@ -425,7 +439,7 @@ class ConnectionGraphicsItem(QGraphicsPathItem):
|
||||
scene = self.scene()
|
||||
if isinstance(scene, GraphScene):
|
||||
scene.add_route_node("connection", self.connection_id, event.scenePos())
|
||||
elif selected is add_junction_action:
|
||||
elif add_junction_action is not None and selected is add_junction_action:
|
||||
scene = self.scene()
|
||||
if isinstance(scene, GraphScene):
|
||||
scene.add_connection_junction(self.connection_id, event.scenePos())
|
||||
@@ -512,12 +526,36 @@ class ConnectionGraphicsItem(QGraphicsPathItem):
|
||||
if self.style.arrow_style == "open":
|
||||
painter.drawLine(tip, right)
|
||||
|
||||
def _draw_causality_mark(
|
||||
self, painter: QPainter, point: QPointF, direction: QPointF, *, warning: bool
|
||||
) -> None:
|
||||
length = max(0.001, (direction.x() ** 2 + direction.y() ** 2) ** 0.5)
|
||||
normal = QPointF(-direction.y() / length, direction.x() / length)
|
||||
half_length = 6.0
|
||||
color = QColor("#c25a00") if warning else self.pen().color()
|
||||
painter.setPen(QPen(color, max(2.0, self.pen().widthF()), Qt.PenStyle.SolidLine))
|
||||
painter.drawLine(point - normal * half_length, point + normal * half_length)
|
||||
|
||||
def paint(self, painter: QPainter, option, widget=None) -> None:
|
||||
super().paint(painter, option, widget)
|
||||
if self.style.arrow_at_target and not self.target_is_junction:
|
||||
self._draw_arrow(painter, self.end, self.end_direction)
|
||||
if self.style.arrow_at_source and not self.source_is_junction:
|
||||
self._draw_arrow(painter, self.start, -self.start_direction)
|
||||
if self.causality in {"source", "warn_source"}:
|
||||
self._draw_causality_mark(
|
||||
painter,
|
||||
self.start,
|
||||
self.start_direction,
|
||||
warning=self.causality == "warn_source",
|
||||
)
|
||||
elif self.causality in {"target", "warn_target"}:
|
||||
self._draw_causality_mark(
|
||||
painter,
|
||||
self.end,
|
||||
self.end_direction,
|
||||
warning=self.causality == "warn_target",
|
||||
)
|
||||
|
||||
|
||||
class WaypointHandle(QGraphicsEllipseItem):
|
||||
@@ -898,12 +936,12 @@ class GraphScene(QGraphicsScene):
|
||||
owner = self.controller.active_component
|
||||
if owner is None or owner.implementation_kind != "graph":
|
||||
return
|
||||
for port in owner.inputs:
|
||||
for port in (port for port in owner.ports if port.orientation != "output"):
|
||||
item = InterfaceTerminalItem(port, "input", self.controller)
|
||||
self.addItem(item)
|
||||
item.setPos(port.x, port.y)
|
||||
self.input_items[port.id] = item
|
||||
for port in owner.outputs:
|
||||
for port in (port for port in owner.ports if port.orientation == "output"):
|
||||
item = InterfaceTerminalItem(port, "output", self.controller)
|
||||
self.addItem(item)
|
||||
item.setPos(port.x, port.y)
|
||||
@@ -1224,15 +1262,16 @@ class GraphScene(QGraphicsScene):
|
||||
source_item: ComponentGraphicsItem, target_item: ComponentGraphicsItem
|
||||
) -> None:
|
||||
source_ports = [
|
||||
*source_item.component.outputs,
|
||||
*(
|
||||
port
|
||||
for port in source_item.component.inputs
|
||||
if port.orientation == "indifferent"
|
||||
),
|
||||
port
|
||||
for port in source_item.component.ports
|
||||
if port.orientation in {"output", "indifferent"}
|
||||
]
|
||||
for output in source_ports:
|
||||
for input_port in target_item.component.inputs:
|
||||
for input_port in (
|
||||
port
|
||||
for port in target_item.component.ports
|
||||
if port.orientation in {"input", "indifferent"}
|
||||
):
|
||||
if not PortTypeRegistry.compatible(
|
||||
output.type,
|
||||
input_port.type,
|
||||
@@ -1267,7 +1306,11 @@ class GraphScene(QGraphicsScene):
|
||||
choices: list[ConnectionChoice] = []
|
||||
interface = Endpoint(interface=terminal.port.id)
|
||||
if terminal.direction == "input":
|
||||
for port in component.component.inputs:
|
||||
for port in (
|
||||
port
|
||||
for port in component.component.ports
|
||||
if port.orientation in {"input", "indifferent"}
|
||||
):
|
||||
target = Endpoint(block=component.component_id, port=port.id)
|
||||
if not PortTypeRegistry.compatible(
|
||||
terminal.port.type, port.type, terminal.port.domain, port.domain
|
||||
@@ -1286,12 +1329,9 @@ class GraphScene(QGraphicsScene):
|
||||
)
|
||||
else:
|
||||
ports = [
|
||||
*component.component.outputs,
|
||||
*(
|
||||
port
|
||||
for port in component.component.inputs
|
||||
if port.orientation == "indifferent"
|
||||
),
|
||||
port
|
||||
for port in component.component.ports
|
||||
if port.orientation in {"output", "indifferent"}
|
||||
]
|
||||
for port in ports:
|
||||
source = Endpoint(block=component.component_id, port=port.id)
|
||||
@@ -1319,7 +1359,11 @@ class GraphScene(QGraphicsScene):
|
||||
) -> list[ConnectionChoice]:
|
||||
choices: list[ConnectionChoice] = []
|
||||
source = junction.endpoint
|
||||
for port in component.component.inputs:
|
||||
for port in (
|
||||
port
|
||||
for port in component.component.ports
|
||||
if port.orientation in {"input", "indifferent"}
|
||||
):
|
||||
target = Endpoint(block=component.component_id, port=port.id)
|
||||
if not PortTypeRegistry.compatible(junction.junction.type, port.type):
|
||||
continue
|
||||
@@ -1600,6 +1644,8 @@ class GraphScene(QGraphicsScene):
|
||||
graphics = self.connection_items.get(connection_id)
|
||||
if connection is None or graphics is None:
|
||||
return
|
||||
if connection.type == "power":
|
||||
return
|
||||
snapped = _snapped(position)
|
||||
anchors = [graphics.start, *points, graphics.end]
|
||||
|
||||
@@ -1850,8 +1896,7 @@ class GraphWorkspaceView(QGraphicsView):
|
||||
return
|
||||
blocks: set[str] = set()
|
||||
connections: set[str] = set()
|
||||
inputs: set[str] = set()
|
||||
outputs: set[str] = set()
|
||||
ports: set[str] = set()
|
||||
annotations: set[str] = set()
|
||||
for item in self.scene().selectedItems():
|
||||
if isinstance(item, ComponentGraphicsItem):
|
||||
@@ -1859,10 +1904,10 @@ class GraphWorkspaceView(QGraphicsView):
|
||||
elif isinstance(item, ConnectionGraphicsItem):
|
||||
connections.add(item.connection_id)
|
||||
elif isinstance(item, InterfaceTerminalItem):
|
||||
(inputs if item.direction == "input" else outputs).add(item.port.id)
|
||||
ports.add(item.port.id)
|
||||
elif isinstance(item, (AnnotationGraphicsItem, LineAnnotationGraphicsItem)):
|
||||
annotations.add(item.annotation_id)
|
||||
self.controller.delete_selection(blocks, connections, inputs, outputs)
|
||||
self.controller.delete_selection(blocks, connections, ports)
|
||||
self.controller.delete_annotations(annotations)
|
||||
|
||||
def has_selected_components(self) -> bool:
|
||||
|
||||
Reference in New Issue
Block a user