mirror of
https://github.com/GraphiteEditor/Graphite.git
synced 2026-10-10 17:50:57 +08:00
Make the data model use Item and List types universally, with nodes authored as rank-polymorphic kernels (#4335)
* Add rank polymorphism node audit classifying all 271 nodes
* Implement StaticType for Item<T>
* Generate Item and mapped List wire variants for nodes declaring an Item<T> primary input
* Migrate nine nodes to Item element-wise kernels, dissolving the blending trait boilerplate
* Document the Item kernel implementation and staging plan
* Route Item<Vector> through TaggedValue::TypeDefault
* Add executor integration tests covering the Item and List wire variants
* Collapse element-wise Item/List wire pairs to the List form for conversion insertion
* Migrate sixteen vector modifier nodes to Item element-wise kernels
* Migrate Sample Image, Extend Image to Bounds, and Dehaze to Item element-wise kernels
* Fix bevel_with_transform test to actually exercise the transform attribute
* Implement From<T> for Item<T>
* Register PromoteNode rank adapters wrapping bare values into Item wires
* Insert PromoteNode adapters for Item/List wire pair fields in the preprocessor
* Define a real promote node backing the PromoteNode registry identifiers
* Zip ranked Item connectors by frame slot in the mapped element-wise variant
* Register ItemToListNode singleton raise adapters
* Resolve Item wires against List connectors by inserting promotion adapters at construction
* Rank the Offset Points distance connector and prove mixed-rank resolution end-to-end
* Implement Clampable for Item and List wires with per-variant clamp bounds
* Rank the Round Corners radius connector, exercising hard bounds on a ranked wire
* Implement ApplyTransform for Item
* Add Item wire implementations to the Transform node, keeping rank-0 chains rank 0
* Detect element-wise nodes by lazy primary connectors declaring Output = Item
* Convert Transform to an Item kernel with ranked parameters, delivering the broadcast milestone
* Rename Apply Transform to Bake Transform, baking item transforms on Vector, DAffine2, and DVec2
* Promote bare wires onto Item connectors at resolution via WrapItemNode adapters
* Rank the numeric, vector, and boolean parameters across the migrated element-wise nodes
* Rank the enum, integer, and seed parameters, registering their rank adapters via a consolidated macro
* Amend the audit with the DashPattern value type resolution
* Migrate the string family to Item element-wise kernels
* Unwrap Item wires into bare legacy connectors at resolution via UnwrapItemNode adapters
* Shadow owned node parameters in bodies instead of mut in signatures
* Migrate the math family and string measure nodes to Item element-wise kernels
* Convert the comparison and clamp nodes to Item kernels, dropping unreachable &str rows
* Flat-map expander kernels returning List under the mapped variant's frame
* Migrate the expander nodes to Item kernels flat-mapping under the frame
* Remove the unused peel_list helper
* Rank the raster adjustment and blending kernels, recontextualizing shader nodes onto an Item stand-in
Migrate the 16 adjustment nodes, Mix, Color Overlay, and Gradient Map from whole-List kernels to rank-0 Item kernels, letting the macro derive the List-mapped (zip) variants. Move the Adjust and Blend per-element seams off List onto the element types (add the Raster<CPU> impls, drop the now-dead List impls).
Shader nodes keep their bodies verbatim: PerPixelAdjust re-emits the identical kernel against a transparent no_std Item stand-in, so every Item<T> connector and .element() call resolves to a zero-cost identity on the GPU while the uniform buffer stays bare repr(C). The macro peels Item off ranked uniform params, wraps the fetched texel and uniforms at the entry point, and unwraps the result. This drops the shader_node/Item incompatibility guard. Register rank adapters for the adjustment enums.
* Update the rank polymorphism roadmap for the landed shader-node and adjustments chunk
* Rename the GPU Item stand-in to ShaderItem, aliased as Item at its shader-node import sites
* Flip the vector shape generators to emit rank-0 Item<Vector>
The shape generators (Rectangle, Circle, Ellipse, Arc, Spiral, Polygon, Star, Arrow, Line, Grid, QR Code) each produced exactly one shape wrapped in a singleton List<Vector>. Emit Item<Vector> directly so they connect to the rank-0 content connector of the migrated Transform node. Downstream List consumers receive the value through the existing Item to List promotion.
Relax the element-wise validation so a `()` (generator) primary may return Item<T> without being element-wise. Adapt the Repeat on Points test, which still takes a List content connector, by raising the generator's Item output through a singleton wrapper node.
* Parse ranked Item<T> parameter defaults against the bare element type
A ranked `Item<T>` parameter's default value is a bare, unranked `T` (promoted to the wire at resolution), but the preprocessor was handed the wrapped `Item<T>` type and could not parse the literal, flooding the console with warnings and dropping the defaults. Key the field's default_type metadata off the peeled element type for concrete ranked parameters, leaving generic `Item<T>` primaries and skip_impl nodes untouched.
* Parse an element-wise primary's scalar default against the bare element type
An element-wise node's primary reports its default_type as the List wire form so an unconnected primary defaults to an empty list. But when the primary carries a scalar `#[default]` (such as Root's radicand), that literal must parse as a bare element, not a List. Key the primary's default_type off the bare element type when it has a Default value source, keeping the List form otherwise.
* Add the DashPattern value type for stroke dash sequences
Introduce a rank-0 DashPattern value type (a Vec<f64> of alternating dash and gap lengths) so a stroke's dash pattern is a single frameable value rather than a rank-1 List<f64>. Register it as an auto-generated TaggedValue variant, parse its default from a comma or space separated string, and register its rank adapters. Not yet wired into the Stroke node.
* Rank the Fill and Stroke nodes element-wise and give Stroke a DashPattern connector
Migrate Fill and Stroke to element-wise Item<V> primaries (over Vector and Graphic element types) via a new element-level VectorItemMut trait, so styling one shape yields one shape and rank is preserved instead of promoting the input to a singleton List and emitting a List. The macro derives the List-mapped variant for genuine collections.
Wire the Stroke dash sequence to the new rank-0 DashPattern value type, collapsing the old content x paint x dash cartesian and dropping the IntoF64Vec trait. Update the stroke properties dash widget, the drawing tool, and graph-operation plumbing to read and write DashPattern, and migrate legacy F64Array, F64, and String dash inputs on document open.
Assign Colors stays a whole-collection node: each element's gradient position depends on its index among all siblings, which the element frame does not expose, so it keeps its List primary and the VectorListIterMut trait.
* Register rank adapters for the ranked Stroke enum parameters
The element-wise Stroke node ranks its align, cap, and paint order parameters as Item<StrokeAlign>, Item<StrokeCap>, and Item<PaintOrder>, but those enums lacked promotion adapters, so a bare default enum value could not be promoted to its Item wire and no Stroke variant resolved ("No construct found for node"). Register their rank adapters alongside StrokeJoin.
* Display Item wires in the Data panel without a List's ID column
Add a TableItemLayout impl for Item<T> and recognize Item wire types when introspecting graph data. An Item holds a single element, so it renders as a one-row table of the element plus its attributes with no leading index column, and it labels as its element type T rather than a List's T[]. Add ItemAttributeValues::get_any for the attribute widget dispatch.
* Register MonitorNode for Item wire types so the Data panel introspects them directly
Graph introspection wraps the inspected output in a generic MonitorNode typed to the wire. Without Item<T> monitor registrations, an Item<Vector> output could only be monitored after an Item to List promotion, so the Data panel captured and displayed a List<Vector> despite the connector being Item<Vector>. Register monitors for the Item types the element-wise nodes emit, and add the matching Data panel downcast entries.
* Color and double Item/List wires and cleave layer-stack connectors in the node graph
* Route wire color and rank through hidden nodes and refresh them on type changes
* Rework the DashPattern connector conversions with element-wise promotion and an explicit reducer node
* Rank the remaining value, context, aggregation, and transform nodes onto Item<T> wires
* Back DashPattern with a List<f64> so the Data panel can introspect its lengths
* Carry a single Item<T> through varargs so the Read context nodes emit Item<T> not List<T>
* Relax rank validation for aggregation shapes, add element adapters, and match variants by fewest promotions
* Rank the remaining bare and unnecessarily-List connectors across the node catalog
* Add Graphic::None and the FillChoice paint value, making colors and gradients plain values
* Rename GradientStops to Gradient and the legacy Gradient/Fill structs to LegacyGradient/LegacyFill
* Restore generator frame-from-params ranking to the roadmap as a planned stage
* Rename the ranked-field adapter identifier from PromoteNode to FieldAdapterNode to reflect its full contract
* Unload only the wires whose displayed style changed when types update
* Peel wire rank in the editor's semantic type checks so rank-0 layers are recognized
* Restore the whole-List Transform variant so rank-1 content wires resolve again
* Register the Item wire forms for the Memoize and Context Modification infrastructure nodes
* Give every ranked connector a field adapter and add numeric cast variants for legacy wires
* Key a ranked param's type default off its Item wire form when no literal default exists
* Inherit the layer's content value when splicing a node into an empty chain
* Migrate stale List-form TypeDefault inputs to the definition's current default
* Generate the mapped wire variant only when the element-wise node has a frame source
* Let a bare wire feed a List connector via a wrap-raise adapter, costed as two rank steps
* Add a zip companion to the whole-List Transform so ranked List parameters pair per slot
* Add the Sum, Average, Minimum, Maximum, Any, and All list reducers
* Convert the measure family to element-wise Item kernels per the audit classification
* Prefer the bare element value over the Item type default so ranked params keep their widgets
* Rename GradientStopsUI to GradientUI
* Split Fill's optional transform into a _has_transform bool and a ranked _transform matrix
* Rename the migration-only OptionalDAffine2 TaggedValue to LegacyOptionalDAffine2
* Flow byte buffers as Item<Resource> instead of List<u8> across the byte nodes
* Macro-generate the list-content wire variant, retiring the hand-written Transform-zip, Area, and Centroid companions
* Let ()-primary generators take ranked params and frame over them via the mapped variant, ranking Circle's radius
* Rank the vector shape generators' params to Item, adding a rank-aware input grab to the introspection harness
* Rank the value, color, and text generator params to Item
* Rank the raster, web-request, and context-reader generator params to Item
* Fix the repeat and brush test wirings left behind by the param-ranking sweeps
* Delete the vestigial Some, Unwrap Option, and Size Of debug nodes
* Delete the Attach Attribute node, folding its role into Write Attribute
* Add the Filter and Sort list companion nodes
* Guard the removed-definition migration swap target with a test
* Add the Box Corners value type in place of the rectangle corner radius list
* Split Text to Vector's per-glyph mode into a Text to Vector Glyphs node
* Rank the Combine Channels node's channel connectors to Item
* Make Map Points an element-wise node
* Delete the deprecated Upload Texture node
* Update the implementation roadmap to reflect the landed stages
* Let monitor introspection read rank-0 wires, locking in the layer coercion promotion path
* Prefer the rank-0 default when disconnecting a rank-capable input
* Make Path Modify an element-wise node
* Wrap node paths in a NodeIdPath newtype so they flow as a single Item
* Give Item<Raster<CPU>> a default so an unconnected Brush background resolves
* Stop the Brush node from setting layer attributes its paint operation doesn't produce
* Present-gate Flatten Path's adopted layer path like its fill and stroke
* Gate carried layer attributes on static column presence, not runtime values
* Give the remaining graphic Item<T> types a default so unconnected primaries resolve
* Dispatch a ranked param's Properties widget from its rank-0 element type
* Make Extract Transform an element-wise node, restoring the Origins to Polyline body
* Rename Flatten Path to Combine Paths
* Stamp Legacy Layer Extend's adopted layer path as a readable NodeIdPath
* Drop the dead List<u8> and List<NodeId> wire rows
* Rank Flatten Graphic's Fully Flatten toggle to Item
* Update the implementation roadmap with the endgame scope
* Make Combine Paths a reducer that collapses the whole frame into one path
* Stop type-converter nodes from carrying the source's unrelated attributes
* Format the Origins to Polyline regression test
* Wrap the Brush node's trace in a BrushTrace newtype so it flows as one value
* Make Switch a framed element-wise select, bundling whole collections
* Widen and align element-type coverage across the list and graphic nodes
* Register the compiler's cache chain pair for every ranked enum and newtype wire
* Fix wire colors for Passthrough outputs, bundled lists, and bools, and widen list wires
* Represent List wire types structurally with Type::List, replacing name-parsed rank promotion
* Treat scope and data fields as environment, rank scope wires as Item, and feed the render boundary through a context vararg
* Delete the vestigial Clone debug node
* Reinstate Upload Texture as an element-wise node and fix the GPU variants' scope executor and rank adapters
* Rename Combine Paths back to Flatten Path, deferring that rename to its own PR
* Deduplicate the promotion adapter registrations into the field adapter macro
* Rank Write Attribute's value connector to Item<AttributeValueDyn>, retiring the UnwrapItem bridge
* Vertical wire styling
* Store the editor layer path attribute as a bare NodeIdPath, not an Item<NodeIdPath>
* Rank Context Modification's features connector to Item<ContextFeatures>, dropping the dead memoize row
* Rank Path Modify's modification parameter to Item<Box<VectorModification>>
* Rename the field adapter node family to input adapter
* Drop the dead bare scalar rows from Context Modification's implementations list
* Move the dynamic executor's test module into its own file
* Drop the registry's unreachable bare rows for Memoize, the cache chain, and ConvertNode
* Materialize stored TaggedValues as ranked Item wires at the source
* Remove the bare-wire promotion and adapter machinery made dead by ranked value materialization
* Plant the input adapter for List-only inputs, composing position conversion from standard rows
* Consolidate Into/Convert conversions into the input adapter umbrella and rename the rank adapter identifiers
* Fix grouped layers gaining a phantom None stack element from the FillChoice default hijacking every List<Graphic> disconnect
* Enforce ranked node inputs in the macro, rejecting bare wire declarations
* Remove the unit Context => () machinery rows, leaving () purely as the no-primary sentinel
* Add a --signatures rank-audit mode to node-docs for the ranked-wire migration
* Remove the node-docs --signatures rank-audit mode now that ranked wires are enforced
* Migrate legacy no-color values on the Black & White, Color Overlay, and Empty Image color inputs
* Rewrite the element-wise accessor wire type at the primary input, not raw index 0
* Register the cache chain for Resource wires, replacing the lone hand-written Monitor row
* Gate the remaining Raster<GPU> registry rows behind the gpu feature
* Let List<DVec2> wires erase to ListDyn for the attribute reader and element counter
* Rename Extract Element to Item at Index, Count Elements to List Length, and Omit Element to Remove at Index
* Store paint picks as plain color/gradient values, removing the FillChoice value type
* Code review restructuring
* Sort by the consumed sort_key attribute or natural element order, adding the Sort Key node
* Remove the new list-combinator and reducer nodes to defer them to a follow-up PR
* Parse Fill and Stroke color defaults through the paint wire's Graphic element
* Emit ranked implementation-row default types structurally so their element TypeIds survive to default-literal parsing
* Exempt the deliberate no-paint choice from the stale List-form TypeDefault migration
* Migrate the legacy 4-input Fill directly to the split has-transform shape
* Upgrade the demo artwork
* Fix the valid AI review findings: Item eq/hash contract, table-era no-paint migration, quantize List rows, and other smaller issues
* Remove the rank polymorphism working documents
* Hash Item attribute values directly instead of debug-formatting them, speeding up cached evaluation
* Replace the data panel's dead bare-wire downcast arms with full coverage of the ranked monitor row types
* Derive PartialEq for Item now that attributes participate in equality
* Extend the data panel's attribute dispatchers with the newly supported scalar and choice enum types
* Add List monitor rows for the framed numeric conversion outputs so inspecting them resolves, with matching data panel arms
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
+++
|
||||
title = "Volunteer"
|
||||
|
||||
[extra]
|
||||
css = ["/page/volunteer.css", "/component/feature-box.css"]
|
||||
+++
|
||||
|
||||
<section>
|
||||
<div class="block">
|
||||
|
||||
# Get involved
|
||||
|
||||
**Graphite is 100% built by volunteers.** Get involved in the effort to bring great, free creative software to the world.
|
||||
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
|
||||
## Code contributions
|
||||
|
||||
<div class="feature-box-narrow">
|
||||
|
||||
<a href="/volunteer/guide">
|
||||
<img src="https://static.graphite.art/content/volunteer/code-contributions.avif" class="feature-box-full-image" style="aspect-ratio: 3/1 auto; background: var(--color-seaside)" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.jpg')" alt="Flavor graphic depicting a library of knowledge in a digital realm" />
|
||||
</a>
|
||||
|
||||
Get started by reading the contributor guide:
|
||||
|
||||
<a href="/volunteer/guide" class="button arrow">Contributor guide</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="diptych code-contributions">
|
||||
|
||||
<div class="block feature-box-narrow">
|
||||
|
||||
<h1 class="feature-box-header">Editor team</h1>
|
||||
|
||||
The Graphite editor is built much like a game engine, split across user interface application tooling and a renderer with nodes implementing an assortment of graphics algorithms.
|
||||
|
||||
</div>
|
||||
<div class="block feature-box-narrow">
|
||||
|
||||
<h1 class="feature-box-header">Compiler team</h1>
|
||||
|
||||
[Graphene](/volunteer/guide/graphene) is a programming language, interpreter, and runtime environment built upon Rust which enables Graphite artwork to compile to executable programs for fast rendering.
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
|
||||
## Creative contributions
|
||||
|
||||
<div class="feature-box-narrow">
|
||||
|
||||
<img src="https://static.graphite.art/content/volunteer/creative-contributions.avif" class="feature-box-full-image" style="aspect-ratio: 3/1 auto; background: var(--color-lemon)" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.jpg')" alt="Flavor graphic depicting a fountain pen, ink pots, and a book" />
|
||||
</a>
|
||||
|
||||
Assign yourself the *"🙌 Interested in helping with art or marketing"* role in the *#welcome* Discord channel. Then mention your experience and how you'd like to help in the *#introductions* channel.
|
||||
|
||||
<a href="https://discord.graphite.art" class="button arrow">Volunteer on Discord</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="diptych creative-contributions">
|
||||
|
||||
<div class="block feature-box-narrow">
|
||||
|
||||
<h1 class="feature-box-header">Art team</h1>
|
||||
|
||||
Use your artistic talents to conceptualize and produce high-quality open art projects published by the Graphite project to stress-test and showcase the editor's capabilities.
|
||||
|
||||
</div>
|
||||
<div class="block feature-box-narrow">
|
||||
|
||||
<h1 class="feature-box-header">Marketing team</h1>
|
||||
|
||||
Help write, edit, and design content for this website, social media, newsletters, blog posts, user manual pages, videos, fundraising campaigns, press releases, and industry outreach.
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<section>
|
||||
|
||||
## User contributions
|
||||
|
||||
<div class="feature-box-narrow">
|
||||
|
||||
<img src="https://static.graphite.art/content/volunteer/user-contributions.avif" class="feature-box-full-image" style="aspect-ratio: 3/1 auto; background: var(--color-lilac)" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.jpg')" alt="Flavor graphic depicting a magnifying glass on the search for a software bug" />
|
||||
|
||||
Assign yourself the *"🐒 Volunteer to get pinged regularly for QA testing"* or *"🤖 Interested in contributing code"* roles in the *#welcome* Discord channel. In the latter case, drop by the *#development* channel to get advice writing your first node.
|
||||
|
||||
<a href="https://discord.graphite.art" class="button arrow">Volunteer on Discord</a>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="diptych user-contributions">
|
||||
|
||||
<div class="block feature-box-narrow">
|
||||
|
||||
<h1 class="feature-box-header">QA team</h1>
|
||||
|
||||
Get familiar with the ins-and-outs of the editor and respond actively to developer requests on a recurring basis to test out new features and find bugs and breakages.
|
||||
|
||||
</div>
|
||||
<div class="block feature-box-narrow">
|
||||
|
||||
<h1 class="feature-box-header">Nodes team</h1>
|
||||
|
||||
Explore and push the limits of the node graph with complex procedural designs. Report your findings about limitations, opportunities, and use cases to help in designing new nodes.
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</section>
|
||||
@@ -0,0 +1,29 @@
|
||||
+++
|
||||
title = "Contributor guide"
|
||||
template = "book.html"
|
||||
page_template = "book.html"
|
||||
aliases = ["/contribute"]
|
||||
|
||||
[extra]
|
||||
book = true
|
||||
+++
|
||||
|
||||
Welcome, potential contributor! We're excited to have you join the Graphite project. It is our goal to make the process as smooth as possible. This guide will serve as your library of knowledge to help you get started contributing to the project. If you find any information missing or unclear, please let us know through Discord or submit a pull request to help document the process for future contributors.
|
||||
|
||||
## Required experience level
|
||||
|
||||
Our aim is to make the process as accessible as possible for first-time Graphite contributors with solid computer science foundations to get started, even without prior open source experience. Many contributors have remarked that they found our process notably more welcoming and straightforward than open source projects they've previously tried contributing to.
|
||||
|
||||
However, please bear in mind that developing Graphite is **not for beginner programmers**. It is a complex, cutting-edge software engineering endeavor that requires at least a strong grasp of programming principles (although not necessarily Rust; you can learn that as you go).
|
||||
|
||||
If you are at least a junior-level developer or a self-motivated student who has built a number of self-directed, non-trivial personal projects (especially if they involve graphics), then you should be well-prepared to contribute to Graphite. If you are only a couple years into your programming journey, be warned that you will find Graphite frustratingly above your current skill level and we will be unable to support you adequately. The Graphite project is **not a learning platform or a classroom for inexperienced developers**, but with the requisite skills, it is a great way to develop real-world engineering experience and get involved in something meaningful.
|
||||
|
||||
Do not make the mistake of assuming AI tools and agents can be a substitute for your own skills and experience. If you use AI tools in your workflow, read our [AI contribution policy](./starting-a-task/ai-contribution-policy).
|
||||
|
||||
## Discord communication
|
||||
|
||||
The next page will cover how to compile the Graphite source code. But first, make sure you've joined our [Discord server](https://discord.graphite.art) and assigned yourself the *"🤖 Interested in contributing code"* role from the `#🙂welcome` channel. (And after your first PR is accepted and merged, you should post a request in `#📄development` to be assigned the *"Code Contributor"* role.)
|
||||
|
||||
This is semi-mandatory, particularly if you intend to become a regular contributor on the team, because it is how our team communicates and is just as central to our process as GitHub.
|
||||
|
||||
Done that? Alright, proceed to the next page!
|
||||
@@ -0,0 +1,61 @@
|
||||
+++
|
||||
title = "Codebase overview"
|
||||
template = "book.html"
|
||||
page_template = "book.html"
|
||||
|
||||
[extra]
|
||||
order = 2 # Chapter number
|
||||
js = ["/js/component/youtube-embed.js", "/js/page/contributor-guide/crate-hierarchy.js"]
|
||||
css = ["/component/youtube-embed.css", "/page/contributor-guide/crate-hierarchy.css"]
|
||||
+++
|
||||
|
||||
The best introduction for getting up-to-speed with Graphite contribution comes from watching this webcast recording. Before asking questions in Discord, please watch the full video because it gives a comprehensive overview of most things you will need to know.
|
||||
|
||||
{{ youtube_embed(id="vUzIeg8frh4", title="Workshop: Intro to Coding for Graphite") }}
|
||||
|
||||
## Codebase structure
|
||||
|
||||
Graphite is built from several main software components. New developers may choose to specialize in one or more area without having to attain a working knowledge of the full codebase.
|
||||
|
||||
### Frontend
|
||||
|
||||
*Location: [`/frontend/src`](https://github.com/GraphiteEditor/Graphite/tree/master/frontend/src)*
|
||||
|
||||
The frontend is the GUI for Graphite which users see and interact with. It is built using web technologies with TypeScript and Svelte (HTML and SCSS). The frontend's philosophy is to be as lightweight and minimal as possible. It acts as the entry point for user input and then quickly hands off its work to the WebAssembly editor backend via its [Wasm wrapper API](https://github.com/GraphiteEditor/Graphite/tree/master/frontend/wrapper). That API is written in Rust but has TypeScript bindings generated by the [wasm-bindgen](https://github.com/rustwasm/wasm-bindgen) tooling that is part of the Vite-based build toolchain. The frontend is built of many [components](https://github.com/GraphiteEditor/Graphite/tree/master/frontend/src/components) that recursively form the window, panels, and widgets that make up the user interface.
|
||||
|
||||
### Editor
|
||||
|
||||
*Location: [`/editor`](https://github.com/GraphiteEditor/Graphite/tree/master/editor)*
|
||||
|
||||
[The editor](./editor-structure) is the core of the Graphite application, and it's where all the business logic occurs for the tooling and user interaction. It is written in Rust and compiled to WebAssembly. At its heart is the message system. It is responsible for communicating with Graphene as well as handling the actual logic, state, tooling, and responsibilities of the interactive application.
|
||||
|
||||
### Graphene
|
||||
|
||||
*Location: [`/node-graph`](https://github.com/GraphiteEditor/Graphite/tree/master/node-graph)*
|
||||
|
||||
[Graphene](../graphene/) is the node graph engine which manages and renders the documents. It is itself a programming language, where Graphene programs are compiled while being edited live by the user, and where executing the program renders the document.
|
||||
|
||||
## Crate dependency graph
|
||||
|
||||
```sh
|
||||
# Access this quickly in the future:
|
||||
cargo run explore deps
|
||||
```
|
||||
|
||||
This diagram shows the structure of the crates that comprise the Graphite codebase and how they depend on each other. Every Arrow points from a crate to another which it depends on.
|
||||
|
||||
<div class="crate-hierarchy">
|
||||
|
||||
<!-- replacements::crate_hierarchy() -->
|
||||
|
||||
</div>
|
||||
|
||||
## Frontend/backend communication
|
||||
|
||||
Frontend-to-backend communication is achieved through a thin Rust translation layer in [`/frontend/wrapper/src/editor_wrapper.rs`](https://github.com/GraphiteEditor/Graphite/tree/master/frontend/wrapper/src/editor_wrapper.rs) which wraps the editor backend's Rust-based message system API and provides the TypeScript-compatible API of callable functions. These wrapper functions are compiled by [wasm-bindgen](https://github.com/rustwasm/wasm-bindgen) into autogenerated TS functions that serve as an entry point from TS into the Wasm binary.
|
||||
|
||||
Backend-to-frontend communication happens by sending a queue of messages to the frontend message dispatcher. After the TS has called any wrapper API function to get into backend code execution, the editor's business logic runs and queues up each [`FrontendMessage`](https://github.com/GraphiteEditor/Graphite/tree/master/editor/src/messages/frontend/frontend_message.rs) which get mapped from Rust to JavaScript data structures in [`/frontend/src/messages.ts`](https://github.com/GraphiteEditor/Graphite/tree/master/frontend/src/messages.ts). Various TS code subscribes to these messages by calling:
|
||||
|
||||
```rs
|
||||
subscribeFrontendMessage(NameOfMessage, (messageData) => { /* callback code */ });
|
||||
```
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
+++
|
||||
title = "Debugging tips"
|
||||
|
||||
[extra]
|
||||
order = 2 # Page number after chapter intro
|
||||
css = ["/page/contributor-guide/bisect-tool.css"]
|
||||
js = ["/js/page/contributor-guide/bisect-tool.js"]
|
||||
+++
|
||||
|
||||
The Wasm-based editor has some unique limitations about how you are able to debug it. This page offers tips and best practices to get the most out of your problem-solving efforts.
|
||||
|
||||
## Comparing with deployed builds
|
||||
|
||||
When tracking down a bug, first check if the issue you are noticing also exists in `master` or just in your branch. Open up [dev.graphite.art](https://dev.graphite.art) which always deploys the lastest commit, as opposed to [editor.graphite.art](https://editor.graphite.art) which deploys the latest stable release. Build links for any commit may be found by clicking the "comment" icon on the right side of any commit in the GitHub repo [commits list](https://github.com/GraphiteEditor/Graphite/commits/master/).
|
||||
|
||||
Use *Help* > *About Graphite…* in the editor to view any build's Git commit hash.
|
||||
|
||||
Beware of a potential pitfall: all deploys and build links are built with release optimizations enabled. This means some bugs (like crashes from bounds checks or debug assertions) may exist in `master` and would appear if run locally, but not in the deployed version.
|
||||
|
||||
## Build bisect tool
|
||||
|
||||
```sh
|
||||
# Access this quickly in the future:
|
||||
cargo run explore bisect
|
||||
```
|
||||
|
||||
This interactive tool helps you binary search through recent commits, test the build links of each, and pinpoint which change introduced a regression or added a feature.
|
||||
|
||||
<div class="bisect-tool">
|
||||
|
||||
<div class="phase active" data-phase="setup">
|
||||
<div class="setup-section">
|
||||
<div class="section-label">
|
||||
<span><strong>What are you looking for?</strong></span>
|
||||
</div>
|
||||
<label>
|
||||
<input type="radio" name="bisect-mode" value="regression" checked />
|
||||
<span>Find when a regression or bug started</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" name="bisect-mode" value="feature" />
|
||||
<span>Find when a feature was added or fixed</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="setup-section">
|
||||
<div class="section-label">
|
||||
<span><strong>When do you estimate this changed?</strong></span>
|
||||
</div>
|
||||
<label>
|
||||
<input type="radio" name="start-method" value="date" checked />
|
||||
<span>Date</span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" name="start-method" value="hash" />
|
||||
<span>Commit</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="commit-inputs">
|
||||
<div class="start-input" data-input="date">
|
||||
<input type="date" data-commit-date />
|
||||
</div>
|
||||
<div class="start-input hidden" data-input="hash">
|
||||
<input data-commit-hash placeholder="Commit hash" pattern="[0-9a-fA-F]{7,40}" />
|
||||
</div>
|
||||
<span class="button arrow" data-start-button>Begin bisect</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="phase" data-phase="bisect">
|
||||
<div class="block feature-box-narrow">
|
||||
<div class="step-header">
|
||||
<span class="step-label" data-step-label><strong>Bisect step 1</strong></span>
|
||||
<span class="go-back hidden" data-go-back-button>(<a>go back</a>)</span>
|
||||
</div>
|
||||
<div class="progress-info" data-progress-info></div>
|
||||
<div class="commit-info" data-commit-info></div>
|
||||
<span class="button arrow" data-test-build-button>Test this build</span>
|
||||
<span class="findings">After testing, what have you found?</span>
|
||||
<div class="bisect-actions">
|
||||
<span class="button" data-issue-present-button></span>
|
||||
<span class="button" data-issue-absent-button></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="error-message" data-message-box></div>
|
||||
|
||||
</div>
|
||||
|
||||
## Printing to the console
|
||||
|
||||
Use the browser console (<kbd>F12</kbd>) to check for warnings and errors. In Rust, use `log::debug!("The number is {some_number}");` to print to the browser console. These statements should be for temporary debugging. Remove them before your code is reviewed. Print-based debugging is necessary because breakpoints are not supported in WebAssembly.
|
||||
|
||||
Additional print statements are available that *should* be committed:
|
||||
|
||||
- `log::error!()` is for descriptive user-facing error messages arising from a bug
|
||||
- `log::warn!()` is for non-critical problems that likely indicate a bug somewhere
|
||||
- `log::trace!()` is for verbose logs of ordinary internal activity, hidden by default but viewable by activating *Help* > *Debug: Print Trace Logs*
|
||||
|
||||
## Message system logs
|
||||
|
||||
To also view logs of the messages dispatched by the message system, activate *Help* > *Debug: Print Messages* > *Only Names*. Or use *Full Contents* for a more verbose view containing the actual data being passed. This is an invaluable window into the activity of the message flow and works well together with `log::debug!()` printouts for tracking down message-related defects.
|
||||
|
||||
## Node/layer and document IDs
|
||||
|
||||
In debug mode, hover over a layer's name in the Layers panel, or a layer/node in the node graph, to view a tooltip with its ID. Likewise, document IDs may be read from their tab tooltips.
|
||||
|
||||
## Performance profiling
|
||||
|
||||
Be aware that having your browser's developer tools open will significantly impact performance in both debug and release builds, so it's best to close that when not in use.
|
||||
|
||||
The *Performance* tab of the browser developer tools lets you record and analyze performance profiles, and this is a useful way to track down bottlenecks. The Firefox profiler has some additional features missing from the Chromium debugger, so if you are digging deep into a performance issue, it can be worth giving Firefox a try for that purpose. Be sure to use debug builds while profiling, otherwise inlined functions and other optimizations may produce a misleading view of where time is being spent. The live deployed web app (production and dev) and build links hosted by our CI infrastructure are all built with release optimizations.
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
+++
|
||||
title = "Editor structure"
|
||||
|
||||
[extra]
|
||||
order = 1 # Page number after chapter intro
|
||||
css = ["/page/contributor-guide/editor-structure.css"]
|
||||
js = ["/js/page/contributor-guide/editor-structure.js"]
|
||||
+++
|
||||
|
||||
The Graphite editor is the application users interact with to create documents. Its code is a single Rust crate that lives below the frontend (web code) and above [Graphene](../../graphene) (the node-based graphics engine). The main business logic of all visual editing is handled by the editor backend. When running in the browser, it is compiled to WebAssembly and passes messages to the frontend.
|
||||
|
||||
## Message system
|
||||
|
||||
The Graphite editor backend is organized into a hierarchy of subsystems which talk to one another through message passing. Messages are pushed to the front or back of a queue and each one is processed sequentially by the editor's dispatcher.
|
||||
|
||||
The dispatcher lives at the root of the editor hierarchy and acts as the owner of all its top-level message handlers. This satisfies Rust's restrictions on mutable borrows because only the dispatcher may mutate its message handlers, one at a time, while each message is processed.
|
||||
|
||||
## Editor outline
|
||||
|
||||
```sh
|
||||
# Access this quickly in the future:
|
||||
cargo run explore editor
|
||||
```
|
||||
|
||||
Click to explore the outline of the editor subsystem hierarchy which forms the structure of the editor's subsystems, state, and interactions. Also available as a searchable <a href="/volunteer/guide/codebase-overview/hierarchical-message-system-tree.txt">plain text file</a>.
|
||||
|
||||
<div class="structure-outline">
|
||||
<!-- replacements::hierarchical_message_system_tree() -->
|
||||
</div>
|
||||
|
||||
### Parts of the hierarchy
|
||||
|
||||
<span class="subsystem">Subsystem components</span>
|
||||
|
||||
- A <span class="subsystem">*Message</span> enum is the component of an editor subsystem that defines its message interfaces as enum variants. Messages are used for passing a request from anywhere in the application, optionally with some included data, to have a particular block of code be run by its respective message handler.
|
||||
|
||||
- A <span class="subsystem">*MessageHandler</span> struct is the component of an editor subsystem that has ownership over its persistent editor state and its child message handlers for the lifetime of the application. It also defines the logic for handling each of its messages that it receives from the dispatcher. Those blocks of logic may further enqueue additional messages to be processed by itself or other message handlers during the same dispatch cycle.
|
||||
|
||||
- A <span class="subsystem">*MessageContext</span> struct is the component of an editor subsystem that defines what data is made available from other subsystems when running the logic to handle a dispatched message. It is a struct that is passed to the message handler when processing a message, and it gets filled in with data (owned, borrowed, or mutably borrowed) from its parent message handler. Intermediate subsystem layers may forward data from their parent to their child contexts to make state available from further up the hierarchy.
|
||||
|
||||
<span class="submessage">Sub-messages</span>
|
||||
|
||||
- A <span class="submessage">#[child] *</span> attribute-decorated message enum variant is a special kind of message that encapsulates a nested subsystem. As with all messages, its handler has a manually written code block. But that code must call its corresponding child message handler's `process_message` method. The child message handler is a field of this parent message handler's state struct.
|
||||
|
||||
`Messages`
|
||||
|
||||
- A `*` message enum variant is used throughout the editor to request that a certain subsystem performs some action, potentially given some data. In that sense, it resembles a function call, but a key difference is that messages are queued up and processed sequentially in a flat order, always invoked by the dispatcher.
|
||||
|
||||
## How messages work
|
||||
|
||||
Messages are enum variants that are dispatched to perform some intended activity within their respective message handlers. Here are two message definitions from <span class="subsystem">DocumentMessage</span>:
|
||||
```rs
|
||||
pub enum DocumentMessage {
|
||||
...
|
||||
// A message that carries one data field
|
||||
DeleteLayer {
|
||||
id: NodeId,
|
||||
}
|
||||
// A message that carries no data
|
||||
DeleteSelectedLayers,
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
As shown above, additional data fields can be included with each message. But as a special case denoted by a <span class="submessage">#[child]</span> attribute, that data can also be a sub-message enum, which enables hierarchical nesting of message handler subsystems.
|
||||
|
||||
By convention, regular data must be written as struct-style named fields (shown above), while a sub-message enum must be written as a tuple/newtype-style field (shown below). The <span class="subsystem">DocumentMessage</span> enum of the previous example is defined as a child of <span class="subsystem">PortfolioMessage</span> which wraps it like this:
|
||||
|
||||
```rs
|
||||
pub enum PortfolioMessage {
|
||||
...
|
||||
// A message that carries the `DocumentMessage` child enum as data
|
||||
#[child]
|
||||
Document(DocumentMessage),
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Likewise, the <span class="subsystem">PortfolioMessage</span> enum is wrapped by the top-level <span class="subsystem">Message</span> enum. The dispatcher operates on the queue of these base-level <span class="subsystem">Message</span> types.
|
||||
|
||||
So for example, the `DeleteSelectedLayers` message mentioned previously will look like this as a <span class="subsystem">Message</span> data type:
|
||||
|
||||
```rs
|
||||
Message::Portfolio(
|
||||
PortfolioMessage::Document(
|
||||
DocumentMessage::DeleteSelectedLayers
|
||||
)
|
||||
)
|
||||
```
|
||||
|
||||
Writing out these nested message enum variants would be cumbersome, so that <span class="submessage">#[child]</span> attribute shown earlier invokes a proc macro that automatically implements the `From` trait, letting you write this instead to get a <span class="subsystem">Message</span> data type:
|
||||
|
||||
```rs
|
||||
DocumentMessage::DeleteSelectedLayers.into()
|
||||
```
|
||||
|
||||
Most often, this is simplified even further because the `.into()` is called for you when pushing a message to the queue with `.add()` or `.add_front()`. So this becomes as simple as:
|
||||
|
||||
```rs
|
||||
responses.add(DocumentMessage::DeleteSelectedLayers);
|
||||
```
|
||||
|
||||
The `responses` message queue is composed of <span class="subsystem">Message</span> data types, and thanks to this system, child messages like `DocumentMessage::DeleteSelectedLayers` are automatically wrapped in their ancestor enum variants to become a <span class="subsystem">Message</span>, saving you from writing the verbose nested form.
|
||||
@@ -0,0 +1,106 @@
|
||||
+++
|
||||
title = "Graphene"
|
||||
template = "book.html"
|
||||
page_template = "book.html"
|
||||
|
||||
[extra]
|
||||
order = 5 # Chapter number
|
||||
+++
|
||||
|
||||
Graphene is the node graph engine that powers the Graphite editor.
|
||||
|
||||
It's hard to describe in one sentence precisely what Graphene is, because it's a technology that serves several roles when viewed from different angles. But to get a feel for what it encompasses, here is a list of some of its purposes:
|
||||
|
||||
- Render engine
|
||||
- Runtime environment
|
||||
- Procedural data processor
|
||||
- Node-based scripting system
|
||||
- Compiled programming language
|
||||
- Compiler toolchain built around `rustc`
|
||||
|
||||
## Background
|
||||
|
||||
### Artwork as a program
|
||||
|
||||
Artwork created in Graphite is represented as a node graph that generates the graphical content authored by the user. This document is essentially source code for a program in the Graphene language. Modifying the graph (like adding a layer, changing a node's parameter, or updating a node's data every frame while interactively drawing a shape) changes the actual program that generates and renders the artwork. This program must be recompiled and executed every frame a change is made.
|
||||
|
||||
Nodes are functions that run algorithms related to graphical operations. Some may read bitmap images from disk, others may generate procedural patterns, and more may be used for compositing and blending. Vector nodes can also produce shapes, alter their geometry, and apply styling and effects. Put together, a full document is built from just its interconnected nodes— producing a complete work of art generated entirely with algorithms and data.
|
||||
|
||||
### Graph executors as programming languages
|
||||
|
||||
Every node-based application needs to run its node graph to compute the resulting data. Execution occurs in an order that depends on the shape of the graph so that every node has the data it needs to compute its output.
|
||||
|
||||
A procedural graph executor, in its basic form, is a simple system that executes functions in the appropriate order. It feeds information between nodes and caches that data for reuse between executions so that only changed branches of the graph have to be computed again. The system, as described, is the approach commonly used by virtually all node-based apps.
|
||||
|
||||
Crucially, the execution flow is handled at runtime so there is some overhead during every run. By analogy to programming languages, this traditional execution model acts like an interpreted language. But interpreted languages are famously slow, and we don't want Graphite leaving performance on the table.
|
||||
|
||||
In designing Graphene, we decided to take a more advanced approach that could yield many of the benefits of a compiled language— code inlining, compiler optimizations, and a philosophy of offloading invariant enforcement to the type system. Instead of building a simple graph interpreter where functions (nodes) are run as user input changes, we designed a system that dynamically executes the graph with a variable degree of pre-compiled optimizations where bits and pieces are recompiled and patched in while the user modifies the artwork (and graph) every frame. Thereby, Graphene can dynamically range between an interpreted language, a JIT-optimized language, and a fully compiled language.
|
||||
|
||||
## Technical overview
|
||||
|
||||
### The latency/performance tradeoff
|
||||
|
||||
While working in Graphite, multiple needs arise for speed in different contexts. While making interactive changes, the user needs feedback as quickly as possible. While panning and zooming the canvas or playing an animation, the user cares about smoothness and responsiveness. When procedural artwork is exported as a standalone program that processes data at runtime (like as part of an image processing web server or embedded within a game engine), performance is the sole concern.
|
||||
|
||||
This sliding scale of latency/performance concerns maps directly to programming language concepts. Interpreted languages run immediately, but with slow runtime performance. JIT-optimized languages also run nearly without delay, but with less overhead than an interpreter since it can dynamically balance its effort towards optimizing and executing code. Compiled languages take upfront time to compile, but run with less overhead. A choice of optimization levels can be applied to further trade initial compilation time for runtime performance.
|
||||
|
||||
We designed Graphene to operate in all three regimes:
|
||||
|
||||
| Regime | Usage |
|
||||
|-|-|
|
||||
| Interpreted | While editing. Simple and currently the only mode that's implemented. |
|
||||
| JIT | While editing. Dynamically bridges the gap between both other regimes by selectively substituting branches of the graph with interpreted and compiled nodes to keep latency low and work towards higher execution performance. |
|
||||
| Compiled | When exported. The entire graph is compiled as a standalone program. |
|
||||
|
||||
### Building upon the Rust compiler
|
||||
|
||||
Nodes are functions written in Rust and every node has precompiled bytecode that ships with Graphite for use in the interpreted regime. The graph `input` → `A` → `B` → `C` → `output` is equivalent to the Rust statement `let output = C(B(A(input)));`. Graphene can either execute `A`, `B`, and `C` sequentially in its interpreted regime, or its JIT and compiled regimes can generate that Rust statement and compile it with the Rust compiler, `rustc`. The inlined and optimized bytecode can then be substituted for those three nodes in the JIT regime.
|
||||
|
||||
Graphene figures out which branches of the graph to compile and substitute as part of the JIT process while the user is authoring content in Graphite. While editing the graph, as changes occur to specific nodes, their surrounding graph branches drop back down to using the slower interpreted nodes. Then the JIT system works its way back up to faster execution over time by gradually compiling and swapping in larger optimized parts of the overall graph.
|
||||
|
||||
The fully compiled regime is used only when the user exports the procedural artwork as a standalone program. For example, a CLI program may read a string input argument (like a name) and procedurally generate an output image file (like a birthday card).
|
||||
|
||||
### Compile server
|
||||
|
||||
The three regimes have thus far been only a description of the eventual architecture direction. The interpreted regime is currently the only mode implemented in Graphene. The other two will require access to `rustc` which will necessitate the compile server that we will finish building and then publicly host for Graphite users in the future. Users of the desktop version of Graphite will be able to use an embedded `rustc` if the user has opted to download the Rust toolchain while installing Graphite.
|
||||
|
||||
Without a compile server, all the nodes are precompiled when Graphite is built. The node registry (in the file `node_registry.rs`) currently exists to allow the interpreted executor to find the Rust functions that correspond to each node with its appropriate type signature. Nodes support generics, so it's currently necessary to list every forseeable concrete type signature in the registry until the compile server can generate bytecode for less common type combinations on-the-fly.
|
||||
|
||||
### GPU compute shaders
|
||||
|
||||
Further building upon the Rust compiler toolchain, we employ the [`rust-gpu`](https://github.com/EmbarkStudios/rust-gpu) compiler backend for `rustc` which generates compute shaders that get executed on the GPU. This means we can write the same code to implement nodes that run on both CPU and GPU. (Although in practice, some nodes may need GPU-specific versions suited for the architectural limitations of GPU programming.) And we don't have to use a separate shader language!
|
||||
|
||||
### A language within a language
|
||||
|
||||
While Graphene is a programming language, it is also foundationally built upon the Rust language. We don't just use the Rust compiler, but we also employ its type system, traits, data structures, standard library, and crate ecosystem. The data that flows between nodes are Rust types (like structs, enums, tuples, primitives, and collections). Graphene's generic type system uses Rust's trait definitions in its enforcement of type safety and type inference.
|
||||
|
||||
### Graphene language concepts
|
||||
|
||||
Since Graphene is fundamentally a programming language, throughout this documentation we will use analogies which correlate Graphene concepts with their counterparts from traditional programming language theory. Here is an at-a-glance overview:
|
||||
|
||||
| Graphene concept | Programming language concept |
|
||||
|:------------------|:-------------------------------------|
|
||||
| Node | Function |
|
||||
| Graphite editor | IDE/text editor |
|
||||
| Document | Source code |
|
||||
| Graph/network | Abstract syntax tree (AST) |
|
||||
| Graph compilation | Linking/JIT optimization/compilation |
|
||||
| Graph execution | Program execution |
|
||||
|
||||
<!-- Our philosophy of building (bootstrapping) our own higher-level language features from the language itself -->
|
||||
<!-- Call arguments, construction arguments, `.eval()`, recompiling when construction argument values are updated but not when call argument data changes -->
|
||||
<!-- Extract/inject nodes and metaprogramming -->
|
||||
<!-- Cache nodes and stable node IDs -->
|
||||
<!-- Graph rewriting step (currently used only to remove Identity nodes),
|
||||
at various points in the compilation process,
|
||||
based on rules akin to an optimizing compiler -->
|
||||
<!-- Borrow tree -->
|
||||
<!-- Document nodes, proto nodes, and networks (must be: acyclic) -->
|
||||
<!-- Lambdas -->
|
||||
<!-- Graph compilation process -->
|
||||
<!-- The compilation server -->
|
||||
<!-- Code structure overview -->
|
||||
<!-- Guide for implementing a node -->
|
||||
<!-- The `Node` trait -->
|
||||
<!-- Generics, type inference, type erasure, and the node registry -->
|
||||
<!-- Monitor nodes -->
|
||||
@@ -0,0 +1,24 @@
|
||||
+++
|
||||
title = "Networks and nodes"
|
||||
|
||||
[extra]
|
||||
order = 1 # Page number after chapter intro
|
||||
+++
|
||||
|
||||
In Graphite, users build their artwork by connecting nodes together in a graph. When they want to organize and reuse a complex group of nodes, those may be encapsulated together as a subgraph in which one parent node represents the functionality of its children. In fact, many of the nodes provided in Graphite are themselves subgraphs built out of other nodes.
|
||||
|
||||
Double-clicking on nodes backed by a subgraph will display the subgraph's interior. Double-clicking nodes that are, instead, backed directly by Rust source code will open a code editor.
|
||||
|
||||
Any (sub)graph can import/export data from/to the outside world. For example, a reusable subgraph may receive an imported image then use several nodes to process it and finally export the result. Or the root-level artwork graph may import the animation timestamp and render a frame of the artwork then export it to the canvas.
|
||||
|
||||
In the Graphite editor UI, here is an example graph of artwork that imports no data but exports its content to the canvas:
|
||||
|
||||
<img src="https://static.graphite.art/content/features/mockup-node-graph.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Node graph UI mockup" />
|
||||
|
||||
The graph shown above represents the full artwork, meaning it's the root-level graph in its document. But there is nothing special about that graph compared to any subgraph. To avoid the confusion of calling it a graph or subgraph which comes with implications about user-facing concepts in the context of a document, we will use the less-ambiguous term **network** in the context of Graphene's internal concepts and codebase.
|
||||
|
||||
## Networks
|
||||
|
||||
A node network can be thought of as a box containing a finite set of nodes that are connected together as a directed acyclic graph (DAG). The network is only concerned with its own node-to-node data flow. But to interact with the outside world, data can be imported into the network and exported out of it. From the inside, those imported/exported data sources/destinations are connected to the other nodes in the network. From the outside, a network can be considered a "black box" that is simply fed inputs and can be executed to produce outputs.
|
||||
|
||||
***More coming soon...***
|
||||
@@ -0,0 +1,53 @@
|
||||
+++
|
||||
title = "Project setup"
|
||||
template = "book.html"
|
||||
page_template = "book.html"
|
||||
|
||||
[extra]
|
||||
order = 1 # Chapter number
|
||||
+++
|
||||
|
||||
To begin working with the Graphite codebase, you will need to set up the project to build and run on your local machine. Development usually involves running the dev server which watches for changes to frontend (web) and backend (Rust) code and automatically recompiles and reloads the Graphite editor in your browser.
|
||||
|
||||
## Dependencies
|
||||
|
||||
Graphite is built with Rust and web technologies, which means you will need to install:
|
||||
- [Rust](https://www.rust-lang.org/) (the latest stable release)
|
||||
- [Node.js](https://nodejs.org/) (the latest LTS version)
|
||||
- [Git](https://git-scm.com/) (any recent version)
|
||||
|
||||
## Repository
|
||||
|
||||
Clone the project to a convenient location:
|
||||
|
||||
```sh
|
||||
git clone https://github.com/GraphiteEditor/Graphite.git
|
||||
```
|
||||
|
||||
## Development builds
|
||||
|
||||
In the project directory, run the build system by executing:
|
||||
|
||||
```sh
|
||||
cargo run
|
||||
```
|
||||
|
||||
This will check for the required system dependency versions, help you install any that are missing, and spin up the dev server at <http://localhost:8080> serving the web app with debug optimizations. A file watcher hot-reloads the web app when you save a code file. Shut down the dev server by double pressing <kbd>Ctrl</kbd><kbd>C</kbd>.
|
||||
|
||||
For additional build commands, see:
|
||||
|
||||
```sh
|
||||
cargo run help
|
||||
```
|
||||
|
||||
For example, if you must proxy the dev server connection over a slow network where the >100 MB unoptimized binary size would pose an issue, you may need to run with release optimizations using `cargo run release`.
|
||||
|
||||
## Development tooling
|
||||
|
||||
We provide default configurations for VS Code users. When you open the project, watch for a prompt to install the project's [suggested extensions](https://github.com/GraphiteEditor/Graphite/blob/master/.vscode/extensions.json). They will provide helpful web and Rust tooling. If you use a different IDE, you won't get default configurations for the project out of the box, so please remember to format your code and check CI for errors.
|
||||
|
||||
### Checking, linting, and formatting
|
||||
|
||||
While developing Rust code: `cargo check`, `cargo clippy`, and `cargo fmt` terminal commands may be run from the root directory. For web code: errors, code quality lints, and formatting issues can be checked using `npm run check` (to view them) and `npm run fix` (to fix them) if run from the `/frontend` directory.
|
||||
|
||||
If you don't use VS Code and its format-on-save feature, please remember to format before committing or [set up a `pre-commit` hook](https://githooks.com/) to do that automatically. Disabling VS Code's *Auto Save* files feature is recommended to ensure you actually save (and thus format) file changes. CI will enforce that everything passes these checks before your PR can be merged.
|
||||
@@ -0,0 +1,16 @@
|
||||
+++
|
||||
title = "Starting a task"
|
||||
template = "book.html"
|
||||
page_template = "book.html"
|
||||
|
||||
[extra]
|
||||
order = 3 # Chapter number
|
||||
+++
|
||||
|
||||
There are two places to look for beginner-friendly development tasks. Usually, the best option is to select one of the many bite-sized task descriptions marked with a ‼️ reaction in the `#✅code-todo-list` channel of the [Discord server](https://discord.graphite.art). You may also browse the task board for a list of [beginner issues](https://github.com/orgs/GraphiteEditor/projects/1/views/6) to pick from. The Discord option usually has the more approachable tasks, compared to the GitHub issues that often have more variability in complexity.
|
||||
|
||||
If you're unsure about which task to pick, feel free to ask in the `#📄development` channel. You can also use that channel to ask for coding help if you anticipate it will be a quick discussion rather than a longer-running conversation that deserves its own thread.
|
||||
|
||||
You may right click a `#✅code-todo-list` task and select "Create Thread" to ask questions and discuss your development progress. If your work doesn't correspond to a specific listed task in that channel, you can also create a thread in `#🧵task-help` with a short, descriptive title ending with your issue or PR number.
|
||||
|
||||
If you're tackling a GitHub issue, please remember to comment in the issue with a link to your PR once you submit it. This is necessary because we will assign you to the issue after your PR has merged, but GitHub only allows assignments to those who have commented on the issue.
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
+++
|
||||
title = "AI contribution policy"
|
||||
|
||||
[extra]
|
||||
order = 4 # Page number after chapter intro
|
||||
+++
|
||||
|
||||
Many open source projects including Graphite have begun to be spammed with an ever-increasing flood of low-quality PRs written partly or wholly by AI. These harm the project by wasting the time of maintainers and preventing PRs by genuine contributors from receiving timely review. We aim to be reasonable and understanding to contributors who put in the effort, but it has become necessary to set some strict rules against low-effort PRs.
|
||||
|
||||
## Acceptable usage
|
||||
|
||||
- Non-agent AI tools may **assist** with debugging and tab-completion of single lines of code you would have otherwise written yourself. This does not require disclosure.
|
||||
- AI chat tools (not agents) may help you **generate** small (sub-40 line) snippets of code that you manually copy and paste, provided that you carefully review every line to ensure it is consistent with how you would have written it yourself. This requires disclosure.
|
||||
|
||||
## Unacceptable usage
|
||||
|
||||
- AI slop, "vibe-coded", or agent-written PRs are strictly forbidden and may be treated as malicious spam attacks against the project, resulting in a ban.
|
||||
- PR description text and replies to reviewers must be written by you, not AI. If your English is imperfect, just try your best; it is better than AI babble.
|
||||
|
||||
## Required disclosure
|
||||
|
||||
- Graphite has **zero-tolerance** for contributing undisclosed AI-generated content.
|
||||
- A detailed, human-written description must accompany every line of material that you did not personally write using your own brain. It should justify why each line is correct and appropriate. This should be prepared ahead of time and written as self-review comments on the GitHub PR's diff immediately after the PR is opened or new code is pushed.
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
+++
|
||||
title = "Code quality guidelines"
|
||||
|
||||
[extra]
|
||||
order = 2 # Page number after chapter intro
|
||||
+++
|
||||
|
||||
The Graphite project prizes code quality and accessibility to new contributors. Therefore, we ask you please make all efforts to contribute readable, well-documented code according to these best practices.
|
||||
|
||||
## Linting
|
||||
|
||||
Please ensure Clippy is enabled. This should be set up automatically in VS Code. Avoid committing code with lint warnings so the code review process goes smoothly. You may execute `cargo clippy` anytime to confirm.
|
||||
|
||||
## Naming
|
||||
|
||||
Please use descriptive variable/function/symbol names and keep abbreviations to a minimum. Prefer spelling out full words most of the time, such as `generate_document_format` instead of `gen_doc_fmt`.
|
||||
|
||||
This avoids the mental burden of expanding abbreviations into semantic meaning. Monitors are wide enough to display long variable/function names, so descriptive is better than cryptic.
|
||||
|
||||
Totally unambiguous, common shortened forms are acceptable such as "max" for "maximum", "eval" for "evaluate", and "info" for "information".
|
||||
|
||||
To avoid wasted effort in code review, it's recommended that you set up a spellcheck plugin, like [this extension](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker) for VS Code. The project uses American English spelling conventions.
|
||||
|
||||
## Whole-number floats
|
||||
|
||||
Always use the style `42.` instead of `42.0` for whole-number floats to maintain consistency and brevity. For range syntax, either `0.0..42.` or `(0.)..42.` is acceptable.
|
||||
|
||||
## Comments
|
||||
|
||||
For consistency, please try to write comments (`//`) in *Sentence case* (with a capital first letter) and don't end with a period unless multiple sentences are used in the same comment. For doc comments (`///`), always end your sentences with a period. There should always be one space after the `//` or `///` comment markers, and `/* */` style comments should be avoided.
|
||||
|
||||
Avoid including commented-out code in PRs that are open for code review unless you have a compelling reason to keep it around for future reference.
|
||||
|
||||
Comments should usually be placed on a separate line above the code they are referring to, not at the end of the same code line.
|
||||
|
||||
## Blank lines
|
||||
|
||||
Please make a habit of grouping together related lines of code in blocks separated by blank lines. These are like your paragraphs if you were writing a novel — they greatly aid readability and your copy editor would have significant concerns with your writing if they were absent.
|
||||
|
||||
If you have dozens of lines comprising a single unbroken block of logic, you are likely not splitting it apart enough to aid readability. Find sensible places to partition the logic and insert blank lines between each. At least 10% of the code you write should ideally be blank lines, otherwise you are likely underutilizing them at the expense of readability.
|
||||
|
||||
## Imports
|
||||
|
||||
Our imports used to be a mess before we tamed the chaos with a formatting rule that has to be applied manually, since `rustfmt` doesn't support it.
|
||||
|
||||
We always combine imports with common paths, but only at the same depth. For example:
|
||||
|
||||
```rs
|
||||
use crate::A::B::C;
|
||||
use crate::A::B::C::Foo;
|
||||
use crate::A::B::C::Bar;
|
||||
|
||||
// Should be combined into:
|
||||
|
||||
use crate::A::B::C::{self, Foo, Bar};
|
||||
```
|
||||
|
||||
But we do not combine imports at mixed path depths. In other words, never put `::` inside `{}`. For example:
|
||||
|
||||
```rs
|
||||
use crate::A::{B::C::Foo, X::Hello};
|
||||
|
||||
// Should be separated into:
|
||||
|
||||
use crate::A::B::C::Foo;
|
||||
use crate::A::X::Hello;
|
||||
```
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
+++
|
||||
title = "Submitting a contribution"
|
||||
|
||||
[extra]
|
||||
order = 3 # Page number after chapter intro
|
||||
+++
|
||||
|
||||
Collaboration is a key part of real-world software engineering. Graphite follows some basic procedures to keep the process smooth and efficient. You will want to familiarize yourself with these guidelines to save yourself and Graphite maintainers time and confusion.
|
||||
|
||||
This assumes you understand enough about how Git works to utilize commits, branches, and multiple remotes. If you're new to Git, you will need to learn those topics on your own, but a good starting point is [this portion](https://youtu.be/vUzIeg8frh4?t=237) of the Graphite intro webcast which recommends installing the [Git Graph](https://marketplace.visualstudio.com/items?itemName=mhutchie.git-graph) extension for VS Code to visualize your Git history and branches.
|
||||
|
||||
## AI usage
|
||||
|
||||
If you are using any form of AI tools in your development workflow, you must read and comply with our [AI contribution policy](../ai-contribution-policy) before submitting your PR.
|
||||
|
||||
## Git branch name
|
||||
|
||||
Before making your first commit, create a new branch with a name that describes what it's about. Aim for short but sufficiently descriptive. Kebab-case (using hyphens between words) is our usual convention. Don't include a prefix like `feature/` or `fix/` which just adds visual noise. An example like `fix-path-tool-selection-history` is fine, but almost too long.
|
||||
|
||||
**Warning: do not open a PR from a branch named `master`.** It makes code review considerably more difficult. Create a new branch if you've already been committing to `master` and open your PR from that correctly-named branch.
|
||||
|
||||
After you push your branch to GitHub then open a PR, you won't be able to change its branch name. But please don't close a PR and open a new one just because the branch name isn't optimal. Just keep these tips in mind for the next time.
|
||||
|
||||
## Pull request
|
||||
|
||||
Once you have gotten your code far enough along that you are confident you'll be able to complete it, open a pull request (PR). You might also do this earlier if a maintainer requests to see your code in order to assist you.
|
||||
|
||||
Later on when you are building larger features, a PR should be opened once you have meaningful progress. That way, it can be kept safe on GitHub and other maintainers can check in to see your status so your work is less of a mystery.
|
||||
|
||||
**Here's the important part:** when you open a PR, it should be marked as a draft unless it is currently ready for review. The left image shows how to open a new PR as a draft, and the right image shows how to convert an existing PR to a draft.
|
||||
|
||||
<p><img src="https://static.graphite.art/content/volunteer/guide/draft-pr.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" alt="Screenhots showing GitHub's "Create pull request (arrow) > Create draft pull request" and "Still in progress? Convert to draft" buttons" /></p>
|
||||
|
||||
<center><em>Open a new PR as a draft / convert an existing PR to a draft</em></center>
|
||||
|
||||
You should mark it as ready for review and ping a maintainer when you believe your code implements the needed functionality and doesn't introduce any new bugs or broken features.
|
||||
|
||||
## Title and description
|
||||
|
||||
Your PR title will become the commit message of your feature's commit in the project Git history. It should aim to concisely but descriptively summarize what your PR does. We use sentence case and imperative mood ("Fix X bug", "Add Y feature", "Make Z faster").
|
||||
|
||||
If you are working on a task from the `#✅code-todo-list` Discord channel, you should right-click the exact message, select "Copy Message Link", and paste that into your PR description.
|
||||
|
||||
If you are working on a task with a GitHub issue, please be certain to include that issue number in the description. GitHub requires the format "Closes #123", "Fixes #123", or "Resolves #123". If there are multiple issues, you have to fully repeat this trigger word for each one. If there is no issue, remove the pre-filled "Closes #" description text.
|
||||
|
||||
When the PR gets merged, any issue referenced by the trigger word will be automatically closed. That isn't desirable for [tracking issues](https://github.com/GraphiteEditor/Graphite/issues?q=is%3Aissue+is%3Aopen+in%3Atitle+%22tracking+issue%22), so you should instead use "Part of #123" which isn't a trigger word. After the PR merges, please edit the description to change "Part of" to "Closes" so the tracking issue links to the PR without it having gotten closed.
|
||||
|
||||
As a bonus, it can be helpful for maintainers if you take a few minutes to write about what you changed and include relevant screenshots or video clips.
|
||||
|
||||
If you have concerns about a certain approach you took or if a certain part of your code is as clean as it could be, you can leave comments on lines of your own code from the "Files changed" tab after opening the PR.
|
||||
|
||||
## Comment on the issue for assignment after it merges
|
||||
|
||||
For any issue referenced in your PR (including tracking issues), we need you to leave a comment on issue. It doesn't matter what you write. You can just say "I opened PR #456" or something to that effect. This is only necessary because we will need to assign that issue to you upon merging your PR, but GitHub only allows assignments to those who have commented.
|
||||
|
||||
That way you get credit for your work and we can keep our closed issues cleanly organized. For consistency, a closed issue should have an assignee if it was resolved by a PR. Otherwise, only duplicate or invalid issues should be closed without an assignee.
|
||||
|
||||
We don't commonly assign issues while a PR is still in progress, only upon landing the PR. That's because PRs often get abandoned and we don't want an assignment blocking someone else from picking up the work.
|
||||
|
||||
## Code review etiquette
|
||||
|
||||
It is your responsibility to build the editor, thoroughly test your work, and employ common sense to avoid wasting a maintainer's time in needing to point out obvious flaws. It is not uncommon for inexperienced contributors to request review when their code entirely fails to implement the task at hand, or breaks surrounding functionality in a way that should have been immediately apparent. This doesn't leave a good impression and can frustrate maintainers. It may also be interpreted as AI-generated spam if the mistakes are egregious enough, which will lead to a ban according to our [AI contribution policy](../ai-contribution-policy).
|
||||
|
||||
If you don't actually understand what is intended with your feature/fix and why this is meaningful to a user of Graphite, spend time becoming that user and understanding the context. [Learning](/learn) at least the basics of using Graphite is important. Then ask questions in Discord if you're still confused about specific edge cases or the wording of the task.
|
||||
|
||||
It is also common for larger tasks to enter a round of review to confirm the direction is correct before you go back and polish the remaining details of the implementation. It's good to be in touch with the team to decide on when is the right time for this kind of preliminary review. It can save you effort reworking problems if you misunderstand the goals, or if the exact details of the requirements were never well-defined and you'll need to iterate on the design together with the team. Don't feel that every part of your PR needs to be 100% finished before requesting feedback, but also be clear so you aren't taking a maintainer away from other work to point out that you are obviously nowhere near done.
|
||||
|
||||
## Self-review
|
||||
|
||||
Before marking your PR as ready for review, you should do a self-review. That means reading over the diff of all your changes to ensure they are correct, complete, and lacking frivolous changes like unintended whitespace alterations, leftover debugging code, or commented-out lines. Read over it with a fine-toothed comb so maintainers don't have to nitpick as much. It is only fair that your first code reviewer should be yourself, so you catch the obvious flaws first.
|
||||
|
||||
Feel free to leave comments on lines of your own code in the diff if you want to communicate concerns or highlight uncertainties to the maintainer. This is also where you must [disclose AI generated lines of code](../ai-contribution-policy) if applicable.
|
||||
|
||||
## Passing CI
|
||||
|
||||
Upon pushing a commit to your PR's branch, CI will need to build and test your code. PRs from forks will have to wait until a maintainer approves the CI run. If you're uncertain, run `cargo test --all-features` on your machine or ask a maintainer to trigger CI for you.
|
||||
|
||||
You also have to pass `cargo fmt` and `cargo clippy` in CI before your PR can be merged. You should run these commands locally before pushing to confirm.
|
||||
|
||||
Your goal is for the check called "Editor: Dev & CI / build (pull_request)" to pass with a ✅. If it fails with an ❌, you will need to investigate. If you need access to the build logs, ask a maintainer to provide them. Occasionally, other checks may fail, but you likely won't be responsible for fixing those and they can be ignored.
|
||||
|
||||
## Keeping your work up-to-date
|
||||
|
||||
Be sure to start your work from the latest commit on the `master` branch by pulling (`git pull`) with `master` checked out when you begin coding.
|
||||
|
||||
As time goes on and `master` accumulates new commits, your branch will become outdated. It has to be synced up with `master` before your PR can be merged. Sometimes there will be conflicts that you need to resolve, which you can find learning resources for online. Enabling Git's three-way diff conflict style with `git config --global merge.conflictstyle diff3` can make this process easier.
|
||||
|
||||
When your branch can be updated with `master` without conflicts, you can click the "Update branch" button below the CI status. If you click the dropdown button beside it, you can choose instead to update with a rebase. If this can be done without conflicts, this is preferred because it maintains a clean, linear history for your branch.
|
||||
|
||||
<p><img src="https://static.graphite.art/content/volunteer/guide/update-branch-with-rebase.avif" onerror="this.onerror = null; this.src = this.src.replace('.avif', '.png')" onload="this.width = this.naturalWidth / 2" alt="Screenhots showing GitHub's "Update with rebase" button" /></p>
|
||||
|
||||
Be sure to pull the rebased, or updated-with-a-merge-commit, branch after you or a maintainer updates it (or pushes other commits to it) to ensure you are working on the latest code.
|
||||
|
||||
**Please do not** constantly rebase or merge every day while you're waiting for review since it's unhelpful and gets annoying. A reviewer will do that for you if there are no conflicts. But if there are conflicts, you *will* need to resolve them and push the updated code before review.
|
||||
|
||||
## Review process
|
||||
|
||||
Assuming you have done what's explained above, a maintainer will aim to review your PR within a few days if possible. Feel free to send reminders because PRs can get overlooked.
|
||||
|
||||
As a rule of thumb, at this stage you are about 50% done with your work. The other 50% of your time will be spent responding to feedback and making (sometimes significant) changes.
|
||||
|
||||
There are two parts to the review process, QA and code review, which occur separately:
|
||||
|
||||
- Quality assurance (QA): A build of your code will be opened and tested to ensure it implements the requested functionality and doesn't introduce regressions. This is not a substitute for your own testing, but it is a necessary line of defense against overlooked issues. This is usually performed by Keavon, the founder and product designer, whose eye for detail keeps the app polished and consistent. Maintainers (and only maintainers) have the ability to invoke CI by commenting "!build" on your PR which will produce a build link. That is a unique link hosting a build of your PR's current code.
|
||||
- Code review: The code will be checked for flawed approaches, pitfalls, confusing logic, [style guide](../code-quality-guidelines) adherence, sufficient comments and tests, and general quality. A review may be left through GitHub or your PR may have commits added to it. Feel free to read the diffs of those commits to understand what was changed so you can learn from that feedback. Direct commits are often faster than leaving dozens of comments. These can range from nitpicks to larger improvements. Our process is to collaborate on PRs as a team to write the best code possible, meaning your PR won't always be exclusively written by you.
|
||||
|
||||
When changes are requested, the maintainer will usually mark the PR as a draft again while awaiting your updates. **It is your responsibility to mark it as ready for review** again once you have addressed the feedback.
|
||||
|
||||
- If a PR is a draft, the ball is in your court to move it forward.
|
||||
- If it's marked as ready for review, it means there is nothing more for you to do until the maintainer has time to review it. (You're encouraged to work on other PRs while waiting.)
|
||||
|
||||
After any number of back-and-forth cycles, a maintainer (usually Keavon who often gives the final say) will merge your PR. All your commits will be squashed into a single new commit on the `master` branch. This keeps the Git history linear and easy to follow.
|
||||
|
||||
Congratulations on landing your successful contribution! Post a request in `#📄development` on Discord to be assigned the *"Code Contributor"* role.
|
||||
@@ -0,0 +1,218 @@
|
||||
+++
|
||||
title = "Student projects"
|
||||
template = "book.html"
|
||||
page_template = "book.html"
|
||||
|
||||
[extra]
|
||||
order = 4 # Chapter number
|
||||
+++
|
||||
|
||||
Graphite offers a number of opportunities for students to contribute by building a self-contained project as part of a structured format. These projects are designed to be completed over several months and are ideal for Google Summer of Code or similar internship programs, solo or group university capstone projects, and other arrangements. Each project has a distinct focus and is a great way to make a meaningful contribution to open source over the length of the program while receiving mentorship and guidance from the Graphite team.
|
||||
|
||||
Student projects require adherence to a set schedule with regular check-ins, milestones, and evaluations. The structured setting is designed to provide a supportive environment for students to learn and grow as developers while gaining real-world industry experience from collaborating on a sizable software product and remaining accountable to stakeholders. It's our goal to make sure you succeed!
|
||||
|
||||
Use this [contributor guide](..) to start out with the code. Then when you're ready, reach out through [Discord](https://discord.graphite.art) and use the `#🎓student-projects` channel to discuss and work towards proposing a project with the Graphite core team.
|
||||
|
||||
## AI contribution policy
|
||||
|
||||
Be sure to familiarize yourself with our [AI contribution policy](../starting-a-task/ai-contribution-policy) before getting involved with the Graphite code base. Proposals also must not be written by AI or else they will be rejected.
|
||||
|
||||
## Google Summer of Code
|
||||
|
||||
GSoC is a program offering students a [stipend](https://developers.google.com/open-source/gsoc/help/student-stipends) for successful completion of an internship-style experience with an open source organization. Read about [how it works](https://summerofcode.withgoogle.com/how-it-works/).
|
||||
|
||||
Graphite participated in GSoC [2024](https://summerofcode.withgoogle.com/programs/2024/organizations/graphite) and [2025](https://summerofcode.withgoogle.com/programs/2025/organizations/graphite) and we anticipate doing so again in [2026](https://developers.google.com/open-source/gsoc/timeline) if our organization is accepted back. We accept year-round contributions; getting involved early is a great way to have a head start and stand out in your application in the upcoming program.
|
||||
|
||||
### Writing a proposal
|
||||
|
||||
Writing a good proposal is an important step that demonstrates your understanding of the project and your ability to think ahead and execute it. A well-defined plan will set you up for success throughout the rest of the program.
|
||||
|
||||
<details>
|
||||
<summary>For proposal writing guidelines and requirements: click here</summary>
|
||||
|
||||
You are encouraged to reference the project idea list below to find several potential projects suited to your experience, interest, and choice of scope. Then, you must reach out to a [core team member](/about#core-team) through Discord to discuss your plan in detail before writing a proposal. This will help you understand the project's scope and requirements and develop a detailed timeline for your expected summer-long work schedule. Importantly, it will also help us understand your background and capabilities to offer you feedback and suggestions for the best outcome in the competitive applicant selection process.
|
||||
|
||||
When it comes to writing the proposal, which you will submit to the GSoC application website, we offer some guidelines below:
|
||||
|
||||
- **Proposal structure:** Please consult the [Blender GSoC application template](https://developer.blender.org/docs/programs/gsoc/application_template/) as reference for our desired format. For project ideas already listed below, omit the "Benefits" section. Remember: don't waste your—and our—time restating information that we already know, like background info about Graphite or our tech stack; we just want to hear your thoughts and plans about what you uniquely bring to the table and how you'll execute the project. Proposals should be utilitarian, not formal, while also demonstrating your professional communication skills. Using an LLM to write your proposal won't be to your advantage.
|
||||
- **Experience:** We're especially interested in your background and work experience, so attaching a résumé or CV is an optional but recommended way to help us understand your capabilities. If able, please also include links to past open source contributions or personal projects in the bio section. Our goal is to provide an environment for you to learn and grow as a productive software engineer and team collaborator, not to help you learn the basics of coding, so any included work examples will help us understand your potential as a self-motivated contributor to the open source community.
|
||||
- **Work timeline:** Your goal is to write a proposal that inspires confidence in your ability to successfully complete the project, which means understanding in detail what's involved at a technical level and how you plan to tackle it. A detailed work timeline is the most important written part of your proposal. It should be broken into weekly milestones with a couple sentences of technical detail. The summary in the project idea list below doesn't give enough information to develop a timeline, so you'll need to discuss this with the core team on Discord.
|
||||
- **Prior PRs:** The largest factor in our selection decision will be the quality and extent of your prior contributions to Graphite made during the proposal formulation period (or before, if applicable). Include a link to `https://github.com/GraphiteEditor/Graphite/commits?author=YOUR_GITHUB_USERNAME` in your proposal and feel free to write up a summary of what you've contributed and learned from the process. You may also keep contributing during the month after applications close, before we've finalized our selections, for those additional PRs to be considered.
|
||||
|
||||
</details>
|
||||
|
||||
## Project idea list
|
||||
|
||||
Projects listed below vary considerably in their required skills and technical background. Some are very research-heavy and are only suited for students with years of self-motivated learning and project development in adjacent topics. Others have a more general focus and are approachable to a wider range of students. Please pay close attention to the "Needed Skills" and "Difficulty" indicators so you don't waste your opportunity applying to a project we don't think you're a good fit for.
|
||||
|
||||
<!--
|
||||
- System for nodes displaying gizmos to update their parameters
|
||||
- Category of tools for repeating, mirroring, patterning, and manipulating objects ("recipes")
|
||||
- Text improvements (formatting spans, flows between text areas, text-on-path)
|
||||
- Feature-complete SVG import and rendering support
|
||||
-->
|
||||
|
||||
### Graphene language bidirectional type inference
|
||||
|
||||
*Graphene needs to implement a more powerful type system so a generic type may be inferred based on surrounding context of the type's usage constraints.*
|
||||
|
||||
- **Possible Mentors:** [Dennis](https://github.com/truedoctor)
|
||||
- **Needed Skills:** Rust, type theory, programming languages theory, past experience implementing such a system
|
||||
- **Project Size:** Large *(GSoC: 350 hours)*
|
||||
- **Difficulty:** Hard
|
||||
- **Expected Outcomes:** A complete implementation to upgrade the current limited type inference system. The new system should work like Rust's, where variables of unknown types can be given a type satisfying the later usages of the variable.
|
||||
|
||||
Consider a node with a generic input parameter which is connected to a node supplying a concrete type. As long as the type is one that satisfies the constraints of the generic parameter, this is valid. The current system checks for this single-directional constraint. But many cases arise where this is insufficient. For example, if the generic parameter is used in multiple places with different constraints, the system needs to be able to infer a type that satisfies all of those constraints.
|
||||
|
||||
<details>
|
||||
<summary>For additional technical details: click here</summary>
|
||||
|
||||
Read more about [HM type inference](https://en.wikipedia.org/wiki/Hindley%E2%80%93Milner_type_system), a powerful (but potentially more complex than necessary) model. See also the [GitHub issue](https://github.com/GraphiteEditor/Graphite/issues/2350) describing this, where you can ask questions if needed. This is an advanced topic and only suitable for individuals who have already implemented a similar system in a programming language or compiler project before.
|
||||
|
||||
</details>
|
||||
|
||||
### Node equivalence rewriting
|
||||
|
||||
*A sequence of nodes may perform operations on data that can be expressed using fewer equivalent nodes, and users may often wish to perform such simplifications.*
|
||||
|
||||
- **Possible Mentors:** [Dennis](https://github.com/truedoctor)
|
||||
- **Needed Skills:** Rust, graph theory, algorithm design
|
||||
- **Project Size:** Large *(GSoC: 350 hours)*
|
||||
- **Difficulty:** Hard
|
||||
- **Expected Outcomes:** A system for classifying and tracking data transformations symbolically within the DAG of the node graph. A system for applying rewrite rules to selected portions of the graph to produce an equivalent graph with fewer nodes. Integration with the editor to allow users to apply simplifications to selected nodes, especially to transforms and geometry.
|
||||
|
||||
Oftentimes, node graphs contain redundant steps that collectively perform a simpler operation. For example, two Transform nodes may produce the same result as a single Transform node with the combined transformation. Or a node that generates a star shape, then a Path node that applies a differential modification to its geometry, may be equivalent to a single Path node that produces the same geometry in one step. This project is about architecting and integrating a system for tracking classes of data transformations, like transforms or geometric modifications or appearance changes, and allowing the user to select the redundant nodes to collapse or "bake" them into a simpler graph with identical output. This is sort of like selecting the terms of a math expression and applying algebraic simplification rules to reduce it to its simplified form.
|
||||
|
||||
<details>
|
||||
<summary>For additional technical details: click here</summary>
|
||||
|
||||
This is best for someone with an interest towards graph theory and compiler optimization topics like [E-graphs](https://en.wikipedia.org/wiki/E-graph). Additional detail is provided in the [GitHub issue](https://github.com/GraphiteEditor/Graphite/issues/2021) including some introductory explanation about E-graphs from a Rust crate that implements them, [egg](https://egraphs-good.github.io/).
|
||||
|
||||
</details>
|
||||
|
||||
### Machine learning architecture
|
||||
|
||||
*AI/ML image/vision models for content editing will need to run in Graphite's node graph with a Rust-centric, modular, portable, deployable, scalable environment.*
|
||||
|
||||
- **Possible Mentors:** [Oliver](https://github.com/otdavies)
|
||||
- **Needed Skills:** Machine learning (and potentially: Rust, Python, ONNX, Burn)
|
||||
- **Project Size:** Large *(GSoC: 350 hours)*
|
||||
- **Difficulty:** Hard
|
||||
- **Expected Outcomes:** Specifics will vary by proposal. In general, a useful end-to-end integration of at least one image model into Graphite's node graph which can run both locally and deployed to a hosting provider server.
|
||||
|
||||
AI/ML is filling a rapidly growing role in the industry as a tool in some creative processes. Graphite's procedural node-based workflow is uniquely suited to leveraging the power and flexibility of AI nodes.
|
||||
|
||||
[Segment Anything 2](https://ai.meta.com/research/sam2/) (object segmentation) and [Depth Anything 3](https://github.com/ByteDance-Seed/Depth-Anything-3) (depth estimation) are currently the models we are most [interested in integrating](https://github.com/GraphiteEditor/Graphite/issues/1694). The challenge is settling on an architecture and tech stack which is well suited for Graphite's requirements.
|
||||
|
||||
<details>
|
||||
<summary>For additional technical details: click here</summary>
|
||||
|
||||
The approach should be extensible to future models. It needs to run fast and natively on the assorted hardware of local user machines with hardware acceleration. It should be a one-click installation process for users to download and run models without requiring dependencies or environment setup. Ideally, it should allow the more lightweight models to run locally in browsers with WebGPU. It needs to also be deployable to servers in a scalable, cost-viable manner that reuses most of the same code that runs locally. Runtime overhead, cold start times, and memory usage should be minimized for quick, frequent switching between models in a node graph pipeline. The tech stack also needs to be permissively licensed and, as much as possible, Rust-centric so it doesn't add complexity to our Wasm and desktop build processes.
|
||||
|
||||
To meet most of these criteria, our current thinking is to distribute and run our models using the [ONNX](https://onnx.ai/) format. This would integrate ONNX runtimes for WebGPU, native, and GPU cloud providers. One challenge is that many of the best-performing models are not packaged in ONNX format, but this approach also allows for direct implementation of model architectures in Rust.
|
||||
|
||||
[Burn](https://burn.dev/) is Rust's most promising and advanced machine learning framework, and in addition to Rust model implementations, it also [supports](https://github.com/tracel-ai/burn-onnx) ONNX model loading for conversion into its native format.
|
||||
|
||||
Another potential direction is to find a portable, modular, lightweight approach for bundling existing Python-based models. It would need to work across simple and complex models with different architectures. License compliance, if GPL code is involved, would be a consideration.
|
||||
|
||||
Based on the experience and insight brought to the table by the student, the nature of the project should be defined through preliminary discussions with the mentors and codified in the proposal. Machine learning and MLOps are fields that Graphite's team lack deep expertise in, so we are looking for a knowledgable student who can bring forth a well-researched and well-architected proposal and then execute on it.
|
||||
|
||||
</details>
|
||||
|
||||
### Generalized graphical data rendering representation
|
||||
|
||||
*Rendering graphical content like colors, gradients, patterns, and whole other layers needs to be possible in a more flexible way that can target the fills and strokes of vector shapes.*
|
||||
|
||||
- **Possible Mentors:** [Keavon](https://github.com/keavon)
|
||||
- **Needed Skills:** Rust, SVG
|
||||
- **Project Size:** Medium or Large *(GSoC: 175 or 350 hours)*
|
||||
- **Difficulty:** Medium
|
||||
- **Expected Outcomes:** Improved SVG and Vello renderer implementations that can handle a wider variety of paint types and effects. Support for every combination of paint type with its application to fills, strokes, and full-canvas drawing. Inclusion of the specified paint source types in the graphical data model and appropriate nodes for generating and handling such data.
|
||||
|
||||
Presently, Graphite has a limited methodology for defining what gets painted when rendering vector shape fills and strokes. Solid colors and spatially positioned gradients are supported for fills, but only solid colors for strokes. Also, gradients cannot be painted across the entire canvas, and patterns do not exist at all yet. This project involves refactoring the renderer and data model to support a more generalized representation of paint sources that can be applied to fills, strokes, and entire layers. It deprecates the current solid/gradient/none selection for fills and solid/none selection for strokes in favor supporting anything that could be painted as a layer.
|
||||
|
||||
<details>
|
||||
<summary>For additional technical details: click here</summary>
|
||||
|
||||
An extended description and a list of child issues is available in the [GitHub issue](https://github.com/GraphiteEditor/Graphite/issues/2779). A large-sized project would likely include support for the polyfilled gradient types described in the sub-issues of [this task](https://github.com/GraphiteEditor/Graphite/issues/2304).
|
||||
|
||||
</details>
|
||||
|
||||
<!-- ### Advanced text layout and typography
|
||||
|
||||
*This is a newly added project pending a full written overview. Come ask on Discord for details.*
|
||||
|
||||
- [See the GitHub issue.](https://github.com/GraphiteEditor/Graphite/issues/1105)
|
||||
|
||||
### Brush engine
|
||||
|
||||
*This is a newly added project pending a full written overview. Come ask on Discord for details.*
|
||||
|
||||
- [See the GitHub issue.](https://github.com/GraphiteEditor/Graphite/issues/1297)
|
||||
|
||||
### Advanced color management
|
||||
|
||||
*This is a newly added project pending a full written overview. Come ask on Discord for details.*
|
||||
|
||||
- Add support for HDR/WCG and/or CMYK and alternate color spaces/models
|
||||
- Requires an experienced understanding of color science
|
||||
|
||||
### SVG with raster effects
|
||||
|
||||
*This is a newly added project pending a full written overview. Come ask on Discord for details.*
|
||||
|
||||
- The SVG spec supports a number of filters and other raster effects, and we currently only implement a small subset.
|
||||
- Add support for the rest of the SVG spec, including filters, masks, and other raster effects.
|
||||
- Allow roundtrip import and export of SVG files with these features.
|
||||
- Import, render (through SVG and Vello), and export of [filters like these](https://codepen.io/miXTim/pen/ZErggMQ).
|
||||
|
||||
### Snapping system overhaul
|
||||
|
||||
*This is a newly added project pending a full written overview. Come ask on Discord for details.*
|
||||
|
||||
- [See the GitHub issue.](https://github.com/GraphiteEditor/Graphite/issues/2352)
|
||||
|
||||
### Tooling polishing and gizmo additions
|
||||
|
||||
*This is a newly added project pending a full written overview. Come ask on Discord for details.* -->
|
||||
|
||||
### Marquee selection masking
|
||||
|
||||
*Graphite's raster editing features requires the implementation of Select mode, where users can draw a mask which becomes a marquee (marching ants) selection.*
|
||||
|
||||
- **Possible Mentors:** [Keavon](/about#keavon)
|
||||
- **Needed Skills:** Rust, computer graphics
|
||||
- **Project Size:** Large *(GSoC: 350 hours)*
|
||||
- **Difficulty:** Medium
|
||||
- **Expected Outcomes:** Complete implementation of Mask mode and its marquee selection. Marching ants visualization shader effect. Integration of selection mask with the node graph and raster editing tools. Useful raster editing workflow.
|
||||
|
||||
A central part of the workflow in raster image editors is the selection of portions of the image to constrain manipulations just to the masked areas. Tools such as the circular and rectangular marquee, lasso, and magic wand are used to create masks. Instead of using dedicated tools, Graphite's design reuses the existing vector and raster drawing tools (like Rectangle, Ellipse, Pen, and Fill) to create masks in a dedicated Mask mode. Returning from Mask mode reveals the marching ants selection that constrains further editing operations.
|
||||
|
||||
This is a key feature in Graphite's evolution to a fully-featured raster editor.
|
||||
|
||||
### Testing and performance instrumentation
|
||||
|
||||
*Graphite has many areas that could benefit from better automated testing for bugs and performance regressions.*
|
||||
|
||||
- **Possible Mentors:** [Dennis](/about#dennis)
|
||||
- **Needed Skills:** Rust, unit testing
|
||||
- **Project Size:** Small *(GSoC: 90 hours)* or larger if proposed
|
||||
- **Difficulty:** Easy
|
||||
- **Expected Outcomes:** Specific focus and scope may vary by the student's interests and proposal. In general, a significant increase in the coverage of tests in useful code areas (such as document loading, tool manipulation, and rendering) and attention towards systems which measure performance metrics and identify bottlenecks and regressions.
|
||||
|
||||
Graphite could benefit from better testing coverage in a number of areas, especially end-to-end testing in the tool, document, and node graph systems. This project is about identifying and addressing areas that are lacking and most vulnerable to suffering from regressions. The student will be responsible for identifying areas that could benefit from better testing.
|
||||
|
||||
### Your own idea
|
||||
|
||||
*If you have an idea for a project that you think would be a good fit, we'd love to hear it!*
|
||||
|
||||
- **Possible Mentors:** Varies
|
||||
- **Needed Skills:** Varies
|
||||
- **Project Size:** Varies
|
||||
- **Difficulty:** Varies
|
||||
- **Expected Outcomes:** Stated in your proposal.
|
||||
|
||||
If none of the projects above suit your interests or experience, we are very open to discussing your own project ideas that could benefit Graphite. You may consult our [task board](https://github.com/orgs/GraphiteEditor/projects/1/views/1) and [roadmap](/features#roadmap) to get a feel for what our current priorities are.
|
||||
|
||||
As is the case with all projects, please discuss this with us on Discord to flesh out your idea. Unsolicited proposals that have not been discussed with us will almost certainly be rejected.
|
||||
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
+++
|
||||
title = "Completed projects"
|
||||
|
||||
[extra]
|
||||
order = 2 # Page number after chapter intro
|
||||
+++
|
||||
|
||||
We keep an archive of our successful student projects from past years to help prospective applicants get a better feel for the types and scope of projects we can support.
|
||||
|
||||
## 2025
|
||||
|
||||
### GPU-accelerated raster operations
|
||||
|
||||
*Raster operations are limited to slow CPU-based fallbacks while GPU shader implementations require further infrastructure engineering.*
|
||||
|
||||
Affiliation: GSoC 2025
|
||||
Duration: 3 months
|
||||
Student: Firestar99
|
||||
|
||||
- [Program project listing](https://summerofcode.withgoogle.com/organizations/graphite/projects/details/TfdLAuN4)
|
||||
- [Report and weekly updates](https://github.com/GraphiteEditor/Graphite/discussions/2658)
|
||||
|
||||
**Outcomes:** Restructuring of dependencies within the node and data type definitions to allow for `#[no_std]` in the implementations of shader-driven raster nodes. Introduction of a compile-time pipeline for loading and compiling CPU node implementations to shader code using [Rust GPU](https://github.com/Rust-GPU/rust-gpu). Node definition macro changes to declare per-pixel color adjustment nodes as fragment shaders. Upstream improvements to Rust GPU and its build tool, [Cargo GPU](https://github.com/Rust-GPU/cargo-gpu), to support Graphite's use cases while avoiding a need for the rest of the Graphite project adopt the nightly Rust toolchain.
|
||||
|
||||
**Background:** Graphite's node graph engine executes and renders graphics by means of defining artwork as procedural node graph programs within the purpose-built Graphene language. Each graphics operation is a node with, at minimum, a CPU implementation which supports compilation along with the rest of the graph into an executable Graphene program for rendering to the screen. A major goal is to share that same CPU implementation for the GPU version that compiles to a GPU shader in order to maintain identical algorithms between versions and avoid the maintenance burden of separate code paths. However, GPU architectures enforce challenging constraints which leaves this goal as yet unrealized. The project must tackle the engineering challenges of setting up a basic shader compilation system and integrate GPU versions of nodes into the Graphite editor and its render pipeline.
|
||||
|
||||
|
||||
### 3 additional projects (summaries coming soon)
|
||||
|
||||
See the [program listing](https://summerofcode.withgoogle.com/programs/2025/organizations/graphite) for more details until the other three GSoC 2025 project summaries are added here.
|
||||
|
||||
## 2024
|
||||
|
||||
### Interactive node graph auto-layout
|
||||
|
||||
*Graphite's graph UI needs a system to automatically arrange layers and nodes given incremental changes to the graph contents.*
|
||||
|
||||
Affiliation: GSoC 2024
|
||||
Duration: 3 months
|
||||
Student: Adam Gerhant
|
||||
|
||||
- [Program project listing](https://summerofcode.withgoogle.com/programs/2024/projects/gvbBoCpT)
|
||||
- [Report and weekly updates](https://github.com/GraphiteEditor/Graphite/discussions/1769)
|
||||
|
||||
**Outcomes:** A system that manages the placement of nodes based on a set of layout constraint rules and incremental updates to the graph topology. It should run efficiently, even with large graphs. It should be robust enough to handle a variety of graph topologies and user interactions, producing organized, useful, and stable layouts.
|
||||
|
||||
**Background:** The Graphite concept is built around a node graph representation of layer stacks, while tools automatically generate and manipulate nodes. When a layer or node is inserted, deleted, moved, or referenced, the graph needs to be reorganized to maintain a clear and useful layout. Users can also interactively expand and collapse groups of nodes which occupies or frees up graph real estate.
|
||||
|
||||
Unlike other node editors that are centered around manual graph editing, where users are fully in charge of node placements within one large node network, Graphite's node UI is more oriented towards automatic layout management and viewing just parts of the graph at one time. This means the shown graph topology is constantly changing and the layout system needs to cooperatively organize the graph in concert with user actions.
|
||||
|
||||
While general graph layout algorithms are complex and struggle to produce good results in other node editors, Graphite's graph topology is more constrained and predictable, which makes it possible to design a layout system that can produce good results. Nodes tend to be organized into rows, and layers into columns. This turns the problem into more of a constraint-based, axis-aligned packing problem.
|
||||
|
||||
### Rendering performance infrastructure improvements
|
||||
|
||||
*Graphite performance is bottlenecked by limitations in the new node graph rendering architecture that needs improvements.*
|
||||
|
||||
Affiliation: GSoC 2024
|
||||
Duration: 4 months
|
||||
Student: Dennis Kobert
|
||||
|
||||
- [Program project listing](https://summerofcode.withgoogle.com/programs/2024/projects/v5z2Psnc)
|
||||
- [Report and weekly updates](https://github.com/GraphiteEditor/Graphite/discussions/1773)
|
||||
|
||||
**Outcomes:** A holistic, metrics-driven focus on fixing the many unoptimized areas of Graphite's node graph compilation, execution, and rendering systems. Integration of Vello as an integrated rendering backend. A significant improvement in the performance of the editor, especially in the node graph, and a more stable and predictable performance profile. Benchmarking and profiling tools to measure and visualize performance improvements and regressions.
|
||||
|
||||
**Background:** Graphite's node graph system is the backbone of the editor, but it has many performance problems that need to be addressed because the system is relatively immature and performance-impacting shortcuts were taken during its initial development. This project is all about making the node graph system more robust and optimized, which will have a direct impact on the user experience and the editor's overall performance. By the end of the project, the editor should finally feel usable in the majority of user workflows. Vello should be enabled as an alternate render engine that will fully replace the existing SVG-based one in the future, once browser support arrives across major platforms.
|
||||
|
||||
### Raw photograph decoding in Rust
|
||||
|
||||
*For Graphite to support editing photos from professional digital cameras, it needs a raw decoding/processing library.*
|
||||
|
||||
Affiliation: GSoC 2024
|
||||
Duration: 5 months
|
||||
Student: Elbert Ronnie
|
||||
|
||||
- [Program project listing](https://summerofcode.withgoogle.com/programs/2024/projects/2uiwOfz8)
|
||||
- [Report and weekly updates](https://github.com/GraphiteEditor/Graphite/discussions/1771)
|
||||
- [Rawkit library](https://crates.io/crates/rawkit)
|
||||
|
||||
**Outcomes:** A Rust library that implements raw photo decoding functionality to native Rust. A clean, well-structured codebase and API. At a minimum, demonstrate the successful end-to-end decoding, debayering, and color space handling of Sony ARW format photos in Graphite. Publish the library to crates.io.
|
||||
|
||||
**Background:** For Graphite to work as a photo editing app, it needs to import raw photos. These contain compressed sensor imagery and metadata in a variety of formats. Sony ARW is the first target and additional camera brands are stretch goals. Graphite needs a library written in pure Rust with a suitable (non-GPL) license, which does not currently exist in the ecosystem, so we need to create one ourselves.
|
||||
|
||||
## 2023
|
||||
|
||||
### Bezier-rs library
|
||||
|
||||
*Graphite's vector editing features require the implementation of Bezier curve and path manipulation computational geometry algorithms.*
|
||||
|
||||
Affiliation: University of Waterloo, Ontario, Canada
|
||||
Duration: 9 months
|
||||
Students: Hannah Li, Rob Nadal, Thomas Cheng, Linda Zheng, Jackie Chen
|
||||
|
||||
- [Bezier-rs library](https://crates.io/crates/bezier-rs)
|
||||
- [Interactive web demo](https://keavon.github.io/Bezier-rs/)
|
||||
|
||||
**Outcomes:** The student group designed an API for representing and manipulating Bezier curves and paths as a standalone Rust library which was published to crates.io. It now serves as the underlying vector data format used in Graphite, and acts as a testbed for new computational geometry algorithms. The team also built an interactive web demo catalog to showcase many of the algorithms, which are also handily embedded in the library's [documentation](https://docs.rs/bezier-rs/latest/bezier_rs/).
|
||||
|
||||
## 2022
|
||||
|
||||
### Backend layout system
|
||||
|
||||
*Graphite's UI needs a system to define and manage layouts for widgets from the backend.*
|
||||
|
||||
Affiliation: California Polytechnic State University, San Luis Obispo, USA
|
||||
Duration: 3 months
|
||||
Student: Max Fisher
|
||||
|
||||
**Outcomes:** The student designed and implemented a new system across the editor's frontend and backend which made it possible to define and manage layouts for widgets from the backend and receive input data from those widgets. Previously, all layouts were statically defined in the frontend and extensive plumbing was required to pass data back and forth.
|
||||
|
||||
### Path boolean operations
|
||||
|
||||
*Graphite's vector editing features require the implementation of boolean operations on paths, such as union, intersection, and difference.*
|
||||
|
||||
Affiliation: California Polytechnic State University, San Luis Obispo, USA
|
||||
Duration: 3 months
|
||||
Student: Caleb Dennis
|
||||
|
||||
**Outcomes:** The student devised and prototyped algorithms for performing boolean operations on paths, such as union, intersection, and difference. These were used as a stopgap during 2022 and 2023 to provide users with a rudimentary boolean operation feature set.
|
||||
Reference in New Issue
Block a user