# ninsent/plasticity-c4d-plugin architecture

> Architecture diagram of the GitHub repository ninsent/plasticity-c4d-plugin, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-08-12). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/ninsent/plasticity-c4d-plugin
- Repository: https://github.com/ninsent/plasticity-c4d-plugin

## Overview

#### Purpose and Plugin Entry
This is a Cinema 4D 2023+ Python plugin that bridges Plasticity CAD and C4D over WebSocket, supporting live inbound mesh sync and capability-gated outbound uploads. The plugin entry module registers the command/dialog and Auto-Refacet tag integration: `plasticity_c4d.pyp`. UI orchestration begins in `dialogs/main_dialog.py`.

#### Runtime and External Boundary
Cinema 4D is the host runtime and its scene API must run on its main thread. Plasticity is an external CAD process exposing a binary WebSocket server, normally `localhost:8980`. WebSocket transport is vendored, so deployment requires no package install: `libs/websockets/client.py`, `libs/websockets/connection.py`, and `libs/websockets/legacy/protocol.py`.

#### Connection, Protocol, and Feature Negotiation
`core/client.py` owns the background WebSocket client lifecycle: connect/disconnect, receive binary messages, and send commands. `core/protocol.py` defines the Plasticity-compatible little-endian, aligned binary message encoding/decoding and message layouts. Connection begins with `HANDSHAKE_1`; server-advertised capabilities determine whether features such as `PUT_SOME_1` uploads are enabled.

#### Threading Boundary and Event Dispatch
The client thread never modifies the C4D document. Parsed network events cross through a thread-safe queue/callback bridge in `core/threading_bridge.py`. The dialog timer drains that bridge at roughly 60 FPS and dispatches work on the C4D main thread, linking `dialogs/main_dialog.py` to the scene layer. This is the repository’s principal concurrency and API-safety boundary.

#### Scene Synchronization and Geometry Lifecycle
`core/handler.py` is the main-thread scene controller. It maps Plasticity file/group/solid data into the C4D root hierarchy: root null, protected Outbox, and inbound Inbox. Incoming geometry updates replace mesh data in place so existing user materials, animation, constraints, and non-managed tags survive. It also handles coordinate conversion (Z-up to Y-up), meter-to-centimeter scaling, reversed winding, managed normals, CAD face/edge selections, topology modes, and refresh/live-link updates.

#### Refacet and Persistent Per-Object Settings
Refaceting is coordinated by scene handling and the custom tag implementation in `core/refacet_tag.py`. The tag persists tessellation/topology parameters on individual C4D objects; subsequent refreshes or live updates can reapply them automatically. C4D resource definitions expose that tag and its Attribute Manager fields: `res/description/tplasticityrefacet.res`, `res/description/tplasticityrefacet.h`, and `res/strings_us/description/tplasticityrefacet.str`.

#### Bidirectional Content Boundary
Inbound Plasticity objects are managed beneath Inbox; they are eligible for synchronization. User-authored content belongs in Outbox and is protected from incoming replacement. On refresh, supported polygon/SDS and evaluated parametric-generator content can be resolved and serialized into `PUT_SOME_1` requests through `core/handler.py`, `core/client.py`, and `core/protocol.py`. Server handshake support is required before this upload path is used.

Main entry points: [`core/client.py`](https://github.com/ninsent/plasticity-c4d-plugin/blob/main/core/client.py) (WebSocket client), [`core/handler.py`](https://github.com/ninsent/plasticity-c4d-plugin/blob/main/core/handler.py) (Scene handler), [`core/refacet_tag.py`](https://github.com/ninsent/plasticity-c4d-plugin/blob/main/core/refacet_tag.py) (Auto-Refacet tag), [`core/threading_bridge.py`](https://github.com/ninsent/plasticity-c4d-plugin/blob/main/core/threading_bridge.py) (Threading bridge)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_host["Cinema 4D Host"]
  node_plugin["Plugin entry<br/>C4D plugin<br/>[plasticity_c4d.pyp]"]
  node_dialog["Main dialog<br/>C4D dialog<br/>[main_dialog.py]"]
  node_resources["Refacet resources<br/>C4D resources"]
  node_inbox["Inbox hierarchy<br/>managed scene root"]
  node_outbox["Outbox hierarchy<br/>protected scene root"]
end

subgraph group_core["Sync Core"]
  node_handler["Scene handler<br/>scene controller<br/>[handler.py]"]
  node_refacet["Auto-Refacet tag<br/>C4D tag<br/>[refacet_tag.py]"]
  node_bridge["Threading bridge<br/>event queue"]
  node_client["WebSocket client<br/>background client<br/>[client.py]"]
  node_protocol["Binary protocol<br/>codec<br/>[protocol.py]"]
  node_handshake{{"Capability handshake<br/>feature negotiation"}}
  node_geometry["Geometry lifecycle<br/>mesh update<br/>[handler.py]"]
  node_upload["PUT_SOME_1 upload<br/>outbound request<br/>[handler.py]"]
end

subgraph group_external["External Boundary"]
  node_transport["Vendored WebSocket<br/>transport library<br/>[client.py]"]
  node_plasticity(("Plasticity CAD<br/>external CAD server"))
end

node_plugin -->|"opens"| node_dialog
node_plugin -->|"registers"| node_refacet
node_resources -->|"defines UI"| node_refacet
node_dialog -->|"drains at ~60 FPS"| node_bridge
node_bridge -->|"main-thread events"| node_handler
node_client -->|"parsed network events"| node_bridge
node_client -->|"encodes and decodes"| node_protocol
node_client -->|"uses"| node_transport
node_transport -->|"binary WebSocket"| node_plasticity
node_client -->|"starts with HANDSHAKE_1"| node_handshake
node_plasticity -->|"advertises capabilities"| node_handshake
node_handler -->|"synchronizes inbound objects"| node_inbox
node_handler -->|"updates in place"| node_geometry
node_refacet -->|"reapplies saved settings"| node_geometry
node_handler -->|"refreshes upload candidates"| node_outbox
node_outbox -->|"supported content"| node_upload
node_upload -->|"PUT_SOME_1 encoding"| node_protocol
node_handshake -.->|"enables when supported"| node_upload
node_protocol -->|"outbound command"| node_client

click node_plugin "https://github.com/ninsent/plasticity-c4d-plugin/blob/main/plasticity_c4d.pyp"
click node_dialog "https://github.com/ninsent/plasticity-c4d-plugin/blob/main/dialogs/main_dialog.py"
click node_resources "https://github.com/ninsent/plasticity-c4d-plugin/blob/main/res/description/tplasticityrefacet.res"
click node_handler "https://github.com/ninsent/plasticity-c4d-plugin/blob/main/core/handler.py"
click node_refacet "https://github.com/ninsent/plasticity-c4d-plugin/blob/main/core/refacet_tag.py"
click node_bridge "https://github.com/ninsent/plasticity-c4d-plugin/blob/main/core/threading_bridge.py"
click node_client "https://github.com/ninsent/plasticity-c4d-plugin/blob/main/core/client.py"
click node_protocol "https://github.com/ninsent/plasticity-c4d-plugin/blob/main/core/protocol.py"
click node_geometry "https://github.com/ninsent/plasticity-c4d-plugin/blob/main/core/handler.py"
click node_transport "https://github.com/ninsent/plasticity-c4d-plugin/blob/main/libs/websockets/client.py"
click node_upload "https://github.com/ninsent/plasticity-c4d-plugin/blob/main/core/handler.py"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_plugin,node_dialog,node_resources,node_inbox,node_outbox toneBlue
class node_handler,node_refacet,node_bridge,node_client,node_protocol,node_handshake,node_geometry,node_upload toneAmber
class node_transport,node_plasticity toneMint
```

## Components

### Cinema 4D Host

Main-thread UI, scene API, and plugin resources.

- [Plugin entry](https://github.com/ninsent/plasticity-c4d-plugin/blob/main/plasticity_c4d.pyp): C4D plugin · Registers the command, dialog, and Auto-Refacet tag integration. · `plasticity_c4d.pyp`
- [Main dialog](https://github.com/ninsent/plasticity-c4d-plugin/blob/main/dialogs/main_dialog.py): C4D dialog · Drains network events on the C4D main thread. · `dialogs/main_dialog.py`
- [Refacet resources](https://github.com/ninsent/plasticity-c4d-plugin/blob/main/res/description/tplasticityrefacet.res): C4D resources · Defines the tag and Attribute Manager fields. · `res/description/tplasticityrefacet.res`
- **Inbox hierarchy**: managed scene root · Inbound Plasticity objects eligible for synchronization.
- **Outbox hierarchy**: protected scene root · User-authored content eligible for outbound upload.

### Sync Core

Connection, protocol, dispatch, and scene synchronization.

- [Scene handler](https://github.com/ninsent/plasticity-c4d-plugin/blob/main/core/handler.py): scene controller · Maps CAD data into C4D and preserves user-authored scene state. · `core/handler.py`
- [Auto-Refacet tag](https://github.com/ninsent/plasticity-c4d-plugin/blob/main/core/refacet_tag.py): C4D tag · Persists per-object tessellation and topology settings. · `core/refacet_tag.py`
- [Threading bridge](https://github.com/ninsent/plasticity-c4d-plugin/blob/main/core/threading_bridge.py): event queue · Thread-safe boundary between client and C4D main thread. · `core/threading_bridge.py`
- [WebSocket client](https://github.com/ninsent/plasticity-c4d-plugin/blob/main/core/client.py): background client · Owns connection lifecycle, binary receive, and command send. · `core/client.py`
- [Binary protocol](https://github.com/ninsent/plasticity-c4d-plugin/blob/main/core/protocol.py): codec · Encodes and decodes Plasticity-compatible binary messages. · `core/protocol.py`
- **Capability handshake**: feature negotiation · Server capabilities gate outbound uploads.
- [Geometry lifecycle](https://github.com/ninsent/plasticity-c4d-plugin/blob/main/core/handler.py): mesh update · Performs conversion, in-place replacement, selections, normals, and topology handling. · `core/handler.py`
- [PUT_SOME_1 upload](https://github.com/ninsent/plasticity-c4d-plugin/blob/main/core/handler.py): outbound request · Serializes supported polygon, SDS, and evaluated generator content. · `core/handler.py`

### External Boundary

Plasticity CAD and vendored WebSocket transport.

- [Vendored WebSocket](https://github.com/ninsent/plasticity-c4d-plugin/blob/main/libs/websockets/client.py): transport library · Bundled client transport requiring no package installation. · `libs/websockets/client.py`
- **Plasticity CAD**: external CAD server · Binary WebSocket server, normally localhost:8980.

## Connections

- Plugin entry → Main dialog: opens
- Plugin entry → Auto-Refacet tag: registers
- Refacet resources → Auto-Refacet tag: defines UI
- Main dialog → Threading bridge: drains at ~60 FPS
- Threading bridge → Scene handler: main-thread events
- WebSocket client → Threading bridge: parsed network events
- WebSocket client → Binary protocol: encodes and decodes
- WebSocket client → Vendored WebSocket: uses
- Vendored WebSocket → Plasticity CAD: binary WebSocket
- WebSocket client → Capability handshake: starts with HANDSHAKE_1
- Plasticity CAD → Capability handshake: advertises capabilities
- Scene handler → Inbox hierarchy: synchronizes inbound objects
- Scene handler → Geometry lifecycle: updates in place
- Auto-Refacet tag → Geometry lifecycle: reapplies saved settings
- Scene handler → Outbox hierarchy: refreshes upload candidates
- Outbox hierarchy → PUT_SOME_1 upload: supported content
- PUT_SOME_1 upload → Binary protocol: PUT_SOME_1 encoding
- Capability handshake → PUT_SOME_1 upload: enables when supported
- Binary protocol → WebSocket client: outbound command
