Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
3920953
fix area reinit and sqr_length overflow
NailxSharipov Sep 8, 2026
a5aa82f
fix custom cap bounding range
NailxSharipov Sep 9, 2026
6171efc
fix zero length path buffering
NailxSharipov Sep 9, 2026
7fbaad0
float area filter fix
NailxSharipov Sep 9, 2026
16e8651
remove devide api
NailxSharipov Sep 9, 2026
56f2e7f
outline boundary tests
NailxSharipov Sep 9, 2026
51c34bb
fix area condition in string split
NailxSharipov Sep 10, 2026
d550223
add zero length segments fix and tests cap start/end cases
NailxSharipov Sep 10, 2026
2ebb5a1
clamp acos
NailxSharipov Sep 10, 2026
9df90de
fix ogc set param direction
NailxSharipov Sep 10, 2026
5a4add4
fix edge direction in reverse order
NailxSharipov Sep 10, 2026
b3a5fbb
offset inner overlay area filter fix
NailxSharipov Sep 10, 2026
ec107cd
fix degenerate case filter
NailxSharipov Sep 10, 2026
ebbcbd6
fix hole touching test
NailxSharipov Sep 11, 2026
445ebe4
more ocg tests
NailxSharipov Sep 11, 2026
12309e0
more tests
NailxSharipov Sep 11, 2026
503c8ff
split mesh into int and float add basic Bevel impl
NailxSharipov Sep 13, 2026
cd8a276
move to cordic api
NailxSharipov Sep 13, 2026
859de7b
migrate offset to int
NailxSharipov Sep 13, 2026
c1f45f0
impl clean
NailxSharipov Sep 14, 2026
3622caf
float math
NailxSharipov Sep 15, 2026
7e6ef07
Merge branch 'main' of github.com:iShape-Rust/iOverlay into feature/i…
NailxSharipov Sep 16, 2026
92e8573
api delegate refactoring
NailxSharipov Sep 16, 2026
ec9caa7
lazy iter
NailxSharipov Sep 16, 2026
815d81f
code clean
NailxSharipov Sep 16, 2026
089e349
drop memory allocation
NailxSharipov Sep 16, 2026
fe374b4
refactor float math api
NailxSharipov Sep 16, 2026
3cc67b7
regroup
NailxSharipov Sep 17, 2026
f54bd41
migrate overlay editor form iced to eframe
NailxSharipov Sep 17, 2026
66d7fc1
miter fix
NailxSharipov Sep 17, 2026
4e400af
fix area calculation
NailxSharipov Sep 17, 2026
498a51e
fix doc
NailxSharipov Sep 17, 2026
6018753
math solver to variable stroke and outline
NailxSharipov Sep 17, 2026
c46cbd2
resource api
NailxSharipov Sep 18, 2026
3afe99d
migrate to new source api
NailxSharipov Sep 18, 2026
13f131b
fmt
NailxSharipov Sep 18, 2026
b28499d
fix doc
NailxSharipov Sep 18, 2026
aee082b
fix empty buffer
NailxSharipov Sep 18, 2026
865476f
fix vector init
NailxSharipov Sep 18, 2026
b374bd5
fix edge merge
NailxSharipov Sep 18, 2026
4cd672a
move to new contarct
NailxSharipov Sep 19, 2026
03d6db2
move to new adapter api
NailxSharipov Sep 19, 2026
f380105
fix miter near parallel case
NailxSharipov Sep 19, 2026
95b678f
fix simplify for pos/neg winding rule
NailxSharipov Sep 19, 2026
96f7adc
fix clippy and up version
NailxSharipov Sep 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 10 additions & 17 deletions examples/overlay_editor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,19 @@ lto = false
codegen-units = 1

[lib]
crate-type = ["cdylib"]
crate-type = ["cdylib", "rlib"]

[dependencies]

#iced = { path = "../../../../iced", features = ["wgpu", "advanced"] }
iced = { version = "0.14.0", features = ["wgpu", "advanced", "fira-sans"] }

eframe = { version = "0.34.3", default-features = false, features = ["default_fonts", "wgpu", "x11", "wayland"] }
serde = { version = "^1.0", features = ["derive"] }
serde_json = "^1.0"

wasm-bindgen = "~0.2.95"

log = "0.4.22"
console_log = "^1.0.0"
console_error_panic_hook = "^0"

#i_mesh = "^0.5.0"
#i_triangle = { version = "^0.44.0", features = ["serde"] }

i_triangle = { path = "../../../../iShape/iTriangle/iTriangle", default-features = true, features = ["serde"] }
i_triangle = { path = "../../../../iShape/iTriangle/iTriangle", features = ["serde"] }
i_mesh = { path = "../../../../iShape/iMesh/iMesh" }

#ICED_BACKEND=wgpu cargo r -r
[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
web-sys = { version = "0.3", features = ["Window", "Document", "HtmlCanvasElement", "HtmlElement", "CssStyleDeclaration", "Node"] }
log = "0.4"
console_log = "1.0"
console_error_panic_hook = "0.1"
65 changes: 65 additions & 0 deletions examples/overlay_editor/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Overlay Editor

Native and WebAssembly editor built with `eframe`/`egui`. The existing Boolean,
String, Stroke, Variable Stroke, and Outline modes share the original JSON fixtures.

## Native

From this directory:

```sh
cargo run --release
```

Fixtures are resolved relative to `CARGO_MANIFEST_DIR`, so running with
`cargo run --manifest-path /path/to/overlay_editor/Cargo.toml` also works.

- Drag a point to edit its integer coordinates.
- Drag empty canvas space to pan.
- Scroll over the canvas to zoom around the pointer.
- Use Up/Down to change fixtures when a parameter control does not have focus.

## WebAssembly

```sh
cargo check --target wasm32-unknown-unknown --lib
wasm-pack build --release --target web
```

The exported `WebApp` keeps its five JSON input strings. Startup now returns a
Promise; keep the instance alive and await startup to handle graphics errors:

```js
import init, { WebApp } from "./pkg/overlay_editor.js";
await init();
const app = new WebApp();
await app.start(booleanJson, stringJson, strokeJson, variableStrokeJson, outlineJson);
// app.destroy() releases the runner when removing the editor.
```

The runner uses `<canvas id="overlay-editor-canvas">`. If it is absent, startup
creates a full-window canvas in the document body. An existing canvas can be sized
by the embedding page.

## Rendering and layout

`i_triangle` remains a local path dependency and uses the local `i_overlay`
checkout through its own manifest. It triangulates polygon fills, including holes.
The local `i_mesh` dependency builds contours, open paths, and arrows with
`RoundStrokeBuilder` (round joins and caps), rendered as `egui::Mesh`. Point markers
and the grid use native egui painting APIs. `iced` is no longer required.

The original `app/*/{content,control,workspace}`, `draw`, `geom`, `sheet`,
`point_editor`, and `data` modules are retained. Parameters occupy the area above
the canvas; geometry and fixture calculations remain in the content modules.

## Checks

```sh
cargo test --lib
cargo check --all-targets
cargo check --target wasm32-unknown-unknown --lib
```

Tests cover all fixture workspaces, Boolean/String modes, polygon holes,
degenerate strokes, point dragging, panning, zoom anchoring, and arrow navigation.
99 changes: 32 additions & 67 deletions examples/overlay_editor/src/app/boolean/content.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
use crate::app::boolean::control::ModeOption;
use crate::app::boolean::workspace::WorkspaceState;
use crate::app::design;
use crate::app::fill_option::FillOption;
use crate::app::main::{AppMessage, EditorApp};
use crate::app::solver_option::SolverOption;
use crate::data::boolean::BooleanResource;
use crate::geom::camera::Camera;
use crate::point_editor::point::PathsToEditorPoints;
use crate::point_editor::widget::PointEditUpdate;
use eframe::egui::{self, Vec2};
use i_triangle::i_overlay::core::overlay::Overlay;
use i_triangle::i_overlay::i_float::int::rect::IntRect;
use i_triangle::i_overlay::i_shape::int::count::PointsCount;
use iced::widget::scrollable;
use iced::widget::{Button, Column, Container, Row, Space, Text};
use iced::{Alignment, Length, Padding, Size, Vector};
use std::collections::HashMap;

pub(crate) struct BooleanState {
Expand All @@ -22,7 +19,7 @@ pub(crate) struct BooleanState {
pub(crate) mode: ModeOption,
pub(crate) solver: SolverOption,
pub(crate) workspace: WorkspaceState,
pub(crate) size: Size,
pub(crate) size: Vec2,
pub(crate) cameras: HashMap<usize, Camera>,
}

Expand All @@ -33,65 +30,33 @@ pub(crate) enum BooleanMessage {
ModeSelected(ModeOption),
SolverSelected(SolverOption),
PointEdited(PointEditUpdate),
WorkspaceSized(Size),
WorkspaceZoomed(Camera),
WorkspaceDragged(Vector<f32>),
WorkspaceSized(Vec2),
}

impl EditorApp {
fn boolean_sidebar(&self) -> Column<'_, AppMessage> {
let count = self.app_resource.boolean.count;
let mut column =
Column::new().push(Space::new().width(Length::Fill).height(Length::Fixed(2.0)));
for index in 0..count {
let is_selected = self.state.boolean.test == index;

column = column.push(
Container::new(
Button::new(
Text::new(format!("test_{}", index))
.style(if is_selected {
design::style_sidebar_text_selected
} else {
design::style_sidebar_text
})
.size(14),
)
.width(Length::Fill)
.on_press(AppMessage::Bool(BooleanMessage::TestSelected(index)))
.style(if is_selected {
design::style_sidebar_button_selected
} else {
design::style_sidebar_button
}),
)
.padding(self.design.action_padding()),
);
}

column
}

pub(crate) fn boolean_content(&self) -> Row<'_, AppMessage> {
Row::new()
.push(
scrollable(
Container::new(self.boolean_sidebar())
.width(Length::Fixed(160.0))
.height(Length::Shrink)
.align_x(Alignment::Start)
.padding(Padding::new(0.0).right(8))
.style(design::style_sidebar_background),
)
.direction(scrollable::Direction::Vertical(
scrollable::Scrollbar::new()
.width(4)
.margin(0)
.scroller_width(4)
.anchor(scrollable::Anchor::Start),
)),
)
.push(self.boolean_workspace())
pub(crate) fn boolean_content(&mut self, ui: &mut egui::Ui) {
egui::Panel::left("boolean_tests")
.exact_size(150.0)
.resizable(false)
.show_inside(ui, |ui| {
egui::ScrollArea::vertical().show(ui, |ui| {
for index in 0..self.app_resource.boolean.count {
let response = ui.selectable_label(
self.state.boolean.test == index,
format!("test_{index}"),
);
if response.clicked() {
response.surrender_focus();
self.update(AppMessage::Bool(BooleanMessage::TestSelected(index)));
}
}
});
});
egui::CentralPanel::default().show_inside(ui, |ui| {
self.boolean_control(ui);
ui.separator();
self.boolean_workspace(ui);
});
}

pub(crate) fn boolean_update(&mut self, message: BooleanMessage) {
Expand All @@ -102,8 +67,6 @@ impl EditorApp {
BooleanMessage::ModeSelected(mode) => self.boolean_update_mode(mode),
BooleanMessage::PointEdited(update) => self.boolean_update_point(update),
BooleanMessage::WorkspaceSized(size) => self.boolean_update_size(size),
BooleanMessage::WorkspaceZoomed(zoom) => self.boolean_update_zoom(zoom),
BooleanMessage::WorkspaceDragged(drag) => self.boolean_update_drag(drag),
}
}

Expand All @@ -119,7 +82,7 @@ impl EditorApp {
}

pub(crate) fn boolean_next_test(&mut self) {
let next_test = self.state.boolean.test + 1;
let next_test = self.state.boolean.test.saturating_add(1);
if next_test < self.app_resource.boolean.count {
self.boolean_set_test(next_test);
}
Expand All @@ -132,7 +95,7 @@ impl EditorApp {
}
}

fn boolean_update_size(&mut self, size: Size) {
fn boolean_update_size(&mut self, size: Vec2) {
self.state.boolean.size = size;
let points = &self.state.boolean.workspace.points;
if self.state.boolean.workspace.camera.is_empty() && !points.is_empty() {
Expand Down Expand Up @@ -170,7 +133,7 @@ impl BooleanState {
solver: SolverOption::Auto,
workspace: Default::default(),
cameras: HashMap::with_capacity(resource.count),
size: Size::ZERO,
size: Vec2::ZERO,
};

state.load_test(0, resource);
Expand All @@ -197,13 +160,15 @@ impl BooleanState {

self.cameras.insert(self.test, self.workspace.camera);
let mut camera = *self.cameras.get(&index).unwrap_or(&Camera::empty());
if camera.is_empty() && self.size.width > 0.001 {
if camera.is_empty() && self.size.x > 0.001 {
let rect = IntRect::with_iter(editor_points.iter().map(|p| &p.pos))
.unwrap_or(IntRect::new(-10_000, 10_000, -10_000, 10_000));
camera = Camera::new(rect, self.size);
}

self.workspace.camera = camera;
self.workspace.sheet_state = Default::default();
self.workspace.point_state = Default::default();

self.test = index;
}
Expand Down
Loading
Loading