| @@ -372,10 +372,11 @@ | ||
| 372 | 372 | |
| 373 | 373 | // The shipped defaults write colours as CSS hex strings because |
| 374 | 374 | // that is what reads well in a config array; a clamped look carries |
| 375 | 375 | // them as ints. Normalise so this draws the same either way. |
| 376 | - $appearance['bodyColor'] = openstation_mio_color_int( $appearance['bodyColor'] ); | |
| 377 | - $appearance['eyeColor'] = openstation_mio_color_int( $appearance['eyeColor'] ); | |
| 376 | + $appearance['bodyColor'] = openstation_mio_color_int( $appearance['bodyColor'] ); | |
| 377 | + $appearance['eyeColor'] = openstation_mio_color_int( $appearance['eyeColor'] ); | |
| 378 | + $appearance['linerColor'] = openstation_mio_color_int( $appearance['linerColor'] ); | |
| 378 | 379 | |
| 379 | 380 | // Only [A-Za-z0-9_-] survives, so a caller cannot close the |
| 380 | 381 | // attribute and write markup through this parameter. |
| 381 | 382 | $uid = preg_replace( '/[^A-Za-z0-9_-]/', '', (string) $id_suffix ); |
| @@ -380,13 +381,16 @@ | ||
| 380 | 381 | // attribute and write markup through this parameter. |
| 381 | 382 | $uid = preg_replace( '/[^A-Za-z0-9_-]/', '', (string) $id_suffix ); |
| 382 | 383 | $ring_id = 'r' . $uid; |
| 383 | 384 | $shape_id = 's' . $uid; |
| 385 | + $clip_id = 'c' . $uid; | |
| 384 | 386 | |
| 385 | 387 | // Work on a canonical 100-unit radius and let the viewBox scale it, |
| 386 | 388 | // so the same path serves a 24px avatar and a 176px hero. |
| 387 | 389 | $radius = 100.0; |
| 388 | - $stroke = $appearance['outlineWidth'] * ( $radius / $defaults['appearance']['radius'] ); | |
| 390 | + $scale = $radius / $defaults['appearance']['radius']; | |
| 391 | + $stroke = $appearance['outlineWidth'] * $scale; | |
| 392 | + $liner = $appearance['linerWidth'] * $scale; | |
| 389 | 393 | $reach = ( $appearance['glow'] / 10.0 ) * $radius * 0.18; |
| 390 | 394 | $shells = openstation_mio_portrait_glow_shells(); |
| 391 | 395 | $half = $radius * openstation_mio_portrait_extent( $physics ) |
| 392 | 396 | + $stroke / 2.0 |
| @@ -427,16 +431,36 @@ | ||
| 427 | 431 | . ' rx="' . openstation_mio_portrait_fix( $eye_w / 2.0 ) . '"' |
| 428 | 432 | . ' fill="' . openstation_mio_portrait_hex( $appearance['eyeColor'] ) . '"/>'; |
| 429 | 433 | }; |
| 430 | 434 | |
| 435 | + // The inner line, clipped to the body. | |
| 436 | + // | |
| 437 | + // SVG strokes are centred on their path and cannot be offset to one | |
| 438 | + // side, so the line is drawn at the full width it would need if it | |
| 439 | + // reached both ways — `stroke + liner * 2` — and the clip throws | |
| 440 | + // the outer half away. What is left runs from the outline inward, | |
| 441 | + // and the chroma stroke below is painted over its inner reach, so | |
| 442 | + // the visible white is exactly the band between the two. That is | |
| 443 | + // the same geometry `fillLiner()` produces in the live renderer, by | |
| 444 | + // the only means SVG offers. | |
| 445 | + $line = ''; | |
| 446 | + if ( $liner > 0 ) { | |
| 447 | + $line = '<use href="#' . $shape_id . '" fill="none"' | |
| 448 | + . ' stroke="' . openstation_mio_portrait_hex( $appearance['linerColor'] ) . '"' | |
| 449 | + . ' stroke-width="' . openstation_mio_portrait_fix( $stroke + $liner * 2.0 ) . '"' | |
| 450 | + . ' stroke-linejoin="round" clip-path="url(#' . $clip_id . ')"/>'; | |
| 451 | + } | |
| 452 | + | |
| 431 | 453 | return '<svg xmlns="http://www.w3.org/2000/svg" width="' . (int) $size . '" height="' . (int) $size . '"' |
| 432 | 454 | . ' viewBox="-' . $box . ' -' . $box . ' ' . $span . ' ' . $span . '">' |
| 433 | 455 | . '<defs><linearGradient id="' . $ring_id . '" x1="0" y1="0" x2="0.85" y2="1">' . $stops . '</linearGradient>' |
| 434 | - . '<path id="' . $shape_id . '" d="' . $d . '"/></defs>' | |
| 456 | + . '<path id="' . $shape_id . '" d="' . $d . '"/>' | |
| 457 | + . '<clipPath id="' . $clip_id . '"><use href="#' . $shape_id . '"/></clipPath></defs>' | |
| 435 | 458 | . $glow |
| 436 | 459 | . '<use href="#' . $shape_id . '" fill="' . openstation_mio_portrait_hex( $appearance['bodyColor'] ) . '"' |
| 437 | - . ' fill-opacity="' . openstation_mio_portrait_fix( $appearance['bodyAlpha'] ) . '"' | |
| 438 | - . ' stroke="url(#' . $ring_id . ')"' | |
| 460 | + . ' fill-opacity="' . openstation_mio_portrait_fix( $appearance['bodyAlpha'] ) . '"/>' | |
| 461 | + . $line | |
| 462 | + . '<use href="#' . $shape_id . '" fill="none" stroke="url(#' . $ring_id . ')"' | |
| 439 | 463 | . ' stroke-width="' . openstation_mio_portrait_fix( $stroke ) . '" stroke-linejoin="round"/>' |
| 440 | 464 | . $eye( -$eye_gap ) |
| 441 | 465 | . $eye( $eye_gap ) |
| 442 | 466 | . '</svg>'; |