Skip to main content

A Python library for drawing graphs from scratch.

Project description

graphD

A Python library for drawing graphs from scratch. This library implements its own rendering system without relying on external drawing libraries.

Features

  • Pure Python implementation, no external dependencies
  • Custom rendering engine with a force-directed layout algorithm
  • Support for node and edge attributes (color, size, labels, etc.)
  • Built from scratch, every pixel calculated individually
  • NEW: Support for directed edges with arrowheads
  • NEW: Various line styles (solid, dashed, dotted)
  • NEW: BMP file format support
  • NEW: Factory functions for common graph types
  • NEW: Methods for graph manipulation (remove nodes/edges, neighbors)
  • NEW: Data visualizations (histograms, pie charts, heatmaps)
  • NEW: Multiple charts on one canvas (subplots)
  • NEW: Animation capabilities for dynamic graphs
  • NEW: Interactive elements (tooltips, highlights, point labels)

Installation

pip install graphD

Usage

Basic Example

import graphD

# Create a graph
g = graphD.Graph()

# Add nodes with attributes
g.add_node('A', color='#FF0000', size=20, label='Node A')  # Red node
g.add_node('B', color='#00FF00', size=15, label='Node B')  # Green node
g.add_node('C', color='#0000FF', size=25, label='Node C')  # Blue node

# Add edges
g.add_edge('A', 'B', color='#888888')  # Gray edge
g.add_edge('B', 'C')
g.add_edge('A', 'C', style='dashed')   # Dashed edge

# Render the graph to an image file
image_file = graphD.plot(g, filename="my_graph.bmp")
print(f"Graph image saved to: {image_file}")

Directed Graphs

import graphD

# Create a directed graph
g = graphD.Graph()
g.is_directed = True  # Set the graph to be directed

# Add nodes
g.add_node('A', color='#FF0000')
g.add_node('B', color='#00FF00')
g.add_node('C', color='#0000FF')

# Add directed edges (arrows will be drawn automatically)
g.add_edge('A', 'B')
g.add_edge('B', 'C')
g.add_edge('C', 'A')

# You can also add directed edges to an undirected graph
g2 = graphD.Graph()
g2.add_node('X')
g2.add_node('Y')
g2.add_directed_edge('X', 'Y', color='#FF00FF')  # Explicitly directed edge

graphD.plot(g, "directed_graph.bmp")

Factory Functions

import graphD

# Create common graph types with one line
complete = graphD.create_complete_graph(5)  # Complete graph with 5 nodes
star = graphD.create_star_graph(8)         # Star graph with 8 outer nodes
path = graphD.create_path_graph(10)        # Path with 10 nodes
cycle = graphD.create_cycle_graph(6)       # Cycle with 6 nodes

# Render them
graphD.plot(complete, "complete_graph.bmp")
graphD.plot(star, "star_graph.bmp")
graphD.plot(path, "path_graph.bmp")
graphD.plot(cycle, "cycle_graph.bmp")

Data Visualizations

import graphD

# Create a histogram
data = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 6, 6, 7]
hist = graphD.Histogram(data, bins=7, title="Sample Histogram", 
                        x_label="Value", y_label="Frequency", color="blue")

# Create and plot a pie chart
pie_data = {"Category A": 25, "Category B": 40, "Category C": 15, "Category D": 20}
pie = graphD.PieChart(pie_data, title="Sample Pie Chart", 
                     colors=["#FF5733", "#33FF57", "#3357FF", "#F3FF33"])

# Create a heatmap
heat_data = [
    [1, 2, 3, 4, 5],
    [2, 4, 6, 8, 10],
    [3, 6, 9, 12, 15],
    [4, 8, 12, 16, 20],
    [5, 10, 15, 20, 25]
]
heatmap = graphD.Heatmap(heat_data, title="Sample Heatmap",
                        x_labels=["A", "B", "C", "D", "E"],
                        y_labels=["V", "W", "X", "Y", "Z"],
                        colormap="default", show_values=True)

# Convert to graph and plot
graphD.plot(hist.to_graph(), "histogram.ppm")
graphD.plot(pie.to_graph(), "pie_chart.ppm")
graphD.plot(heatmap.to_graph(), "heatmap.ppm")

Multiple Charts (Subplots)

import graphD

# Create multiple visualizations
hist = graphD.Histogram([1, 2, 2, 3, 3, 3, 4, 4, 4, 5], title="Histogram")
pie = graphD.PieChart({"A": 30, "B": 40, "C": 30}, title="Pie Chart")

# Create subplot layout (2x2 grid)
subplots = graphD.Subplot(rows=2, cols=2, width=1200, height=1000, 
                        title="Multiple Visualizations", spacing=30)

# Add the visualizations to the subplot
subplots.add_graph(hist.to_graph(), row=0, col=0, title="Data Distribution")
subplots.add_graph(pie.to_graph(), row=0, col=1, title="Data Categories")

# Create a simple graph for another subplot
graph = graphD.Graph()
graph.add_node("A", x=20, y=20)
graph.add_node("B", x=80, y=20)
graph.add_node("C", x=50, y=80)
graph.add_edge("A", "B")
graph.add_edge("B", "C")
graph.add_edge("C", "A")
subplots.add_graph(graph, row=1, col=0, title="Simple Graph")

# Plot the combined visualization
combined_graph = subplots.to_graph()
graphD.plot(combined_graph, "subplots.ppm")

Interactive Elements

import graphD

# Create a basic graph
graph = graphD.Graph()
graph.add_node("A", x=50, y=20, size=10, color="red")
graph.add_node("B", x=20, y=70, size=10, color="blue")
graph.add_edge("A", "B")

# Make it interactive
ig = graphD.InteractiveGraph(graph)

# Add tooltips
ig.add_tooltip("A", "Node A: Important node with detailed information")

# Add highlights on hover
ig.add_highlight("A", color="yellow", thickness=3, glow=True)

# Add always-visible point labels
ig.add_point_label("B", "Node B", position="top", always_visible=True)

# Get the enhanced graph and plot
interactive_graph = ig.get_graph()
graphD.plot(interactive_graph, "interactive.ppm")

Animation

import graphD

# Create initial empty graph
graph = graphD.Graph()

# Create animation builder
builder = graphD.AnimationBuilder(graph, title="Growing Graph", loop=True, fps=10)

# Add nodes one by one
builder.add_node("A", x=50, y=50, color="red", duration=0.5)
builder.add_node("B", x=150, y=50, color="blue", duration=0.5)
builder.add_node("C", x=50, y=150, color="green", duration=0.5)

# Add edges one by one
builder.add_edge("A", "B", color="gray", duration=0.3)
builder.add_edge("B", "C", color="gray", duration=0.3)
builder.add_edge("C", "A", color="gray", duration=0.3)

# Change node properties
builder.update_node("A", color="yellow", size=15, duration=0.5)

# Get the animation and render it
animation = builder.get_animation()
animation_graph = animation.to_graph()
graphD.plot(animation_graph, "animation.ppm")

Node and Edge Management

import graphD

g = graphD.Graph()

# Add nodes
g.add_node('A')
g.add_node('B')
g.add_node('C')
g.add_edge('A', 'B')
g.add_edge('B', 'C')

# Check existence and neighbors
print(g.has_edge('A', 'B'))        # True
print(g.get_neighbors('B'))        # ['A', 'C']

# Remove node (automatically removes connected edges)
g.remove_node('B')
print(g.get_nodes())               # {'A': {}, 'C': {}}
print(g.get_edges())               # [] (No edges remain)

# Get graph info
print(g.node_count())              # 2
print(g.edge_count())              # 0

Advanced Usage

You can directly use the renderer components for more control:

from graphD import Graph, GraphRenderer, Color

# Create graph and add elements
graph = Graph()
graph.add_node('1', color='#FF0000')
graph.add_node('2', color='#00FF00')
graph.add_directed_edge('1', '2', style='dotted')

# Create a custom renderer
renderer = GraphRenderer(width=1200, height=900)
renderer.node_color = Color(0, 0, 255)  # Default blue for nodes
renderer.edge_color = Color(0, 0, 0)    # Default black for edges
renderer.arrow_size = 15                # Larger arrowheads
renderer.render(graph, "custom_graph.bmp")

Output Formats

The library natively supports:

  • PPM: Simple portable pixmap format
  • BMP: Windows bitmap format

For PNG/JPEG formats, you'll need to use external conversion tools or libraries.

How It Works

The library uses a force-directed layout algorithm to position nodes in a visually appealing way:

  1. Nodes are initially arranged in a circle
  2. Repulsive forces push nodes away from each other
  3. Attractive forces pull connected nodes toward each other
  4. After several iterations, nodes settle into a balanced layout

Drawing is performed using basic algorithms:

  • Bresenham's line algorithm for edges
  • Bresenham's circle algorithm for nodes
  • Pattern-based drawing for dashed and dotted lines
  • Custom arrowhead drawing for directed edges

The new visualization features are built on the same underlying Graph object, allowing for a unified interface across all visualization types.

Contributing

Contributions are welcome! Please open an issue or submit a pull request.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

graphd-0.2.0.tar.gz (34.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

graphd-0.2.0-py3-none-any.whl (34.7 kB view details)

Uploaded Python 3

File details

Details for the file graphd-0.2.0.tar.gz.

File metadata

  • Download URL: graphd-0.2.0.tar.gz
  • Upload date:
  • Size: 34.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.2

File hashes

Hashes for graphd-0.2.0.tar.gz
Algorithm Hash digest
SHA256 68fd262f36cffccee3c060bcc48e8f2eb92351cf303367efa5ad2a77422ff9c3
MD5 e6aa88051f9aa2ed4ae47e24ce644379
BLAKE2b-256 beb279b9c29fe795e260af9ba585dbbdac2ea9ba625d99d5ac046a8efe33a995

See more details on using hashes here.

File details

Details for the file graphd-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: graphd-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 34.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.13.2

File hashes

Hashes for graphd-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 05cb3af1b52e0a76e227b5a3dddf79d4f8163a9f1be527f0c8f7db8269c4da22
MD5 80aa32ca336d8e882be233f35bed9005
BLAKE2b-256 1b5733ce1adf4ca786ccafef4af9256712e537d5ff02828064a52e19f206597e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page