Skip to content

Core Types

Data structures for drawings and robot joints.


Joint Enum

Enum for robot joint names with IDE tab completion support.

from pib3 import Joint, Robot

with Robot(host="172.26.34.149") as robot:
    # Use enum for tab completion
    robot.set_joint(Joint.ELBOW_LEFT, 50.0)
    pos = robot.get_joint(Joint.SHOULDER_VERTICAL_RIGHT)

    # Multiple joints
    robot.set_joints({
        Joint.SHOULDER_VERTICAL_LEFT: 30.0,
        Joint.ELBOW_LEFT: 60.0,
    })

Available Joints

Enum String Value
Head
Joint.TURN_HEAD turn_head_motor
Joint.TILT_HEAD tilt_forward_motor
Left Arm
Joint.SHOULDER_VERTICAL_LEFT shoulder_vertical_left
Joint.SHOULDER_HORIZONTAL_LEFT shoulder_horizontal_left
Joint.UPPER_ARM_LEFT_ROTATION upper_arm_left_rotation
Joint.ELBOW_LEFT elbow_left
Joint.LOWER_ARM_LEFT_ROTATION lower_arm_left_rotation
Joint.WRIST_LEFT wrist_left
Left Hand
Joint.THUMB_LEFT_OPPOSITION thumb_left_opposition
Joint.THUMB_LEFT_STRETCH thumb_left_stretch
Joint.INDEX_LEFT index_left_stretch
Joint.MIDDLE_LEFT middle_left_stretch
Joint.RING_LEFT ring_left_stretch
Joint.PINKY_LEFT pinky_left_stretch
Right Arm
Joint.SHOULDER_VERTICAL_RIGHT shoulder_vertical_right
Joint.SHOULDER_HORIZONTAL_RIGHT shoulder_horizontal_right
Joint.UPPER_ARM_RIGHT_ROTATION upper_arm_right_rotation
Joint.ELBOW_RIGHT elbow_right
Joint.LOWER_ARM_RIGHT_ROTATION lower_arm_right_rotation
Joint.WRIST_RIGHT wrist_right
Right Hand
Joint.THUMB_RIGHT_OPPOSITION thumb_right_opposition
Joint.THUMB_RIGHT_STRETCH thumb_right_stretch
Joint.INDEX_RIGHT index_right_stretch
Joint.MIDDLE_RIGHT middle_right_stretch
Joint.RING_RIGHT ring_right_stretch
Joint.PINKY_RIGHT pinky_right_stretch

Backward Compatibility

String joint names still work: robot.set_joint("elbow_left", 50.0)


Stroke

A stroke represents a single continuous drawing motion - a sequence of points connected without lifting the pen.

Stroke dataclass

Stroke(points, closed=False)

A single continuous drawing stroke (pen-down motion).

Represents a sequence of 2D points that form a continuous line drawn without lifting the pen.

Attributes:

Name Type Description
points ndarray

Array of shape (N, 2) with normalized [0,1] coordinates. (0,0) = top-left of drawing area, (1,1) = bottom-right.

closed bool

If True, the stroke forms a closed loop (first and last points should be connected).

__len__

__len__()

Return number of points in the stroke.

Source code in pib3/types.py
def __len__(self) -> int:
    """Return number of points in the stroke."""
    return len(self.points)

length

length()

Calculate total arc length of the stroke.

Source code in pib3/types.py
def length(self) -> float:
    """Calculate total arc length of the stroke."""
    if len(self.points) < 2:
        return 0.0
    diffs = np.diff(self.points, axis=0)
    distances = np.linalg.norm(diffs, axis=1)
    return float(np.sum(distances))

reverse

reverse()

Return a new stroke with reversed point order.

Source code in pib3/types.py
def reverse(self) -> "Stroke":
    """Return a new stroke with reversed point order."""
    return Stroke(points=self.points[::-1].copy(), closed=self.closed)

start

start()

Return the starting point of the stroke.

Source code in pib3/types.py
def start(self) -> np.ndarray:
    """Return the starting point of the stroke."""
    return self.points[0]

end

end()

Return the ending point of the stroke.

Source code in pib3/types.py
def end(self) -> np.ndarray:
    """Return the ending point of the stroke."""
    return self.points[-1]

Creating Strokes

import numpy as np
from pib3 import Stroke

# From a list of points
stroke = Stroke(
    points=[[0.1, 0.1], [0.5, 0.2], [0.9, 0.1]],
    closed=False
)

# From numpy array
points = np.array([[0, 0], [1, 0], [1, 1], [0, 1]])
closed_stroke = Stroke(points=points, closed=True)

# Access properties
print(f"Length: {stroke.length()}")
print(f"Start: {stroke.start()}")
print(f"End: {stroke.end()}")

Coordinate System

Stroke coordinates are normalized to the range [0, 1]:

  • (0, 0) = top-left corner
  • (1, 1) = bottom-right corner
  • (0.5, 0.5) = center

Sketch

A sketch is a collection of strokes representing a complete drawing.

Sketch dataclass

Sketch(strokes=list(), source_size=None)

A collection of strokes extracted from an image.

Represents a complete drawing as a list of individual strokes, typically in optimized drawing order to minimize pen-up travel.

Attributes:

Name Type Description
strokes List[Stroke]

List of Stroke objects in drawing order.

source_size Optional[Tuple[int, int]]

Original image dimensions (width, height) in pixels. None if not created from an image.

__len__

__len__()

Return number of strokes in the sketch.

Source code in pib3/types.py
def __len__(self) -> int:
    """Return number of strokes in the sketch."""
    return len(self.strokes)

__iter__

__iter__()

Iterate over strokes.

Source code in pib3/types.py
def __iter__(self):
    """Iterate over strokes."""
    return iter(self.strokes)

__getitem__

__getitem__(idx)

Get stroke by index.

Source code in pib3/types.py
def __getitem__(self, idx) -> Stroke:
    """Get stroke by index."""
    return self.strokes[idx]

total_points

total_points()

Return total number of points across all strokes.

Source code in pib3/types.py
def total_points(self) -> int:
    """Return total number of points across all strokes."""
    return sum(len(s) for s in self.strokes)

total_length

total_length()

Return total arc length of all strokes.

Source code in pib3/types.py
def total_length(self) -> float:
    """Return total arc length of all strokes."""
    return sum(s.length() for s in self.strokes)

bounds

bounds()

Return bounding box (min_u, min_v, max_u, max_v) of all strokes.

Source code in pib3/types.py
def bounds(self) -> Tuple[float, float, float, float]:
    """Return bounding box (min_u, min_v, max_u, max_v) of all strokes."""
    if not self.strokes:
        return (0.0, 0.0, 1.0, 1.0)
    all_points = np.vstack([s.points for s in self.strokes])
    min_coords = all_points.min(axis=0)
    max_coords = all_points.max(axis=0)
    return (min_coords[0], min_coords[1], max_coords[0], max_coords[1])

add_stroke

add_stroke(stroke)

Add a stroke to the sketch.

Source code in pib3/types.py
def add_stroke(self, stroke: Stroke) -> None:
    """Add a stroke to the sketch."""
    self.strokes.append(stroke)

to_dict

to_dict()

Convert sketch to a JSON-serializable dictionary.

Source code in pib3/types.py
def to_dict(self) -> dict:
    """Convert sketch to a JSON-serializable dictionary."""
    return {
        "strokes": [
            {
                "points": s.points.tolist(),
                "closed": s.closed,
            }
            for s in self.strokes
        ],
        "source_size": self.source_size,
    }

from_dict classmethod

from_dict(data)

Create a Sketch from a dictionary (e.g., loaded from JSON).

Source code in pib3/types.py
@classmethod
def from_dict(cls, data: dict) -> "Sketch":
    """Create a Sketch from a dictionary (e.g., loaded from JSON)."""
    strokes = [
        Stroke(
            points=np.array(s["points"]),
            closed=s.get("closed", False),
        )
        for s in data.get("strokes", [])
    ]
    return cls(
        strokes=strokes,
        source_size=tuple(data["source_size"]) if data.get("source_size") else None,
    )

Creating Sketches

from pib3 import Sketch, Stroke

# Empty sketch
sketch = Sketch()

# Add strokes
sketch.add_stroke(Stroke(points=[[0.1, 0.1], [0.9, 0.1]]))
sketch.add_stroke(Stroke(points=[[0.1, 0.9], [0.9, 0.9]]))

# With strokes and source info
sketch = Sketch(
    strokes=[stroke1, stroke2],
    source_size=(800, 600)  # Original image dimensions
)

Iterating and Accessing

# Iterate over strokes
for stroke in sketch:
    print(f"Stroke with {len(stroke)} points")

# Access by index
first = sketch[0]
last = sketch[-1]

# Get statistics
print(f"Strokes: {len(sketch)}")
print(f"Total points: {sketch.total_points()}")
print(f"Total length: {sketch.total_length()}")
print(f"Bounds: {sketch.bounds()}")

Serialization

import json
from pib3 import Sketch

# To dictionary (JSON-serializable)
data = sketch.to_dict()

# Save to file
with open("sketch.json", "w") as f:
    json.dump(data, f)

# Load from file
with open("sketch.json") as f:
    data = json.load(f)
sketch = Sketch.from_dict(data)