177 lines
7.0 KiB
TypeScript
177 lines
7.0 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
import type { ImageEditor } from "../handler/image-editor";
|
|
import type { TracerOptions } from "../interface";
|
|
import { NumericInputWithLabelTable } from "./numeric-labeld-input";
|
|
import styled from "styled-components";
|
|
import { Button } from "../styles";
|
|
import { FaBezierCurve } from "react-icons/fa6";
|
|
import { VerticalSlider } from "./vertical-slider";
|
|
import { TRANSPARENCY_THRESHOLD_DEFAULT } from "../svg-tracer";
|
|
import type { VectraceHandler } from "../handler/vectrace-handler";
|
|
import { clamp } from "lodash";
|
|
import { Hint } from "./tooltip";
|
|
|
|
const Table = styled.table`
|
|
padding-right: 4px;
|
|
`;
|
|
|
|
const WrapperColumn = styled.div`
|
|
display: flex;
|
|
flex-direction: column;
|
|
`;
|
|
|
|
const WrapperRow = styled.div`
|
|
display: flex;
|
|
flex-direction: row;
|
|
height: 100px;
|
|
`;
|
|
|
|
const LayerPanel = styled.div`
|
|
display: flex;
|
|
flex-direction: column;
|
|
width: 100px;
|
|
overflow: auto;
|
|
height: 97px;
|
|
`;
|
|
|
|
const ToggleButton = styled.button<{ $enabled: boolean }>`
|
|
background-color: ${({ $enabled }) => ($enabled ? "var(--secondary-color)" : "var(--primary-color)")};
|
|
color: white;
|
|
border: 1px solid white;
|
|
&:hover {
|
|
color: var(--secondary-color);
|
|
background-color: white;
|
|
}
|
|
`;
|
|
|
|
export function SvgTracerOptions({ selected, handler }: { selected: ImageEditor[]; handler: VectraceHandler }) {
|
|
const [options, setOptions] = useState(selected[0]?.tracerOptions ?? {});
|
|
const [updated, setUpdated] = useState(false);
|
|
|
|
useEffect(() => {
|
|
setOptions(selected[0]?.tracerOptions ?? {});
|
|
setUpdated(true);
|
|
}, [selected.map((e) => `${e.id}${JSON.stringify(e.svgData.options)}`).join("")]);
|
|
|
|
const redraw = () => {
|
|
selected.forEach((e) => {
|
|
e.setTracerSvg({ ...options });
|
|
});
|
|
setUpdated(true);
|
|
};
|
|
|
|
const setValue = (key: keyof TracerOptions, value: any) => {
|
|
setOptions({ ...options, [key]: value });
|
|
setUpdated(false);
|
|
};
|
|
|
|
return (
|
|
<WrapperRow>
|
|
<Hint
|
|
hint={
|
|
"Transparency cutoff — pixels more transparent than this value are ignored when tracing. " +
|
|
"Lower = only opaque pixels traced, Higher = transparent pixels included in the path."
|
|
}
|
|
>
|
|
<VerticalSlider
|
|
max={255}
|
|
min={0}
|
|
value={options.transparencyThreshold ?? TRANSPARENCY_THRESHOLD_DEFAULT}
|
|
onLabelClick={async () => {
|
|
const value = await handler.popup.prompt(
|
|
"Transparency threshold",
|
|
"Enter transparency threshold between 0-255",
|
|
(options.transparencyThreshold ?? TRANSPARENCY_THRESHOLD_DEFAULT).toString(),
|
|
);
|
|
if (value) {
|
|
const int = parseInt(value, 10);
|
|
if (!isNaN(int)) {
|
|
setValue("transparencyThreshold", clamp(int, 0, 255));
|
|
}
|
|
}
|
|
}}
|
|
onChange={(value) => {
|
|
setValue("transparencyThreshold", value);
|
|
}}
|
|
/>
|
|
</Hint>
|
|
<WrapperColumn>
|
|
<Button
|
|
$active={!updated}
|
|
onClick={() => {
|
|
redraw();
|
|
}}
|
|
>
|
|
<span>
|
|
<span>Redraw</span>
|
|
<FaBezierCurve style={{ paddingLeft: 10 }} />
|
|
</span>
|
|
</Button>
|
|
<WrapperRow>
|
|
<Table>
|
|
<tbody>
|
|
<Hint
|
|
hint={
|
|
"Line threshold — how closely straight lines must follow the original shape. Lower = more accurate, more points."
|
|
}
|
|
>
|
|
<NumericInputWithLabelTable
|
|
name="Ltres"
|
|
value={options.ltres}
|
|
onChange={(value) => setValue("ltres", value)}
|
|
/>
|
|
</Hint>
|
|
<Hint
|
|
hint={
|
|
"Curve threshold — how closely curves must follow the original shape. Lower = smoother curves, more points."
|
|
}
|
|
>
|
|
<NumericInputWithLabelTable
|
|
name="Qtres"
|
|
value={options.qtres}
|
|
onChange={(value) => setValue("qtres", value)}
|
|
/>
|
|
</Hint>
|
|
<Hint
|
|
hint={
|
|
"Ignore tiny paths smaller than this many pixels. Higher = cleaner output, but small details get dropped."
|
|
}
|
|
>
|
|
<NumericInputWithLabelTable
|
|
name="Path omit"
|
|
value={options.pathomit}
|
|
onChange={(value) => setValue("pathomit", value)}
|
|
/>
|
|
</Hint>
|
|
<Hint hint={"Decimal precision of path coordinates. Lower = smaller file, slightly less accurate."}>
|
|
<NumericInputWithLabelTable
|
|
name="Round corners"
|
|
value={options.roundcoords}
|
|
onChange={(value) => setValue("roundcoords", value)}
|
|
/>
|
|
</Hint>
|
|
</tbody>
|
|
</Table>
|
|
<LayerPanel>
|
|
{(options.allowedPaths || []).map((e, i) => {
|
|
return (
|
|
<ToggleButton
|
|
key={i}
|
|
$enabled={e}
|
|
onClick={() => {
|
|
const array = options?.allowedPaths ? [...options.allowedPaths] : [];
|
|
array[i] = !array[i];
|
|
setValue("allowedPaths", array);
|
|
}}
|
|
>
|
|
{`${i + 1} Layer`}
|
|
</ToggleButton>
|
|
);
|
|
})}
|
|
</LayerPanel>
|
|
</WrapperRow>
|
|
</WrapperColumn>
|
|
</WrapperRow>
|
|
);
|
|
}
|