Skip to content

[BrushGraph 2/7] Add converters and repository#61

Open
maxmmitchell wants to merge 9 commits intobrush-graph/1-datafrom
brush-graph/2-persistence
Open

[BrushGraph 2/7] Add converters and repository#61
maxmmitchell wants to merge 9 commits intobrush-graph/1-datafrom
brush-graph/2-persistence

Conversation

@maxmmitchell
Copy link
Copy Markdown

Description

This is the second PR in the Brush Graph stack. It introduces persistence and conversion logic to translate the interactive graph data model into Android Ink Proto formats.

Details

  • Mapping definitions between the UI-friendly BrushGraph and the tree-structured ProtoBrushFamily.
  • Deduplication algorithms to reduce the number of identical nodes on-screen, while maintaining semantically identical brushes.
  • BrushGraphRepository abstracts the persistence and conversion details away from the ViewModel, providing a clean API for loading and saving.
  • Centralizes failure points during serialization/deserialization.

Dependencies

  • Target: brush-graph/1-data

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a graph-based data model for custom brushes, providing utilities to convert between the BrushGraph model and the functional BrushFamily proto. It includes a BrushGraphRepository for state management, validation, and persistence, along with comprehensive unit tests. The review feedback highlights several performance concerns regarding $O(N^2)$ complexity in graph conversion and deduplication logic. Additionally, there are recommendations to improve testability by injecting coroutine scopes, enhance maintainability by removing magic strings, and increase robustness by decoupling port reordering logic from UI-specific index offsets.

_graphIssues.update { (it + newIssue).distinctBy { issue -> Triple(issue.displayMessage, issue.nodeId, issue.severity) } }
}

fun validate(): Boolean {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The validate() method is called synchronously on every graph modification (add node, add edge, etc.). Since GraphValidator.validateAll performs extensive checks over all nodes and edges, this can block the main thread and cause UI stutters for larger graphs. Consider offloading validation to a background thread or debouncing the updates if immediate synchronous feedback isn't required for all operations.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we do need validate() to be called on every graph modification, as we are constantly updating the brush being used. Delaying validation could cause a gap between when the brush family updates, and when the user receives notice their brush has errors. So a brush could be broken and the user wouldn't know before using it. Validate also needs to block conversion (to avoid creating an invalid brush), so there'd be a lag between when the graph is modified and when the brush is updated.


val isFromTexture = fromIndex in 0 until T
val isToTexture = toIndex in 0 until T
val isFromColor = fromIndex in (T + 1) until (T + 1 + data.colorPortIds.size)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for reordering Paint ports relies on hardcoded assumptions about the index offsets (e.g., T + 1). This tightly couples the repository logic to the specific visual representation of ports in the UI. If the UI layout changes, this logic will break. Consider passing the specific port IDs or using a more robust way to identify the target list.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's an argument to be made that this logic belongs at the UI layer. But although it does wind up influencing the UI, it is fundamentally enforcing rules about how the ports are stored in the data layer, so I think using the indices is OK. Perhaps this ordering should be completely maintained at the UI layer instead, but then we need to hold onto the portIds in the data layer, but then also hold an ordering for them in the UI layer. I'm not sure this is any better.

@maxmmitchell maxmmitchell force-pushed the brush-graph/2-persistence branch 5 times, most recently from 4f5748b to bd61e05 Compare April 28, 2026 05:00
@maxmmitchell maxmmitchell marked this pull request as ready for review April 28, 2026 07:57
@maxmmitchell maxmmitchell force-pushed the brush-graph/2-persistence branch from bd61e05 to da9bc38 Compare April 30, 2026 14:28
Copy link
Copy Markdown
Contributor

@cka-dev cka-dev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm with comments.
They are non-blocking but need to be addressed as a fast follow if not addressable right now

@maxmmitchell maxmmitchell force-pushed the brush-graph/2-persistence branch from 257bd46 to c7b9a0d Compare May 1, 2026 19:34
@maxmmitchell maxmmitchell force-pushed the brush-graph/1-data branch 2 times, most recently from 035c82e to 74c3765 Compare May 4, 2026 18:12
@maxmmitchell maxmmitchell force-pushed the brush-graph/2-persistence branch from c7b9a0d to cb3121a Compare May 4, 2026 18:28
cka-dev and others added 7 commits May 5, 2026 14:40
    This PR adds the full Brush Designer screen accessible from Settings, including:

    UI Components:
    - BrushDesignerScreen with adaptive layout (compact/expanded)
    - Three editor tabs: Tip Shape, Paint, and Behaviors
    - NumericField with slider, ± buttons, and tap-to-edit dialog
    - EditableListWidget for managing behaviors, nodes, and texture layers
    - PreviewPane with live stroke rendering via DrawingSurface
    - TipPreview for visual tip shape feedback
    - MathCurvePreview for response curve visualization
    - NodeEditors for Source, Damping, Target, ResponseCurve, BinaryOp, etc.

    Features:
    - Proto editing via BrushDesignerViewModel
    - Import/Export .brush files (GZIP-compressed protobuf)
    - Save to Palette (Room DB integration)
    - Load from Brushes(both stock and custom brushes) or My Palette
    - Standard and Advanced Dynamics presets
    - Texture import with thumbnail preview
    - Color picker for brush paint
add interface + test for repo
@maxmmitchell maxmmitchell force-pushed the brush-graph/2-persistence branch from e98ec7d to 9abf30a Compare May 5, 2026 14:42
@maxmmitchell maxmmitchell force-pushed the brush-graph/2-persistence branch from 9abf30a to ee09998 Compare May 5, 2026 14:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants