pxart help DRAWING
Drawing
DRAWING (edits like EDITING: FILE[:SEL] draws on every selected frame, -o OUT, only changed rows
are rewritten; KEY is a palette key, or '.' to erase). Shapes are clipped to the frame (a
note says how many px fell outside); x,y may be negative. line, rect, poly, ellipse, arc and
flood print "painted N px" ("erased" for '.').
line FILE[:frame] KEY x0,y0 x1,y1 [--width N]
Bresenham: one pixel per step along the longer axis, 8-connected, no doubled corners;
the same pixels whichever end comes first (0,0 8,2 is three runs of 3). --width N
paints N px across the line (down for a mostly-horizontal line, right for a mostly-
vertical one), centered, the odd pixel down/right.
rect FILE[:frame] KEY x,y,w,h [--fill] a 1px border, or filled
poly FILE[:frame] KEY x,y x,y x,y ... [--fill]
A closed polygon through the points in order: line's pixels from each point to the
next and from the last back to the first (1px, the same whichever way round it goes).
--fill adds every pixel whose center is inside (nonzero winding: a self-crossing star
is solid). Two points are a line.
ellipse FILE[:frame] KEY cx,cy,rx,ry | --box x,y,w,h [--fill | --ring N]
The ellipse inscribed in the box cx-rx..cx+rx, cy-ry..cy+ry: 2*rx+1 wide, so a whole
center and radius give odd sizes (4,4,3,3 is 7x7) and both ending in .5 give even ones
(3.5,3.5,3.5,2.5 is 8x6 at 0,1; so is --box 0,1,8,6, rect's x,y,w,h: no halves). A thin
8-connected outline (Zingl's algorithm), mirror-symmetric, no stray pixels; boxes 1 or
2 px across are filled. --ring N: N px thick, inward (a halo).
arc FILE[:frame] KEY cx,cy,r a0,a1 [--width N]
Part of the circle ellipse cx,cy,r,r draws, from angle a0 to a1 in degrees, counter-
clockwise, 0 = right, 90 = up (0,90 is the upper-right quarter; 300,60 wraps through 0;
a1 - a0 >= 360 is the whole circle). A pixel is on the arc when the direction from the
center to it is in the range, ends included. --width N thickens it inward: a smear or
swoosh.
flood FILE[:frame] KEY x,y [--diagonal]
Bucket fill: repaint the region of x,y's key that touches x,y through sides (4-connected),
or corners too with --diagonal.
rotate FILE[:SEL] 90|180|270 [-o OUT] turn frames clockwise (a WxH frame becomes HxW)
transpose FILE[:SEL] [-o OUT] mirror across the top-left/bottom-right diagonal
For deriving path edges and corners from one tile. -o FILE:NEWGROUP turns a copy, as
flip's. The shading turns with the pixels (a note says where the light went), so
re-light with shade and outline --selective after.
shade FILE[:frame] --ramp d2,d1,base,l1[,l2] [--keys k1,k2] [--base K] [--light nw]
[--strength N] [--region x,y,w,h] [--dither] [--preview P.png]
Re-shade a material: the pixels whose key is in --keys (default: the ramp's keys, so a
shaded material re-shades) get ramp tones by how they face the light. The ramp runs
darkest to lightest; the base is its middle key (d2,d1,base,l1: the extra key goes
dark), or --base K. Keys are 'a,b,c' or 'abc'. Nothing outside the material changes.
--region x,y,w,h repaints only the material inside it, shaded as part of the whole
frame's material: the region's border is not an edge, only a real one is (an empty
pixel, another material, or the frame's side).
Algorithm, per pixel of the material (the shape: those pixels, over the whole frame;
everything else, and off the frame, is outside):
1. depth: its distance to the shape's edge (a vector distance transform from the
edge pixels, those with an outside side neighbor; ~Euclidean, 0 on the edge);
2. normal: which way is out from it: the pull of the outside pixels within depth + 4
px minus that of the shape's, 1/distance-weighted (a staircase reads as its slope);
3. lighting = (normal . light direction) * (1 - depth / strength), in -1..1: an edge
facing the light is +1, one facing away -1, and the value fades to 0 (the base
tone) --strength px in (default 2: a rim; the shape's radius: full form shading);
4. banding: 0..1 splits evenly over the lights, -1..0 over the darks, rounding to the
nearest step, ties toward the base; then a stray pixel (no 8-neighbor of its own
tone) takes its neighbors' commonest tone. No noise.
--dither mixes adjacent tones with a 4x4 ordered (Bayer) pattern where the lighting is
within a quarter step of a band boundary (and skips the stray-pixel pass). --light: n
ne e se s sw w nw (default nw). --preview P.png renders the result (render's grid and
rulers) and writes nothing else. Prints the pixels it changed, by the tone they got,
darkest first: "changed 24 px: 4->A, 8->B, 8->D, 4->E" (with --preview, "would change").
'shade hero.px:idle/0 --ramp XxcCw --keys c' shades the cloak c with the ramp X x c C w.
outline FILE[:frame] --key K [--outside | --inside] [--lit L [--selective]] [--light nw]
[--corners] [--preview P.png] [--min-alpha A] [--pad]
Outline the frame's shape (every pixel that draws in some variant; with --min-alpha A, at
alpha A+). --outside (the default) paints the empty pixels touching the shape on a side
(--pad: first grow the frame 1px where the shape meets its edge, pivots and all); --inside
repaints the shape's own pixels that have an empty side (off the frame is empty). Sides only
is the pixel-perfect rule: a diagonal edge gets a 1px staircase and a square corner is cut,
so no doubled (L-shaped) corners; --corners also takes the pixels touching only at a corner
(square corners, a 2px staircase). Selective outline (--lit L, or --selective --lit L):
outline pixels facing the light get L (a darker tone of the material), the rest K. Facing:
an outline pixel's outward normal (shade's, over 2px) dotted with the light's direction;
above 0 is lit, so a nw light lights the top and left edges and a 45-degree edge (ne, sw)
stays K. --light and --preview as for shade. Prints "changed 12 px: 6->o, 6->l" (by key; px
that had it already don't count).This page is built from
site/src/data/reference.json in the repository.Edit on GitHub