mirror of
https://github.com/ankitects/anki.git
synced 2025-11-06 12:47:11 -05:00
Merge 46e24d7f3f into dac26ce671
This commit is contained in:
commit
d92966ddb4
16 changed files with 162 additions and 75 deletions
|
|
@ -47,6 +47,7 @@ notetypes-toggle-masks = Toggle Masks
|
|||
notetypes-image-occlusion-name = Image Occlusion
|
||||
notetypes-hide-all-guess-one = Hide All, Guess One
|
||||
notetypes-hide-one-guess-one = Hide One, Guess One
|
||||
notetypes-hide-all-but-one = Hide All But One
|
||||
notetypes-error-generating-cloze = An error occurred when generating an image occlusion note
|
||||
notetypes-error-getting-imagecloze = An error occurred while fetching an image occlusion note
|
||||
notetypes-error-loading-image-occlusion = Error loading image occlusion. Is your Anki version up to date?
|
||||
|
|
|
|||
|
|
@ -69,6 +69,12 @@ message GetImageOcclusionNoteResponse {
|
|||
uint32 ordinal = 2;
|
||||
}
|
||||
|
||||
enum OcclusionMode {
|
||||
HIDE_ONE = 0;
|
||||
HIDE_ALL = 1;
|
||||
HIDE_ALL_BUT_ONE = 2;
|
||||
}
|
||||
|
||||
message ImageOcclusionNote {
|
||||
bytes image_data = 1;
|
||||
repeated ImageOcclusion occlusions = 2;
|
||||
|
|
@ -76,7 +82,7 @@ message GetImageOcclusionNoteResponse {
|
|||
string back_extra = 4;
|
||||
repeated string tags = 5;
|
||||
string image_file_name = 6;
|
||||
bool occlude_inactive = 7;
|
||||
OcclusionMode occlusion_mode = 7;
|
||||
}
|
||||
|
||||
oneof value {
|
||||
|
|
|
|||
|
|
@ -839,4 +839,22 @@ mod test {
|
|||
let card2_html = reveal_cloze_text(text, 2, true);
|
||||
assert!(card2_html.contains(r#"data-ordinal="1,2""#));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn image_occlusion_modes() {
|
||||
// Mode 1 (HideAll): should include data-occludeinactive="1"
|
||||
let hide_all = "{{c1::image-occlusion:rect:left=10:top=20:width=30:height=40:oi=1}}";
|
||||
let html = reveal_cloze_text(hide_all, 1, true);
|
||||
assert!(html.contains(r#"data-occludeInactive="1""#));
|
||||
|
||||
// Mode 2 (HideAllButOne): should include data-occludeinactive="2"
|
||||
let hide_all_but_one = "{{c1::image-occlusion:rect:left=10:top=20:width=30:height=40:oi=2}}";
|
||||
let html = reveal_cloze_text(hide_all_but_one, 1, true);
|
||||
assert!(html.contains(r#"data-occludeInactive="2""#));
|
||||
|
||||
// Mode 0 (HideOne): should not include data-occludeinactive attribute
|
||||
let hide_one = "{{c1::image-occlusion:rect:left=10:top=20:width=30:height=40}}";
|
||||
let html = reveal_cloze_text(hide_one, 1, true);
|
||||
assert!(!html.contains("data-occludeInactive"));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use std::path::PathBuf;
|
|||
use anki_io::metadata;
|
||||
use anki_io::read_file;
|
||||
use anki_proto::image_occlusion::get_image_occlusion_note_response::ImageOcclusionNote;
|
||||
use anki_proto::image_occlusion::get_image_occlusion_note_response::OcclusionMode;
|
||||
use anki_proto::image_occlusion::get_image_occlusion_note_response::Value;
|
||||
use anki_proto::image_occlusion::AddImageOcclusionNoteRequest;
|
||||
use anki_proto::image_occlusion::GetImageForOcclusionResponse;
|
||||
|
|
@ -97,14 +98,22 @@ impl Collection {
|
|||
let idxs = nt.get_io_field_indexes()?;
|
||||
|
||||
cloze_note.occlusions = parse_image_occlusions(fields[idxs.occlusions as usize].as_str());
|
||||
cloze_note.occlude_inactive = cloze_note.occlusions.iter().any(|oc| {
|
||||
oc.shapes.iter().any(|sh| {
|
||||
sh.properties
|
||||
.iter()
|
||||
.find(|p| p.name == "oi")
|
||||
.is_some_and(|p| p.value == "1")
|
||||
cloze_note.occlusion_mode = cloze_note
|
||||
.occlusions
|
||||
.iter()
|
||||
.find_map(|oc| {
|
||||
oc.shapes.iter().find_map(|sh| {
|
||||
sh.properties
|
||||
.iter()
|
||||
.find(|p| p.name == "oi")
|
||||
.and_then(|p| match p.value.as_str() {
|
||||
"1" => Some(OcclusionMode::HideAll as i32),
|
||||
"2" => Some(OcclusionMode::HideAllButOne as i32),
|
||||
_ => None,
|
||||
})
|
||||
})
|
||||
})
|
||||
});
|
||||
.unwrap_or(OcclusionMode::HideOne as i32);
|
||||
cloze_note.header.clone_from(&fields[idxs.header as usize]);
|
||||
cloze_note
|
||||
.back_extra
|
||||
|
|
|
|||
|
|
@ -423,9 +423,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
import type { IOMode } from "../routes/image-occlusion/lib";
|
||||
import { exportShapesToClozeDeletions } from "../routes/image-occlusion/shapes/to-cloze";
|
||||
import {
|
||||
hideAllGuessOne,
|
||||
ioImageLoadedStore,
|
||||
ioMaskEditorVisible,
|
||||
occlusionMode,
|
||||
} from "../routes/image-occlusion/store";
|
||||
import CollapseLabel from "./CollapseLabel.svelte";
|
||||
import * as oldEditorAdapter from "./old-editor-adapter";
|
||||
|
|
@ -477,7 +477,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
|
||||
function saveOcclusions(): void {
|
||||
if (isImageOcclusion && globalThis.canvas) {
|
||||
const occlusionsData = exportShapesToClozeDeletions($hideAllGuessOne);
|
||||
const occlusionsData = exportShapesToClozeDeletions($occlusionMode);
|
||||
fieldStores[ioFields.occlusions].set(occlusionsData.clozes);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ import AlignVerticalCenter_ from "@mdi/svg/svg/align-vertical-center.svg?compone
|
|||
import alignVerticalCenter_ from "@mdi/svg/svg/align-vertical-center.svg?url";
|
||||
import AlignVerticalTop_ from "@mdi/svg/svg/align-vertical-top.svg?component";
|
||||
import alignVerticalTop_ from "@mdi/svg/svg/align-vertical-top.svg?url";
|
||||
import CheckboxBlankOutline_ from "@mdi/svg/svg/checkbox-blank-outline.svg?component";
|
||||
import checkboxBlankOutline_ from "@mdi/svg/svg/checkbox-blank-outline.svg?url";
|
||||
import CheckCircle_ from "@mdi/svg/svg/check-circle.svg?component";
|
||||
import checkCircle_ from "@mdi/svg/svg/check-circle.svg?url";
|
||||
import ChevronDown_ from "@mdi/svg/svg/chevron-down.svg?component";
|
||||
|
|
@ -251,6 +253,7 @@ export const underlineIcon = { url: underline_, component: Underline_ };
|
|||
export const deleteIcon = { url: delete_, component: Delete_ };
|
||||
export const inlineIcon = { url: inline_, component: Inline_ };
|
||||
export const blockIcon = { url: block_, component: Block_ };
|
||||
export const mdiCheckboxBlankOutline = { url: checkboxBlankOutline_, component: CheckboxBlankOutline_ };
|
||||
export const mdiAlignHorizontalCenter = { url: alignHorizontalCenter_, component: AlignHorizontalCenter_ };
|
||||
export const mdiAlignHorizontalLeft = { url: alignHorizontalLeft_, component: AlignHorizontalLeft_ };
|
||||
export const mdiAlignHorizontalRight = { url: alignHorizontalRight_, component: AlignHorizontalRight_ };
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
import Icon from "$lib/components/Icon.svelte";
|
||||
import IconButton from "$lib/components/IconButton.svelte";
|
||||
import {
|
||||
mdiCheckboxBlankOutline,
|
||||
mdiEye,
|
||||
mdiFormatAlignCenter,
|
||||
mdiSquare,
|
||||
|
|
@ -26,11 +27,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
import WithFloating from "$lib/components/WithFloating.svelte";
|
||||
|
||||
import {
|
||||
hideAllGuessOne,
|
||||
ioMaskEditorVisible,
|
||||
textEditingState,
|
||||
saveNeededStore,
|
||||
OcclusionMode,
|
||||
occlusionMode,
|
||||
opacityStateStore,
|
||||
saveNeededStore,
|
||||
textEditingState,
|
||||
} from "./store";
|
||||
import { get } from "svelte/store";
|
||||
import { drawEllipse, drawPolygon, drawRectangle, drawText } from "./tools/index";
|
||||
|
|
@ -228,8 +230,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
disablePan(canvas);
|
||||
};
|
||||
|
||||
function changeOcclusionType(occlusionType: "all" | "one"): void {
|
||||
$hideAllGuessOne = occlusionType === "all";
|
||||
function changeOcclusionType(mode: OcclusionMode): void {
|
||||
$occlusionMode = mode;
|
||||
saveNeededStore.set(true);
|
||||
}
|
||||
|
||||
|
|
@ -312,22 +314,34 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
|
|||
{iconSize}
|
||||
on:click={() => (showFloating = !showFloating)}
|
||||
>
|
||||
<Icon icon={$hideAllGuessOne ? mdiViewDashboard : mdiSquare} />
|
||||
<Icon
|
||||
icon={$occlusionMode === OcclusionMode.HideAll
|
||||
? mdiViewDashboard
|
||||
: $occlusionMode === OcclusionMode.HideAllButOne
|
||||
? mdiCheckboxBlankOutline
|
||||
: mdiSquare}
|
||||
/>
|
||||
</IconButton>
|
||||
|
||||
<Popover slot="floating">
|
||||
<DropdownItem
|
||||
active={$hideAllGuessOne}
|
||||
on:click={() => changeOcclusionType("all")}
|
||||
active={$occlusionMode === OcclusionMode.HideAll}
|
||||
on:click={() => changeOcclusionType(OcclusionMode.HideAll)}
|
||||
>
|
||||
<span>{tr.notetypesHideAllGuessOne()}</span>
|
||||
</DropdownItem>
|
||||
<DropdownItem
|
||||
active={!$hideAllGuessOne}
|
||||
on:click={() => changeOcclusionType("one")}
|
||||
active={$occlusionMode === OcclusionMode.HideOne}
|
||||
on:click={() => changeOcclusionType(OcclusionMode.HideOne)}
|
||||
>
|
||||
<span>{tr.notetypesHideOneGuessOne()}</span>
|
||||
</DropdownItem>
|
||||
<DropdownItem
|
||||
active={$occlusionMode === OcclusionMode.HideAllButOne}
|
||||
on:click={() => changeOcclusionType(OcclusionMode.HideAllButOne)}
|
||||
>
|
||||
<span>{tr.notetypesHideAllButOne()}</span>
|
||||
</DropdownItem>
|
||||
</Popover>
|
||||
</WithFloating>
|
||||
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@ import { get } from "svelte/store";
|
|||
|
||||
import { addOrUpdateNote } from "../add-or-update-note.svelte";
|
||||
import type { IOMode } from "../lib";
|
||||
import { hideAllGuessOne } from "../store";
|
||||
import { occlusionMode } from "../store";
|
||||
import type { PageLoad } from "./$types";
|
||||
|
||||
async function save(): Promise<void> {
|
||||
addOrUpdateNote(globalThis["anki"].imageOcclusion.mode, get(hideAllGuessOne));
|
||||
addOrUpdateNote(globalThis["anki"].imageOcclusion.mode, get(occlusionMode));
|
||||
}
|
||||
|
||||
export const load = (async ({ params }) => {
|
||||
|
|
|
|||
|
|
@ -9,14 +9,14 @@ import { get } from "svelte/store";
|
|||
import { mount } from "svelte";
|
||||
import type { IOAddingMode, IOMode } from "./lib";
|
||||
import { exportShapesToClozeDeletions } from "./shapes/to-cloze";
|
||||
import { notesDataStore, tagsWritable } from "./store";
|
||||
import { notesDataStore, OcclusionMode, tagsWritable } from "./store";
|
||||
import Toast from "./Toast.svelte";
|
||||
|
||||
export const addOrUpdateNote = async function(
|
||||
mode: IOMode,
|
||||
occludeInactive: boolean,
|
||||
occlusionMode: OcclusionMode,
|
||||
): Promise<void> {
|
||||
const { clozes: occlusionCloze, noteCount } = exportShapesToClozeDeletions(occludeInactive);
|
||||
const { clozes: occlusionCloze, noteCount } = exportShapesToClozeDeletions(occlusionMode);
|
||||
if (noteCount === 0) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { get } from "svelte/store";
|
|||
import { addOrUpdateNote } from "./add-or-update-note.svelte";
|
||||
import ImageOcclusionPage from "./ImageOcclusionPage.svelte";
|
||||
import type { IOMode } from "./lib";
|
||||
import { hideAllGuessOne } from "./store";
|
||||
import { occlusionMode } from "./store";
|
||||
|
||||
globalThis.anki = globalThis.anki || {};
|
||||
|
||||
|
|
@ -31,7 +31,7 @@ export async function setupImageOcclusion(mode: IOMode, target = document.body):
|
|||
await i18n;
|
||||
|
||||
async function addNote(): Promise<void> {
|
||||
addOrUpdateNote(mode, get(hideAllGuessOne));
|
||||
addOrUpdateNote(mode, get(occlusionMode));
|
||||
}
|
||||
|
||||
// for adding note from mobile devices
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ import { get } from "svelte/store";
|
|||
|
||||
import { optimumCssSizeForCanvas } from "./canvas-scale";
|
||||
import {
|
||||
hideAllGuessOne,
|
||||
notesDataStore,
|
||||
occlusionMode,
|
||||
opacityStateStore,
|
||||
saveNeededStore,
|
||||
tagsWritable,
|
||||
|
|
@ -75,7 +75,7 @@ export const setupMaskEditorForEdit = async (
|
|||
const clozeNote = clozeNoteResponse.value.value;
|
||||
const canvas = initCanvas();
|
||||
|
||||
hideAllGuessOne.set(clozeNote.occludeInactive);
|
||||
occlusionMode.set(clozeNote.occlusionMode);
|
||||
|
||||
// get image width and height
|
||||
const image = document.getElementById("image") as HTMLImageElement;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import { ModuleName, setupI18n } from "@tslib/i18n";
|
|||
import { optimumPixelSizeForCanvas } from "./canvas-scale";
|
||||
import { Shape } from "./shapes";
|
||||
import { Ellipse, extractShapesFromRenderedClozes, Polygon, Rectangle, Text } from "./shapes";
|
||||
import { OcclusionMode } from "./store";
|
||||
import { SHAPE_MASK_COLOR, TEXT_BACKGROUND_COLOR, TEXT_FONT_FAMILY, TEXT_PADDING } from "./tools/lib";
|
||||
import type { Size } from "./types";
|
||||
|
||||
|
|
@ -168,10 +169,18 @@ async function setupImageOcclusionInner(setupOptions?: SetupImageOcclusionOption
|
|||
// setup button for toggle image occlusion
|
||||
const button = document.getElementById("toggle");
|
||||
if (button) {
|
||||
if (document.querySelector("[data-occludeinactive=\"1\"]")) {
|
||||
button.addEventListener("click", () => toggleMasks(setupOptions));
|
||||
} else {
|
||||
const hasHideAllMode = document.querySelector(`[data-occludeinactive="${OcclusionMode.HideAll}"]`);
|
||||
const hasHideAllButOneMode = document.querySelector(`[data-occludeinactive="${OcclusionMode.HideAllButOne}"]`);
|
||||
const hasAnyToggleableMode = hasHideAllMode || hasHideAllButOneMode;
|
||||
const isBackSide = document.querySelectorAll(".cloze-highlight").length > 0;
|
||||
|
||||
// Hide button if:
|
||||
// 1. No shapes with Hide All or Hide All But One modes, OR
|
||||
// 2. Hide All But One mode on the back side (no shapes to toggle)
|
||||
if (!hasAnyToggleableMode || (hasHideAllButOneMode && isBackSide)) {
|
||||
button.style.display = "none";
|
||||
} else {
|
||||
button.addEventListener("click", () => toggleMasks(setupOptions));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -202,35 +211,55 @@ function drawShapes(
|
|||
properties = processed.properties;
|
||||
}
|
||||
|
||||
for (const shape of activeShapes) {
|
||||
drawShape({
|
||||
context,
|
||||
size,
|
||||
shape,
|
||||
fill: properties.activeShapeColor,
|
||||
stroke: properties.activeBorder.color,
|
||||
strokeWidth: properties.activeBorder.width,
|
||||
});
|
||||
}
|
||||
for (const shape of inactiveShapes.filter((s) => s.occludeInactive)) {
|
||||
drawShape({
|
||||
context,
|
||||
size,
|
||||
shape,
|
||||
fill: shape.fill !== SHAPE_MASK_COLOR ? shape.fill : properties.inActiveShapeColor,
|
||||
stroke: properties.inActiveBorder.color,
|
||||
strokeWidth: properties.inActiveBorder.width,
|
||||
});
|
||||
}
|
||||
for (const shape of highlightShapes) {
|
||||
drawShape({
|
||||
context,
|
||||
size,
|
||||
shape,
|
||||
fill: properties.highlightShapeColor,
|
||||
stroke: properties.highlightShapeBorder.color,
|
||||
strokeWidth: properties.highlightShapeBorder.width,
|
||||
});
|
||||
// Determine occlusion mode from the first shape
|
||||
const occlusionMode = activeShapes[0]?.occlusionMode ?? inactiveShapes[0]?.occlusionMode ?? OcclusionMode.HideOne;
|
||||
|
||||
// HideOne: Draw active only (front), reveal answer with highlight (back)
|
||||
// HideAll: Draw both active and inactive (front & back)
|
||||
// HideAllButOne: Draw inactive only (front), draw nothing (back)
|
||||
|
||||
// Check if we're on the back side (highlightShapes only exist on back)
|
||||
const isBackSide = highlightShapes.length > 0;
|
||||
|
||||
// For HideAllButOne on the back side, draw nothing (show full unoccluded image)
|
||||
if (occlusionMode === OcclusionMode.HideAllButOne && isBackSide) {
|
||||
// Don't draw any shapes on the back for "Hide All But One" mode
|
||||
} else {
|
||||
// Normal drawing logic for all other cases
|
||||
if (occlusionMode !== OcclusionMode.HideAllButOne) {
|
||||
for (const shape of activeShapes) {
|
||||
drawShape({
|
||||
context,
|
||||
size,
|
||||
shape,
|
||||
fill: properties.activeShapeColor,
|
||||
stroke: properties.activeBorder.color,
|
||||
strokeWidth: properties.activeBorder.width,
|
||||
});
|
||||
}
|
||||
}
|
||||
if (occlusionMode === OcclusionMode.HideAll || occlusionMode === OcclusionMode.HideAllButOne) {
|
||||
for (const shape of inactiveShapes) {
|
||||
drawShape({
|
||||
context,
|
||||
size,
|
||||
shape,
|
||||
fill: shape.fill !== SHAPE_MASK_COLOR ? shape.fill : properties.inActiveShapeColor,
|
||||
stroke: properties.inActiveBorder.color,
|
||||
strokeWidth: properties.inActiveBorder.width,
|
||||
});
|
||||
}
|
||||
}
|
||||
for (const shape of highlightShapes) {
|
||||
drawShape({
|
||||
context,
|
||||
size,
|
||||
shape,
|
||||
fill: properties.highlightShapeColor,
|
||||
stroke: properties.highlightShapeBorder.color,
|
||||
strokeWidth: properties.highlightShapeBorder.width,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onDidDrawShapes?.({
|
||||
|
|
|
|||
|
|
@ -20,23 +20,23 @@ export class Shape {
|
|||
top: number;
|
||||
angle?: number; // polygons don't use it
|
||||
fill: string;
|
||||
/** Whether occlusions from other cloze numbers should be shown on the
|
||||
* question side. Used only in reviewer code.
|
||||
/** Occlusion mode: 0=HideOne, 1=HideAll, 2=HideAllButOne.
|
||||
* Used only in reviewer code.
|
||||
*/
|
||||
occludeInactive?: boolean;
|
||||
occlusionMode?: number;
|
||||
/* Cloze ordinal */
|
||||
ordinal: number | undefined;
|
||||
id: string | undefined;
|
||||
|
||||
constructor(
|
||||
{ left = 0, top = 0, angle = 0, fill = SHAPE_MASK_COLOR, occludeInactive, ordinal = undefined }:
|
||||
{ left = 0, top = 0, angle = 0, fill = SHAPE_MASK_COLOR, occlusionMode, ordinal = undefined }:
|
||||
ConstructorParams<Shape> = {},
|
||||
) {
|
||||
this.left = left;
|
||||
this.top = top;
|
||||
this.angle = angle;
|
||||
this.fill = fill;
|
||||
this.occludeInactive = occludeInactive;
|
||||
this.occlusionMode = occlusionMode;
|
||||
this.ordinal = ordinal;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ function extractShapeFromRenderedCloze(cloze: HTMLDivElement): Shape | null {
|
|||
return null;
|
||||
}
|
||||
const props = {
|
||||
occludeInactive: cloze.dataset.occludeinactive === "1",
|
||||
occlusionMode: cloze.dataset.occludeinactive ? parseInt(cloze.dataset.occludeinactive) : undefined,
|
||||
ordinal: parseInt(cloze.dataset.ordinal!),
|
||||
left: cloze.dataset.left,
|
||||
top: cloze.dataset.top,
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
import { fabric } from "fabric";
|
||||
import { cloneDeep } from "lodash-es";
|
||||
|
||||
import { OcclusionMode } from "../store";
|
||||
import { getBoundingBoxSize } from "../tools/lib";
|
||||
import type { Size } from "../types";
|
||||
import type { Shape, ShapeOrShapes } from "./base";
|
||||
|
|
@ -12,7 +13,7 @@ import { Polygon } from "./polygon";
|
|||
import { Rectangle } from "./rectangle";
|
||||
import { Text } from "./text";
|
||||
|
||||
export function exportShapesToClozeDeletions(occludeInactive: boolean): {
|
||||
export function exportShapesToClozeDeletions(mode: OcclusionMode): {
|
||||
clozes: string;
|
||||
noteCount: number;
|
||||
} {
|
||||
|
|
@ -76,7 +77,7 @@ export function exportShapesToClozeDeletions(occludeInactive: boolean): {
|
|||
clozes += shapeOrShapesToCloze(
|
||||
shapeOrShapes,
|
||||
ordinal,
|
||||
occludeInactive,
|
||||
mode,
|
||||
);
|
||||
|
||||
if (!(shapeOrShapes instanceof Text)) {
|
||||
|
|
@ -179,7 +180,7 @@ function fabricObjectToBaseShapeOrShapes(
|
|||
function shapeOrShapesToCloze(
|
||||
shapeOrShapes: ShapeOrShapes,
|
||||
ordinal: number,
|
||||
occludeInactive: boolean,
|
||||
mode: OcclusionMode,
|
||||
): string {
|
||||
let text = "";
|
||||
function addKeyValue(key: string, value: string) {
|
||||
|
|
@ -190,7 +191,7 @@ function shapeOrShapesToCloze(
|
|||
let type: string;
|
||||
if (Array.isArray(shapeOrShapes)) {
|
||||
return shapeOrShapes
|
||||
.map((shape) => shapeOrShapesToCloze(shape, ordinal, occludeInactive))
|
||||
.map((shape) => shapeOrShapesToCloze(shape, ordinal, mode))
|
||||
.join("");
|
||||
} else if (shapeOrShapes instanceof Rectangle) {
|
||||
type = "rect";
|
||||
|
|
@ -207,8 +208,8 @@ function shapeOrShapesToCloze(
|
|||
for (const [key, value] of Object.entries(shapeOrShapes.toDataForCloze())) {
|
||||
addKeyValue(key, value);
|
||||
}
|
||||
if (occludeInactive) {
|
||||
addKeyValue("oi", "1");
|
||||
if (mode !== OcclusionMode.HideOne) {
|
||||
addKeyValue("oi", mode.toString());
|
||||
}
|
||||
|
||||
text = `{{c${ordinal}::image-occlusion:${type}${text}}}<br>`;
|
||||
|
|
|
|||
|
|
@ -3,14 +3,20 @@
|
|||
|
||||
import { writable } from "svelte/store";
|
||||
|
||||
export enum OcclusionMode {
|
||||
HideOne = 0,
|
||||
HideAll = 1,
|
||||
HideAllButOne = 2,
|
||||
}
|
||||
|
||||
// it stores note's data for generate.ts, when function generate() is called it will be used to generate the note
|
||||
export const notesDataStore = writable({ id: "", title: "", divValue: "", textareaValue: "" }[0]);
|
||||
// it stores the tags for the note in note editor
|
||||
export const tagsWritable = writable([""]);
|
||||
// it stores the visibility of mask editor
|
||||
export const ioMaskEditorVisible = writable(true);
|
||||
// it store hide all or hide one mode
|
||||
export const hideAllGuessOne = writable(true);
|
||||
// it stores the occlusion mode (hide one, hide all, or hide all reveal one)
|
||||
export const occlusionMode = writable(OcclusionMode.HideAll);
|
||||
// ioImageLoadedStore is used to store the image loaded event
|
||||
export const ioImageLoadedStore = writable(false);
|
||||
// store opacity state of objects in canvas
|
||||
|
|
|
|||
Loading…
Reference in a new issue