Metadata-Version: 2.4
Name: distforge
Version: 1.0.0
Summary: A Python library for SDF modeling with real-time GLSL rendering and mesh export.
Home-page: https://github.com/nassimberrada/distforge
Author: nassimberrada
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Multimedia :: Graphics :: 3D Modeling
Classifier: Topic :: Scientific/Engineering :: Visualization
Requires-Python: >=3.6
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy
Requires-Dist: scikit-image>=0.17
Requires-Dist: watchdog
Requires-Dist: moderngl
Requires-Dist: glfw
Provides-Extra: ui
Requires-Dist: imgui[glfw]>=2.0.0; extra == "ui"
Provides-Extra: export
Requires-Dist: pygltflib; extra == "export"
Provides-Extra: saving
Requires-Dist: trimesh; extra == "saving"
Provides-Extra: full
Requires-Dist: imgui[glfw]>=2.0.0; extra == "full"
Requires-Dist: pygltflib; extra == "full"
Requires-Dist: trimesh; extra == "full"
Dynamic: author
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: license-file
Dynamic: provides-extra
Dynamic: requires-dist
Dynamic: requires-python
Dynamic: summary

<p align="center">
  <picture>
    <source srcset="./assets/logo_dark.png" media="(prefers-color-scheme: dark)">
    <source srcset="./assets/logo_light.png" media="(prefers-color-scheme: light)">
    <img src="./assets/logo_light.png" alt="DistForge Logo" height="200">
  </picture>
</p>

# DistForge

## About

DistForge is a Python library for creating 3D models using Signed Distance Functions (SDFs). It provides a real-time, interactive rendering experience in a native desktop window, powered by GLSL raymarching.

## Features

- **Simple, Pythonic API:** Define complex shapes by combining primitives using standard operators (`|`, `-`, `&`) and chaining transformations.
- **Real-time Rendering with Hot-Reloading:** Get instant visual feedback in a lightweight native window powered by `moderngl` and `glfw`. Changes to your script are reloaded automatically.
- **Mesh Exporting:** Save your creations as `.stl`, `.obj`, or `.glb` files for 3D printing or use in other software.
- **Interactive Parameters:** Define parameters that can be controlled by UI sliders in real-time (with a compatible UI renderer).
- **Standalone GLSL Export:** Export your entire scene to a single, self-contained GLSL fragment shader for use in game engines or graphics applications.
- **Custom GLSL Primitives:** Write custom SDF logic directly in GLSL using the `Forge` object for maximum flexibility and performance.

## Getting Started

### Hello Forge

Define a simple shape and open a real-time preview window with just a few lines of code.

<div style="display:flex; align-items:center; gap:24px; flex-wrap:wrap;">
<div style="flex:1.3; min-width:300px;">

```python
from distforge import sphere, box

shape = sphere(1) & box(1.5)
shape.render()
```

</div>
<div style="flex:1;  text-align:center; padding-bottom: 30px"> <img src="./assets/hello_forge.png" width="100%"> </div>
</div>

### Live Preview & Hot-Reloading
For an interactive workflow, wrap your scene definition in a `main()` function. When you save any changes to the file, the preview window will automatically update.

<div style="display:flex; align-items:center; gap:24px; flex-wrap:wrap;">
<div style="flex:1.3; min-width:300px;">

```python
from distforge import box, sphere

def main():
    """
    While this script is running, try changing the values below and
    saving the file. The render window will update automatically.
    """
    box_size = 1.6
    sphere_radius = 1.2
    
    scene = box(box_size) | sphere(sphere_radius)
    return scene

if __name__ == "__main__":
    scene = main()
    scene.render()
```

</div>
<div style="flex:1;  text-align:center; padding-bottom: 30px"> <img src="./assets/hot_reloading.png" width="100%"></div>
</div>


### Saving to a Mesh File
You can save any static model to an `.stl`, `.obj`, or `.glb` file. The `.save()` method uses the Marching Cubes algorithm to generate a mesh from the SDF.

```python
from distforge import sphere, box

shape = box(1.5) - sphere(1.2)
shape.save('model.obj', samples=2**22)
```

## Core Concepts

### Primitives & Operations
Create complex objects by starting with primitives and combining them with Python's bitwise operators: `|` for union, `&` for intersection, and `-` for difference.

<div style="display:flex; align-items:center; gap:24px; flex-wrap:wrap;">
<div style="flex:1.3; min-width:300px;">

```python
from distforge import sphere, box

b = box(1.5)
s = sphere(1.0)
scene = b - s
scene.render()
```

</div>
<div style="flex:1;  text-align:center; padding-bottom: 30px"> <img src="./assets/primitives_operations.png" width="100%"> </div>
</div>

### Transformations & Shaping
Chain methods to transform, shape, and repeat objects.

<div style="display:flex; align-items:center; gap:24px; flex-wrap:wrap;">
<div style="flex:1.3; min-width:300px;">

```python
import numpy as np
from distforge import box, Y

b = box(size=(0.5, 2.5, 0.5))
scene = b.twist(3.0).rotate(Y, np.pi / 4)
scene.render()
```

</div>
<div style="flex:1;  text-align:center; padding-bottom: 20px"> <img src="./assets/transformations_shaping.png" width="100%"> </div>
</div>

### Materials
You can assign a unique color to any object or group of objects using the `.color()` method.

<div style="display:flex; align-items:center; gap:24px; flex-wrap:wrap;">
<div style="flex:1.3; min-width:300px;">

```python
from distforge import sphere, box

red_box = box(1.5).color(1.0, 0.2, 0.2)
blue_sphere = sphere(1.0).color(0.3, 0.5, 1.0)
scene = red_box - blue_sphere
scene.render()
```

</div>
<div style="flex:1;  text-align:center; padding-bottom: 20px"> <img src="./assets/materials.png" width="100%"> </div>
</div>

### Camera & Lighting
You can override the default interactive camera and lighting to set a static viewpoint or create specific lighting conditions by passing `Camera` and `Light` objects to the renderer.

<div style="display:flex; align-items:center; gap:24px; flex-wrap:wrap;">
<div style="flex:1.3; min-width:300px;">

```python
from distforge import box, sphere, Camera, Light

def main():
    scene = box(1.5) | sphere(1.2)    
    cam = Camera(position=(4, 3, 4), target=(0, 0, 0), zoom=1.5)    
    light = Light(position=(4, 5, 3), shadow_softness=16.0)

    return scene, cam, light

if __name__ == '__main__':
    scene, cam, light = main()
    scene.render(camera=cam, light=light)
```

</div>
<div style="flex:1;  text-align:center; padding-bottom: 20px"> <img src="./assets/camera_lighting.png" width="100%"> </div>
</div>

## Advanced Usage

### Interactive Parameters
Use `Param` objects to define interactive, real-time parameters for your model. A compatible UI-enabled renderer would show these as sliders. The default renderer will use their default values.

<div style="display:flex; align-items:center; gap:24px; flex-wrap:wrap;">
<div style="flex:1.3; min-width:300px;">

```python
from distforge import box, Param

p_size = Param("Box Size", 0.8, 0.2, 2.0)
scene = box(size=p_size)
scene.render()
```

</div>
<div style="flex:1;  text-align:center; padding-bottom: 20px"> <img src="./assets/interactive_parameters.png" width="100%"> </div>
</div>

### Custom GLSL with `Forge`
For complex or highly-performant shapes, you can write GLSL code directly. This object integrates perfectly with the rest of the API.

<div style="display:flex; align-items:center; gap:24px; flex-wrap:wrap;">
<div style="flex:1.3; min-width:300px;">

```python
from distforge import sphere, Forge

s = sphere(1.2)
custom_box = Forge("""
    vec3 q = abs(p) - vec3(0.8);
    return length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)), 0.0);
""")
scene = s - custom_box
scene.render()
```

</div>
<div style="flex:1;  text-align:center; padding-bottom: 20px"> <img src="./assets/custom_glsl.png" width="100%"> </div>
</div>

### Standalone GLSL Export
Generate a complete, self-contained GLSL fragment shader for your scene. This file can be used directly in applications like Godot, TouchDesigner, or Three.js.

```python
from distforge import box, sphere

scene = box(1.5) - sphere(1.2)
scene.export_shader("exported_shader.glsl")
```

## Installation

### 1. System Dependencies

The live viewer relies on `glfw`, which may require you to install its system-level libraries first. This is a common first step before installing the Python package.

**On Debian/Ubuntu:**
```bash
sudo apt-get install libglfw3-dev
```

**On macOS (with Homebrew):**
```bash
brew install glfw
```
**On Windows:**
`glfw` is generally bundled with the Python wheels, so no separate installation is typically needed.

### 2. Python Package

The library and its core dependencies can be installed using pip.

**Standard Installation:**
This will install the core library, including `numpy`, `moderngl`, and `glfw`, enabling all fundamental features like modeling, live preview with hot-reloading, and exporting to `.stl` and `.obj` formats.

```bash
pip install distforge
```

**Optional Features:**
For additional functionality, you can install "extras":

*   **`.glb` Export:** To enable saving models to the GLB format, a modern and efficient format for web and game engines.
    ```bash
    pip install "distforge[export]"
    ```

*   **Interactive UI:** To enable UI widgets like sliders for `Param` objects.
    ```bash
    pip install "distforge[ui]"
    ```

*   **Full Installation:** To install all optional features at once.
    ```bash
    pip install "distforge[full]"
    ```

## Acknowledgements

This project is inspired by the simplicity and elegant API of Michael Fogleman's [fogleman/sdf](https://github.com/fogleman/sdf) library. DistForge aims to build on that foundation by adding a real-time, interactive GLSL-powered renderer.
