Skip to content

Commit

Permalink
refactor: use a PatternView class to synchronize the pattern state …
Browse files Browse the repository at this point in the history
…and its view (#58)

* create a `PatternView` class to synchronize the pattern state and its view

* use `PatternView` in the store

* improve rendering performance

* remove `pixi-cull`

* don't store `Pattern` in the `PatternView`

* make viewport control better

* improve particle container event handling

* extend Pixi plugins

* store beads textures

* return special stitches rendering

* add map tests

* fix back stitch drawing

* fix the palette item selection

* enchance back stitch drawing
  • Loading branch information
niusia-ua authored Dec 30, 2024
1 parent 754ffb0 commit d0bd72d
Show file tree
Hide file tree
Showing 24 changed files with 2,606 additions and 1,743 deletions.
2,325 changes: 1,413 additions & 912 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
"dequal": "^2.0.3",
"pinia": "^2.3.0",
"pinia-plugin-persistedstate": "^4.2.0",
"pixi-cull": "github:niusia-ua/pixi-cull#custom",
"pixi-viewport": "github:niusia-ua/pixi-viewport#custom",
"pixi.js": "^8.6.0",
"primeicons": "^7.0.0",
Expand Down
26 changes: 18 additions & 8 deletions src-tauri/src/commands/stitches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,18 @@ pub fn add_stitch<R: tauri::Runtime>(
window: tauri::WebviewWindow<R>,
history: tauri::State<HistoryState<R>>,
patterns: tauri::State<PatternsState>,
) -> CommandResult<()> {
) -> CommandResult<bool> {
let mut patterns = patterns.write().unwrap();
let patproj = patterns.get_mut(&pattern_key).unwrap();
if !patproj.pattern.contains_stitch(&stitch) {
let mut history = history.write().unwrap();
let action = AddStitchAction::new(stitch);
action.perform(&window, patproj)?;
history.get_mut(&pattern_key).push(Box::new(action));
Ok(true)
} else {
Ok(false)
}
Ok(())
}

#[tauri::command]
Expand All @@ -29,11 +31,19 @@ pub fn remove_stitch<R: tauri::Runtime>(
window: tauri::WebviewWindow<R>,
history: tauri::State<HistoryState<R>>,
patterns: tauri::State<PatternsState>,
) -> CommandResult<()> {
let mut history = history.write().unwrap();
) -> CommandResult<bool> {
let mut patterns = patterns.write().unwrap();
let action = RemoveStitchAction::new(stitch);
action.perform(&window, patterns.get_mut(&pattern_key).unwrap())?;
history.get_mut(&pattern_key).push(Box::new(action));
Ok(())
let patproj = patterns.get_mut(&pattern_key).unwrap();

// This command may accept the stitches which doesn't contain all the properties of the stitch.
// So we need to get the actual stitch from the pattern.
if let Some(stitch) = patproj.pattern.get_stitch(&stitch) {
let mut history = history.write().unwrap();
let action = RemoveStitchAction::new(stitch);
action.perform(&window, patproj)?;
history.get_mut(&pattern_key).push(Box::new(action));
Ok(true)
} else {
Ok(false)
}
}
22 changes: 16 additions & 6 deletions src-tauri/src/core/actions/stitches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,12 +58,18 @@ impl<R: tauri::Runtime> Action<R> for AddStitchAction {

#[derive(Clone)]
pub struct RemoveStitchAction {
stitch: Stitch,
// Actual stitch contains only the necessary stitch properties ...
target_stitch: Stitch,
// ... while the actual stitch contains all properties.
actual_stitch: OnceLock<Stitch>,
}

impl RemoveStitchAction {
pub fn new(stitch: Stitch) -> Self {
Self { stitch }
Self {
target_stitch: stitch,
actual_stitch: OnceLock::new(),
}
}
}

Expand All @@ -73,8 +79,11 @@ impl<R: tauri::Runtime> Action<R> for RemoveStitchAction {
/// **Emits:**
/// - `stitches:remove_one` with the removed stitch
fn perform(&self, window: &WebviewWindow<R>, patproj: &mut PatternProject) -> Result<()> {
patproj.pattern.remove_stitch(self.stitch);
window.emit("stitches:remove_one", self.stitch)?;
let stitch = patproj.pattern.remove_stitch(self.target_stitch).unwrap();
if self.actual_stitch.get().is_none() {
self.actual_stitch.set(stitch).unwrap();
}
window.emit("stitches:remove_one", stitch)?;
Ok(())
}

Expand All @@ -83,8 +92,9 @@ impl<R: tauri::Runtime> Action<R> for RemoveStitchAction {
/// **Emits:**
/// - `stitches:add_one` with the added stitch
fn revoke(&self, window: &WebviewWindow<R>, patproj: &mut PatternProject) -> Result<()> {
patproj.pattern.add_stitch(self.stitch);
window.emit("stitches:add_one", self.stitch)?;
let stitch = self.actual_stitch.get().unwrap();
patproj.pattern.add_stitch(*stitch);
window.emit("stitches:add_one", stitch)?;
Ok(())
}
}
37 changes: 37 additions & 0 deletions src-tauri/src/core/pattern/pattern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,43 @@ impl Pattern {
Pattern { fabric, ..Pattern::default() }
}

/// Get a stitch from the pattern.
pub fn get_stitch(&self, stitch: &Stitch) -> Option<Stitch> {
// This method accepts a reference stitch which may not contain all the stitch properties.
// We use this method to find the actual stitch.

match stitch {
Stitch::Full(fullstitch) => {
if let Some(&fullstitch) = self.fullstitches.get(fullstitch) {
Some(Stitch::Full(fullstitch))
} else {
None
}
}
Stitch::Part(partstitch) => {
if let Some(&partstitch) = self.partstitches.get(partstitch) {
Some(Stitch::Part(partstitch))
} else {
None
}
}
Stitch::Node(node) => {
if let Some(&node) = self.nodes.get(node) {
Some(Stitch::Node(node))
} else {
None
}
}
Stitch::Line(line) => {
if let Some(&line) = self.lines.get(line) {
Some(Stitch::Line(line))
} else {
None
}
}
}
}

/// Check if the pattern contains a stitch.
pub fn contains_stitch(&self, stitch: &Stitch) -> bool {
match stitch {
Expand Down
8 changes: 4 additions & 4 deletions src-tauri/src/core/pattern/stitches/stitches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,11 @@ impl<T: Ord> Stitches<T> {

/// Returns `true` if the set contains a stitch.
pub fn contains(&self, stitch: &T) -> bool {
// We need to use the `get` method to get the actual stitch.
// Then we need to compare the actual stitch with the passed stitch.
// This is because the indexing is done only by the fields that are used for ordering (coordinates, kind, etc.).
// But we need to compare all the other values (mainly, palindex).
if let Some(contained) = self.inner.get(stitch) {
// We need to use the `get` method to get the actual stitch.
// Then we need to compare the actual stitch with the passed stitch.
// This is because the indexing is done only by the fields that are used for ordering (coordinates, kind, etc.).
// But we need to compare all the other values (mainly, palindex).
contained == stitch
} else {
false
Expand Down
8 changes: 4 additions & 4 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@

<SplitterPanel :size="85">
<ProgressSpinner v-if="loading" class="absolute left-1/2 top-1/2" />
<Suspense v-if="patproj"><CanvasPanel /></Suspense>
<Suspense v-if="pattern"><CanvasPanel /></Suspense>
<div v-else class="relative flex h-full w-full items-center justify-center">
<Panel header="No pattern loaded" class="w-3/12 border-0">
<p class="m-0">Open a pattern or create a new one to get started.</p>
Expand Down Expand Up @@ -79,12 +79,12 @@
import WindowControls from "./components/toolbar/WindowControls.vue";
import { useAppStateStore } from "./stores/state";
import { usePreferencesStore } from "./stores/preferences";
import { usePatternProjectStore } from "./stores/patproj";
import { usePatternsStore } from "./stores/patterns";
const appStateStore = useAppStateStore();
const preferencesStore = usePreferencesStore();
const patternProjectStore = usePatternProjectStore();
const { patproj, loading } = storeToRefs(patternProjectStore);
const patternProjectStore = usePatternsStore();
const { pattern, loading } = storeToRefs(patternProjectStore);
onMounted(async () => {
await preferencesStore.setTheme(preferencesStore.theme);
Expand Down
5 changes: 3 additions & 2 deletions src/api/stitches.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { invoke } from "@tauri-apps/api/core";
import type { PatternKey, Stitch } from "#/schemas/pattern";

export const addStitch = (patternKey: PatternKey, stitch: Stitch) => invoke<void>("add_stitch", { patternKey, stitch });
export const addStitch = (patternKey: PatternKey, stitch: Stitch) =>
invoke<boolean>("add_stitch", { patternKey, stitch });
export const removeStitch = (patternKey: PatternKey, stitch: Stitch) =>
invoke<void>("remove_stitch", { patternKey, stitch });
invoke<boolean>("remove_stitch", { patternKey, stitch });
Loading

0 comments on commit d0bd72d

Please sign in to comment.