Line Symbol Clamping
This guide covers clamping branch corners to line-type node symbols (busbars). Unlike point symbols, line symbols have extent: connections can attach anywhere along the line segment.
Full Example
View the complete code: 13_clamp_line_symbols.py
Line Symbol Types
PyPtP supports two line-type node symbols:
| Symbol | Enum Value | Orientation |
|---|---|---|
| Vertical busbar | NodePresentationSymbol.VERTICAL_LINE | Extends up and down |
| Horizontal busbar | NodePresentationSymbol.HORIZONTAL_LINE | Extends left and right |
Line Extent Calculation
Line symbols extend from their center position based on the size property:
extent = size × 10 pixels in each directionVertical Line Example
A vertical busbar at (100, 300) with size=3:
Vertical Busbar Extent (size=3)
extent = size × 10 pixels in each direction
ATop:
y - 30 → (100, 270)BCenter:
(100, 300)CBottom:
y + 30 → (100, 330)Horizontal Line Example
A horizontal busbar at (100, 500) with size=2:
Horizontal Busbar Extent (size=2)
ALeft:
x - 20 → (80, 500)BCenter:
(100, 500)CRight:
x + 20 → (120, 500)Clamping Behavior
For line symbols, clamp_point() finds the nearest point on the line segment, not just the center:
python
from pyptp.elements.enums import NodePresentationSymbol
# Vertical busbar at (100, 300) with size=3
# Line extends from (100, 270) to (100, 330)
busbar = NodeLV(
NodeLV.General(name="VerticalBusbar"),
presentations=[
NodePresentation(
sheet=sheet_guid,
x=100,
y=300,
symbol=NodePresentationSymbol.VERTICAL_LINE,
size=3,
)
],
)
busbar_pres = busbar.presentations[0]
# Point below the line → clamps to bottom edge
busbar_pres.clamp_point((100, 340)) # Returns (100, 330)
# Point above the line → clamps to top edge
busbar_pres.clamp_point((100, 250)) # Returns (100, 270)
# Point on the line → returns that point
busbar_pres.clamp_point((100, 290)) # Returns (100, 290)
# Point to the side → clamps to nearest point on line
busbar_pres.clamp_point((120, 310)) # Returns (100, 310)Vertical Busbar Connection
python
from pyptp import NetworkLV, configure_logging
from pyptp.elements.enums import NodePresentationSymbol
from pyptp.elements.lv.link import LinkLV
from pyptp.elements.lv.node import NodeLV
from pyptp.elements.lv.presentations import BranchPresentation, NodePresentation
from pyptp.elements.lv.sheet import SheetLV
configure_logging(level="INFO")
network = NetworkLV()
sheet = SheetLV(SheetLV.General(name="Line Symbol Examples"))
sheet.register(network)
sheet_guid = sheet.general.guid
# Vertical busbar at (100, 300) with size=3
busbar = NodeLV(
NodeLV.General(name="VerticalBusbar"),
presentations=[
NodePresentation(
sheet=sheet_guid,
x=100,
y=300,
symbol=NodePresentationSymbol.VERTICAL_LINE,
size=3,
)
],
)
busbar.register(network)
# Load node
load = NodeLV(
NodeLV.General(name="Load"),
presentations=[NodePresentation(sheet=sheet_guid, x=300, y=350)],
)
load.register(network)
# Get presentations
busbar_pres = busbar.presentations[0]
load_pres = load.presentations[0]
# Connect - point (100, 340) is below busbar, clamps to (100, 330)
feeder = LinkLV(
LinkLV.General(name="Feeder", node1=busbar.general.guid, node2=load.general.guid),
presentations=[
BranchPresentation(
sheet=sheet_guid,
first_corners=[
busbar_pres.clamp_point((100, 340)), # → (100, 330)
(150, 340),
(200, 350),
],
second_corners=[load_pres.clamp_point((300, 350))],
)
],
)
feeder.register(network)Horizontal Busbar Connection
python
# Horizontal busbar at (100, 500) with size=2
# Line extends from (80, 500) to (120, 500)
busbar = NodeLV(
NodeLV.General(name="HorizontalBusbar"),
presentations=[
NodePresentation(
sheet=sheet_guid,
x=100,
y=500,
symbol=NodePresentationSymbol.HORIZONTAL_LINE,
size=2,
)
],
)
busbar.register(network)
load = NodeLV(
NodeLV.General(name="Load"),
presentations=[NodePresentation(sheet=sheet_guid, x=300, y=500)],
)
load.register(network)
busbar_pres = busbar.presentations[0]
load_pres = load.presentations[0]
# Point (150, 500) is beyond the line, clamps to right edge (120, 500)
feeder = LinkLV(
LinkLV.General(name="Feeder", node1=busbar.general.guid, node2=load.general.guid),
presentations=[
BranchPresentation(
sheet=sheet_guid,
first_corners=[
busbar_pres.clamp_point((150, 500)), # → (120, 500)
(200, 500),
],
second_corners=[load_pres.clamp_point((300, 500))],
)
],
)
feeder.register(network)contains_point() Method
Check if a point is within the node's clickable area:
python
# Vertical busbar (100, 270) to (100, 330)
busbar_pres.contains_point((100, 300)) # True - on line
busbar_pres.contains_point((100, 340)) # False - below line
busbar_pres.contains_point((105, 300)) # True - within toleranceThis is useful for hit-testing and validation.
Size Reference Table
| Size | Extent | Total Length |
|---|---|---|
| 1 | ±10 px | 20 px |
| 2 | ±20 px | 40 px |
| 3 | ±30 px | 60 px |
| 4 | ±40 px | 80 px |
| 5 | ±50 px | 100 px |
Complete Example
python
"""Clamp branch corners to line symbol nodes.
Line symbols (VERTICAL_LINE, HORIZONTAL_LINE) have extent based on their size.
clamp_point() finds the nearest point on the line segment, not just the center.
"""
from pyptp import NetworkLV, configure_logging
from pyptp.elements.enums import NodePresentationSymbol
from pyptp.elements.lv.link import LinkLV
from pyptp.elements.lv.node import NodeLV
from pyptp.elements.lv.presentations import BranchPresentation, NodePresentation
from pyptp.elements.lv.sheet import SheetLV
from pyptp.ptp_log import logger
configure_logging(level="INFO")
network = NetworkLV()
sheet = SheetLV(SheetLV.General(name="Line Symbol Examples"))
sheet.register(network)
sheet_guid = sheet.general.guid
# Example 1: Vertical line node
busbar = NodeLV(
NodeLV.General(name="VerticalBusbar"),
presentations=[
NodePresentation(
sheet=sheet_guid,
x=100,
y=300,
symbol=NodePresentationSymbol.VERTICAL_LINE,
size=3,
)
],
)
busbar.register(network)
load = NodeLV(
NodeLV.General(name="VerticalLoad"),
presentations=[NodePresentation(sheet=sheet_guid, x=300, y=350)],
)
load.register(network)
busbar_pres = busbar.presentations[0]
load_pres = load.presentations[0]
feeder = LinkLV(
LinkLV.General(name="VerticalFeeder", node1=busbar.general.guid, node2=load.general.guid),
presentations=[
BranchPresentation(
sheet=sheet_guid,
first_corners=[busbar_pres.clamp_point((100, 340)), (150, 340), (200, 350)],
second_corners=[load_pres.clamp_point((300, 350))],
)
],
)
feeder.register(network)
# Example 2: Horizontal line node
busbar2 = NodeLV(
NodeLV.General(name="HorizontalBusbar"),
presentations=[
NodePresentation(
sheet=sheet_guid,
x=100,
y=500,
symbol=NodePresentationSymbol.HORIZONTAL_LINE,
size=2,
)
],
)
busbar2.register(network)
load2 = NodeLV(
NodeLV.General(name="HorizontalLoad"),
presentations=[NodePresentation(sheet=sheet_guid, x=300, y=500)],
)
load2.register(network)
busbar2_pres = busbar2.presentations[0]
load2_pres = load2.presentations[0]
feeder2 = LinkLV(
LinkLV.General(name="HorizontalFeeder", node1=busbar2.general.guid, node2=load2.general.guid),
presentations=[
BranchPresentation(
sheet=sheet_guid,
first_corners=[busbar2_pres.clamp_point((150, 500)), (200, 500)],
second_corners=[load2_pres.clamp_point((300, 500))],
)
],
)
feeder2.register(network)
network.save("clamp_line_symbols_example.gnf")
logger.info("Saved clamp_line_symbols_example.gnf")