Skip to content

Commit

Permalink
implement continuous drawing
Browse files Browse the repository at this point in the history
  • Loading branch information
niusia-ua committed Dec 8, 2024
1 parent 9bf03fa commit f1e6c5d
Show file tree
Hide file tree
Showing 7 changed files with 213 additions and 123 deletions.
11 changes: 7 additions & 4 deletions src-tauri/src/commands/stitches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@ pub fn add_stitch<R: tauri::Runtime>(
history: tauri::State<HistoryState<R>>,
patterns: tauri::State<PatternsState>,
) -> CommandResult<()> {
let mut history = history.write().unwrap();
let mut patterns = patterns.write().unwrap();
let action = AddStitchAction::new(stitch);
action.perform(&window, patterns.get_mut(&pattern_key).unwrap())?;
history.get_mut(&pattern_key).push(Box::new(action));
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(())
}

Expand Down
9 changes: 9 additions & 0 deletions src-tauri/src/core/pattern/pattern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,15 @@ pub struct Pattern {
}

impl Pattern {
pub fn contains_stitch(&self, stitch: &Stitch) -> bool {
match stitch {
Stitch::Full(fullstitch) => self.fullstitches.contains(fullstitch),
Stitch::Part(partstitch) => self.partstitches.contains(partstitch),
Stitch::Node(node) => self.nodes.contains(node),
Stitch::Line(line) => self.lines.contains(line),
}
}

/// Adds a stitch to the pattern and returns any conflicts that may have arisen.
pub fn add_stitch(&mut self, stitch: Stitch) -> StitchConflicts {
log::trace!("Adding stitch");
Expand Down
13 changes: 13 additions & 0 deletions src-tauri/src/core/pattern/stitches/stitches.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,19 @@ impl<T: Ord> Stitches<T> {
self.inner.len()
}

/// 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) {
contained == stitch
} else {
false
}
}

/// Inserts a stitch into the set, replacing the existing one.
/// Returns the replaced stitch if any.
pub fn insert(&mut self, stitch: T) -> Option<T> {
Expand Down
124 changes: 67 additions & 57 deletions src/components/CanvasPanel.vue
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@
import { useMagicKeys, whenever, useThrottleFn } from "@vueuse/core";
import { vElementSize } from "@vueuse/components";
import { CanvasService, type CanvasSize } from "#/services/canvas/canvas.service";
import { AddStitchEventStage, EventType } from "#/services/canvas/events.types";
import type { AddStitchData, RemoveStitchData } from "#/services/canvas/events.types";
import { useAppStateStore } from "#/stores/state";
import * as stitchesApi from "#/api/stitches";
import * as historyApi from "#/api/history";
import { PartStitchDirection, StitchKind } from "#/types/pattern/pattern";
import type { FullStitch, Line, Node, PartStitch, Stitch } from "#/types/pattern/pattern";
import type { PatternProject } from "#/types/pattern/project";
import { EventType, type AddStitchData, type RemoveStitchData } from "#/services/canvas/events.types";
import type { Point } from "pixi.js";
interface CanvasPanelProps {
patproj: PatternProject;
Expand All @@ -36,81 +38,81 @@
(patproj) => canvasService.drawPattern(patproj),
);
// A start point is needed to draw the lines.
// An end point is needed to draw all the other kinds of stitches (in addition to lines).
let prevStitchState: Stitch | undefined;
canvasService.addEventListener(EventType.AddStitch, async (e) => {
if (appStateStore.state.selectedPaletteItemIndex === undefined) return;
const { start, end, alt }: AddStitchData = (e as CustomEvent).detail;
const x = Math.trunc(end.x);
const y = Math.trunc(end.y);
// Decimal portion of the end coordinates.
const xdp = end.x - x;
const ydp = end.y - y;
const palindex = appStateStore.state.selectedPaletteItemIndex;
if (palindex === undefined) return;
// The current pattern is always available here.
const patternKey = appStateStore.state.currentPattern!.key;
const palindex = appStateStore.state.selectedPaletteItemIndex;
const tool = appStateStore.state.selectedStitchTool;
const kind = tool % 2; // Get 0 or 1.
// A start point is needed to draw the lines.
// An end point is needed to draw all the other kinds of stitches (in addition to lines).
const { stage, start, end, alt, fixed }: AddStitchData = (e as CustomEvent).detail;
const x = adjustStitchCoordinate(end.x, tool);
const y = adjustStitchCoordinate(end.y, tool);
switch (tool) {
case StitchKind.Full:
case StitchKind.Petite: {
const fullstitch: FullStitch = {
x: adjustStitchCoordinate(x, xdp, kind),
y: adjustStitchCoordinate(y, ydp, kind),
palindex,
kind,
};
await stitchesApi.addStitch(patternKey, { full: fullstitch });
const full: FullStitch = { x, y, palindex, kind };
if (fixed && prevStitchState && "full" in prevStitchState) {
full.x = Math.trunc(x) + (prevStitchState.full.x - Math.trunc(prevStitchState.full.x));
full.y = Math.trunc(y) + (prevStitchState.full.y - Math.trunc(prevStitchState.full.y));
}
if (prevStitchState && stage !== AddStitchEventStage.Continue) prevStitchState = undefined;
else prevStitchState ??= { full };
await stitchesApi.addStitch(patternKey, { full });
break;
}
case StitchKind.Half:
case StitchKind.Quarter: {
const [fracX, fracY] = [end.x % 1, end.y % 1];
const direction =
(xdp < 0.5 && ydp > 0.5) || (xdp > 0.5 && ydp < 0.5)
(fracX < 0.5 && fracY > 0.5) || (fracX > 0.5 && fracY < 0.5)
? PartStitchDirection.Forward
: PartStitchDirection.Backward;
const partstitch: PartStitch = {
x: adjustStitchCoordinate(x, xdp, kind),
y: adjustStitchCoordinate(y, ydp, kind),
palindex,
kind,
direction,
};
await stitchesApi.addStitch(patternKey, { part: partstitch });
const part: PartStitch = { x, y, palindex, kind, direction };
if (fixed && prevStitchState && "part" in prevStitchState) {
part.direction = prevStitchState.part.direction;
if (tool === StitchKind.Quarter) {
part.x = Math.trunc(x) + (prevStitchState.part.x - Math.trunc(prevStitchState.part.x));
part.y = Math.trunc(y) + (prevStitchState.part.y - Math.trunc(prevStitchState.part.y));
}
}
if (prevStitchState && stage !== AddStitchEventStage.Continue) prevStitchState = undefined;
else prevStitchState ??= { part };
await stitchesApi.addStitch(patternKey, { part });
break;
}
case StitchKind.Back:
case StitchKind.Straight: {
const startX = Math.trunc(start.x);
const startY = Math.trunc(start.y);
const [_start, _end] = orderPoints(start, end);
const line: Line = {
x: [adjustStitchCoordinate(startX, start.x - startX, kind), adjustStitchCoordinate(x, xdp, kind)],
y: [adjustStitchCoordinate(startY, start.y - startY, kind), adjustStitchCoordinate(y, ydp, kind)],
x: [adjustStitchCoordinate(_start.x, tool), adjustStitchCoordinate(_end.x, tool)],
y: [adjustStitchCoordinate(_start.y, tool), adjustStitchCoordinate(_end.y, tool)],
palindex,
kind,
};
await stitchesApi.addStitch(patternKey, { line });
if (stage === AddStitchEventStage.End) await stitchesApi.addStitch(patternKey, { line });
else canvasService.drawLine(line, props.patproj.pattern.palette[palindex]!, true);
break;
}
case StitchKind.FrenchKnot:
case StitchKind.Bead: {
const node: Node = {
x: adjustStitchCoordinate(x, xdp, kind),
y: adjustStitchCoordinate(y, ydp, kind),
x,
y,
palindex,
kind,
rotated: alt,
};
await stitchesApi.addStitch(patternKey, { node });
if (stage === AddStitchEventStage.End) await stitchesApi.addStitch(patternKey, { node });
else canvasService.drawNode(node, props.patproj.pattern.palette[palindex]!, true);
break;
}
}
Expand All @@ -122,25 +124,37 @@
await stitchesApi.removeStitch(patternKey, data);
});
function adjustStitchCoordinate(value: number, decimalPortion: number, tool: StitchKind): number {
function adjustStitchCoordinate(value: number, tool: StitchKind): number {
const int = Math.trunc(value);
const frac = value - int;
switch (tool) {
case StitchKind.Full:
case StitchKind.Half: {
return value;
return int;
}
case StitchKind.Petite:
case StitchKind.Quarter: {
return decimalPortion > 0.5 ? value + 0.5 : value;
return frac > 0.5 ? int + 0.5 : int;
}
case StitchKind.Back:
case StitchKind.Straight:
case StitchKind.FrenchKnot:
case StitchKind.Bead: {
return decimalPortion > 0.5 ? value + 1 : decimalPortion > 0.25 ? value + 0.5 : value;
return frac > 0.5 ? int + 1 : frac > 0.25 ? int + 0.5 : int;
}
}
}
/** Orders points so that is no way to draw two lines with the same coordinates. */
function orderPoints(start: Point, end: Point): [Point, Point] {
const x1 = Math.trunc(start.x);
const y1 = Math.trunc(start.y);
const x2 = Math.trunc(end.x);
const y2 = Math.trunc(end.y);
if (y1 === y2) return x1 < x2 ? [start, end] : [end, start];
else return y1 < y2 ? [start, end] : [end, start];
}
export interface StitchesRemoveManyPayload {
fullstitches: FullStitch[];
partstitches: PartStitch[];
Expand All @@ -162,14 +176,10 @@
"stitches:add_many",
({ payload }) => {
const palette = props.patproj.pattern.palette;
for (const fullstitch of payload.fullstitches) {
canvasService.drawFullStitch(fullstitch, palette[fullstitch.palindex]!.color);
}
for (const partstitch of payload.partstitches) {
canvasService.drawPartStitch(partstitch, palette[partstitch.palindex]!.color);
}
if (payload.line) canvasService.drawLine(payload.line, palette[payload.line.palindex]!.color);
if (payload.node) canvasService.drawNode(payload.node, palette[payload.node.palindex]!.color);
for (const full of payload.fullstitches) canvasService.drawFullStitch(full, palette[full.palindex]!);
for (const part of payload.partstitches) canvasService.drawPartStitch(part, palette[part.palindex]!);
if (payload.line) canvasService.drawLine(payload.line, palette[payload.line.palindex]!);
if (payload.node) canvasService.drawNode(payload.node, palette[payload.node.palindex]!);
},
);
const unlistenRemoveOneStitch = await appWindow.listen<Stitch>("stitches:remove_one", ({ payload }) => {
Expand All @@ -180,10 +190,10 @@
});
const unlistenAddOneStitch = await appWindow.listen<Stitch>("stitches:add_one", ({ payload }) => {
const palette = props.patproj.pattern.palette;
if ("full" in payload) canvasService.drawFullStitch(payload.full, palette[payload.full.palindex]!.color);
if ("part" in payload) canvasService.drawPartStitch(payload.part, palette[payload.part.palindex]!.color);
if ("line" in payload) canvasService.drawLine(payload.line, palette[payload.line.palindex]!.color);
if ("node" in payload) canvasService.drawNode(payload.node, palette[payload.node.palindex]!.color);
if ("full" in payload) canvasService.drawFullStitch(payload.full, palette[payload.full.palindex]!);
if ("part" in payload) canvasService.drawPartStitch(payload.part, palette[payload.part.palindex]!);
if ("line" in payload) canvasService.drawLine(payload.line, palette[payload.line.palindex]!);
if ("node" in payload) canvasService.drawNode(payload.node, palette[payload.node.palindex]!);
});
const keys = useMagicKeys();
Expand Down
2 changes: 1 addition & 1 deletion src/schemas/pattern/pattern.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const BlendSchema = BorshSchema.Struct({
});

const BeadSchema = BorshSchema.Struct({
lenght: BorshSchema.f32,
length: BorshSchema.f32,
diameter: BorshSchema.f32,
});

Expand Down
Loading

0 comments on commit f1e6c5d

Please sign in to comment.