Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
13 changes: 9 additions & 4 deletions src/art/exportSvg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
PADDED_VIEWBOX,
PADDING,
PUPIL_PATH_IDS,
SCLERA_PATH_IDS,
SPOT_PATHS,
type SlorgAppearance,
type SlorgPath,
Expand Down Expand Up @@ -41,7 +42,8 @@ function imageToDataUrl(image: HTMLImageElement): string {
}

export function buildSlorgSvg(image: HTMLImageElement | null, transform: ImageTransform, options: SlorgAppearance): string {
const { backgroundColors, colorGradientAngle, colorOpacity, colorLayer, madEyes, spots, spotColor, spotOpacity } = options
const { backgroundColors, colorGradientAngle, colorOpacity, colorLayer, madEyes, spots, spotColor, spotOpacity, irisColor, scleraColor } =
options
const [oa, ob, oc, od, oe, of] = BODY_OUTLINE.transform
const outlineStroke = BODY_OUTLINE.stroke ?? "#000000"
const outlineWidth = BODY_OUTLINE.strokeWidth ?? 1
Expand Down Expand Up @@ -95,10 +97,13 @@ export function buildSlorgSvg(image: HTMLImageElement | null, transform: ImageTr

const eyesMarkup = EYE_PATHS.filter((p) => !(madEyes && HIGHLIGHT_PATH_IDS.has(p.id)))
.map((p) => {
const isPupil = madEyes && PUPIL_PATH_IDS.has(p.id)
const spec = isPupil && p.id in MAD_PUPIL_TRANSFORMS ? { ...p, transform: MAD_PUPIL_TRANSFORMS[p.id] } : p
const isPupil = PUPIL_PATH_IDS.has(p.id)
const isSclera = SCLERA_PATH_IDS.has(p.id)
const useMadTransform = madEyes && isPupil && p.id in MAD_PUPIL_TRANSFORMS
const spec = useMadTransform ? { ...p, transform: MAD_PUPIL_TRANSFORMS[p.id] } : p
const fill = madEyes && isPupil ? MAD_PUPIL_FILL : isPupil ? irisColor : isSclera ? scleraColor : undefined
// Mad Eyes has no outline on the sclera or pupils, unlike the normal eyes.
return pathTag(spec, madEyes ? { fill: isPupil ? MAD_PUPIL_FILL : undefined, suppressStroke: true } : undefined)
return pathTag(spec, { fill, suppressStroke: madEyes })
})
.join("")

Expand Down
14 changes: 11 additions & 3 deletions src/art/renderSlorg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import {
BODY_BBOX,
BODY_OUTLINE,
DEFAULT_GRADIENT_ANGLE,
DEFAULT_IRIS_COLOR,
DEFAULT_SCLERA_COLOR,
EYE_PATHS,
HIGHLIGHT_PATH_IDS,
MAD_EYEBROW_PATHS,
Expand All @@ -12,6 +14,7 @@ import {
PADDED_VIEWBOX,
PADDING,
PUPIL_PATH_IDS,
SCLERA_PATH_IDS,
SPOT_COLOR,
SPOT_OPACITY,
SPOT_PATHS,
Expand Down Expand Up @@ -117,6 +120,8 @@ export function renderSlorg(ctx: CanvasRenderingContext2D, options: RenderOption
spots = true,
spotColor = SPOT_COLOR,
spotOpacity = SPOT_OPACITY,
irisColor = DEFAULT_IRIS_COLOR,
scleraColor = DEFAULT_SCLERA_COLOR,
} = options

const viewboxToCanvasScale = canvasWidth / PADDED_VIEWBOX.width
Expand Down Expand Up @@ -186,10 +191,13 @@ export function renderSlorg(ctx: CanvasRenderingContext2D, options: RenderOption
if (madEyes) for (const path of MAD_TEAR_PATHS) drawSlorgPath(ctx, path)
for (const path of EYE_PATHS) {
if (madEyes && HIGHLIGHT_PATH_IDS.has(path.id)) continue
const isPupil = madEyes && PUPIL_PATH_IDS.has(path.id)
const spec = isPupil && path.id in MAD_PUPIL_TRANSFORMS ? { ...path, transform: MAD_PUPIL_TRANSFORMS[path.id] } : path
const isPupil = PUPIL_PATH_IDS.has(path.id)
const isSclera = SCLERA_PATH_IDS.has(path.id)
const useMadTransform = madEyes && isPupil && path.id in MAD_PUPIL_TRANSFORMS
const spec = useMadTransform ? { ...path, transform: MAD_PUPIL_TRANSFORMS[path.id] } : path
const fill = madEyes && isPupil ? MAD_PUPIL_FILL : isPupil ? irisColor : isSclera ? scleraColor : undefined
// Mad Eyes has no outline on the sclera or pupils, unlike the normal eyes.
drawSlorgPath(ctx, spec, madEyes ? { fill: isPupil ? MAD_PUPIL_FILL : undefined, suppressStroke: true } : undefined)
drawSlorgPath(ctx, spec, { fill, suppressStroke: madEyes })
}
// Eyebrows sit in front of the eye white, so they're drawn last.
if (madEyes) for (const path of MAD_EYEBROW_PATHS) drawSlorgPath(ctx, path)
Expand Down
11 changes: 11 additions & 0 deletions src/art/slorgArt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export interface SlorgAppearance {
spots: boolean
spotColor: string
spotOpacity: number
irisColor: string
scleraColor: string
}

const VIEWBOX = { width: 170.66667, height: 170.66667 }
Expand Down Expand Up @@ -120,12 +122,21 @@ export const EYE_PATHS: SlorgPath[] = [
/** IDs of the pupil paths within EYE_PATHS, so the renderer can swap their fill in Mad Eyes mode. */
export const PUPIL_PATH_IDS = new Set(["path25", "path27"])

/** IDs of the sclera ("eye white") paths within EYE_PATHS, so the renderer can swap their fill. */
export const SCLERA_PATH_IDS = new Set(["path23", "path24"])

/** IDs of the pupil highlight-dot paths within EYE_PATHS, hidden in Mad Eyes mode. */
export const HIGHLIGHT_PATH_IDS = new Set(["path26", "path28"])

/** Iris color used for the pupils when Mad Slorg Eyes is enabled. */
export const MAD_PUPIL_FILL = "#4a2f00"

/** Default iris (pupil) color for normal (non-Mad-Eyes) rendering. */
export const DEFAULT_IRIS_COLOR = "#000000"

/** Default sclera (eye white) color. */
export const DEFAULT_SCLERA_COLOR = "#ffffff"

/**
* Pupil transforms used only in Mad Slorg Eyes mode: same shape/size as the normal pupils
* (path25/path27), but re-centered within each eye white's sclera circle rather than the normal
Expand Down
24 changes: 23 additions & 1 deletion src/features/slorg-editor/CharacterSection.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Collapsible, HStack, Switch } from "@chakra-ui/react"
import { Collapsible, HStack, Switch, Text } from "@chakra-ui/react"
import { Section } from "../../components/ui/Section"
import { OpacitySlider } from "../../components/ui/OpacitySlider"
import { ColorSwatchInput } from "../../components/ui/ColorSwatchInput"
Expand All @@ -12,6 +12,10 @@ interface CharacterSectionProps {
onSpotColorChange: (color: string) => void
spotOpacity: number
onSpotOpacityChange: (value: number) => void
irisColor: string
onIrisColorChange: (color: string) => void
scleraColor: string
onScleraColorChange: (color: string) => void
}

export function CharacterSection({
Expand All @@ -23,6 +27,10 @@ export function CharacterSection({
onSpotColorChange,
spotOpacity,
onSpotOpacityChange,
irisColor,
onIrisColorChange,
scleraColor,
onScleraColorChange,
}: CharacterSectionProps) {
return (
<Section title="Character">
Expand Down Expand Up @@ -51,6 +59,20 @@ export function CharacterSection({
<OpacitySlider label="Spot opacity" value={spotOpacity} onChange={onSpotOpacityChange} />
</Collapsible.Content>
</Collapsible.Root>

<HStack justify="space-between">
<Text fontSize="xs" color="fg.muted">
Iris
</Text>
<ColorSwatchInput value={irisColor} onChange={onIrisColorChange} />
</HStack>

<HStack justify="space-between">
<Text fontSize="xs" color="fg.muted">
Eye white
</Text>
<ColorSwatchInput value={scleraColor} onChange={onScleraColorChange} />
</HStack>
</Section>
)
}
6 changes: 6 additions & 0 deletions src/features/slorg-editor/SlorgCanvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ export function SlorgCanvas({
spots,
spotColor,
spotOpacity,
irisColor,
scleraColor,
size = 400,
}: SlorgCanvasProps) {
const canvasRef = useRef<HTMLCanvasElement>(null)
Expand All @@ -51,6 +53,8 @@ export function SlorgCanvas({
spots,
spotColor,
spotOpacity,
irisColor,
scleraColor,
})
}, [
image,
Expand All @@ -63,6 +67,8 @@ export function SlorgCanvas({
spots,
spotColor,
spotOpacity,
irisColor,
scleraColor,
size,
])

Expand Down
19 changes: 18 additions & 1 deletion src/features/slorg-editor/SlorgEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,14 @@ import { CharacterSection } from "./CharacterSection"
import { Section } from "../../components/ui/Section"
import { ExportButton } from "./ExportButton"
import { DEFAULT_TRANSFORM, type ImageTransform } from "../../art/coverFit"
import { DEFAULT_BODY_COLOR, DEFAULT_GRADIENT_ANGLE, SPOT_COLOR, SPOT_OPACITY } from "../../art/slorgArt"
import {
DEFAULT_BODY_COLOR,
DEFAULT_GRADIENT_ANGLE,
DEFAULT_IRIS_COLOR,
DEFAULT_SCLERA_COLOR,
SPOT_COLOR,
SPOT_OPACITY,
} from "../../art/slorgArt"

export function SlorgEditor() {
const [image, setImage] = useState<HTMLImageElement | null>(null)
Expand All @@ -19,6 +26,8 @@ export function SlorgEditor() {
const [spots, setSpots] = useState(true)
const [spotColor, setSpotColor] = useState(SPOT_COLOR)
const [spotOpacity, setSpotOpacity] = useState(SPOT_OPACITY)
const [irisColor, setIrisColor] = useState(DEFAULT_IRIS_COLOR)
const [scleraColor, setScleraColor] = useState(DEFAULT_SCLERA_COLOR)

function handleImageReady(nextImage: HTMLImageElement) {
setImage(nextImage)
Expand All @@ -40,6 +49,8 @@ export function SlorgEditor() {
spots={spots}
spotColor={spotColor}
spotOpacity={spotOpacity}
irisColor={irisColor}
scleraColor={scleraColor}
/>

<Section title="Export">
Expand All @@ -54,6 +65,8 @@ export function SlorgEditor() {
spots={spots}
spotColor={spotColor}
spotOpacity={spotOpacity}
irisColor={irisColor}
scleraColor={scleraColor}
/>
</Section>
</Stack>
Expand Down Expand Up @@ -83,6 +96,10 @@ export function SlorgEditor() {
onSpotColorChange={setSpotColor}
spotOpacity={spotOpacity}
onSpotOpacityChange={setSpotOpacity}
irisColor={irisColor}
onIrisColorChange={setIrisColor}
scleraColor={scleraColor}
onScleraColorChange={setScleraColor}
/>
</Stack>
</Flex>
Expand Down
Loading