mirror of
https://github.com/skishore/makemeahanzi.git
synced 2025-11-02 21:41:28 +08:00
Improve area calculation for glyph orientation
This commit is contained in:
39
lib/svg.js
39
lib/svg.js
@ -17,17 +17,40 @@ this.svg = {};
|
||||
|
||||
// Returns twice the area contained in the path. The result is positive iff the
|
||||
// path winds in the counter-clockwise direction.
|
||||
const get2xArea = (path) => {
|
||||
let area = 0;
|
||||
//
|
||||
// The approximation error is an upper-bound on the distance between consecutive
|
||||
// points in the polygon approximation used to compute the area. The default
|
||||
// error of 64 is chosen so that the sign will be correct with good likelihood.
|
||||
const get2xArea = (path, approximation_error) => {
|
||||
const polygon_approximation = [path[0].start];
|
||||
approximation_error = approximation_error || 64;
|
||||
for (let x of path) {
|
||||
area += (x.end[0] + x.start[0])*(x.end[1] - x.start[1]);
|
||||
let num_points = 0;
|
||||
if (x.control !== undefined) {
|
||||
const distance = Math.sqrt(Point.distance2(x.start, x.end));
|
||||
num_points = Math.floor(distance/approximation_error);
|
||||
}
|
||||
for (let i = 0; i < num_points; i++) {
|
||||
const t = (i + 1)/(num_points + 1);
|
||||
const s = 1 - t;
|
||||
polygon_approximation.push(
|
||||
[s*s*x.start[0] + 2*s*t*x.control[0] + t*t*x.end[0],
|
||||
s*s*x.start[1] + 2*s*t*x.control[1] + t*t*x.end[1]]);
|
||||
}
|
||||
polygon_approximation.push(x.end);
|
||||
}
|
||||
let area = 0;
|
||||
for (var i = 0; i < polygon_approximation.length; i++) {
|
||||
const p1 = polygon_approximation[i];
|
||||
const p2 = polygon_approximation[(i + 1) % polygon_approximation.length];
|
||||
area += (p2[0] + p1[0])*(p2[1] - p1[1]);
|
||||
}
|
||||
return area;
|
||||
}
|
||||
|
||||
// Takes a list of paths and orients them so that exterior contours are oriented
|
||||
// counter-clockwise and interior contours clockwise.
|
||||
const orientPaths = (paths, reverse) => {
|
||||
const orientPaths = (paths, approximation_error) => {
|
||||
for (var i = 0; i < paths.length; i++) {
|
||||
const path = paths[i];
|
||||
let contains = 0;
|
||||
@ -43,7 +66,7 @@ const orientPaths = (paths, reverse) => {
|
||||
// other paths. It is counter-clockwise iff its area is positive. The path
|
||||
// should be reversed if (CCW && internal) || (CW && external).
|
||||
const should_reverse = (area > 0) !== (contains % 2 === 0);
|
||||
if (should_reverse && !reverse) {
|
||||
if (should_reverse) {
|
||||
for (let segment of path) {
|
||||
[segment.start, segment.end] = [segment.end, segment.start];
|
||||
}
|
||||
@ -163,10 +186,8 @@ svg.convertCommandsToPath = (commands) => {
|
||||
// Converts a normal-form SVG path string to a list of paths. The paths obey an
|
||||
// orientation constraint: the external paths are oriented counter-clockwise,
|
||||
// while the internal paths are oriented clockwise.
|
||||
//
|
||||
// If 'reverse' is true, the paths will all be oriented opposite this rule.
|
||||
svg.convertSVGPathToPaths = (path, reverse) => {
|
||||
return orientPaths(splitPath(path), reverse);
|
||||
svg.convertSVGPathToPaths = (path) => {
|
||||
return orientPaths(splitPath(path));
|
||||
}
|
||||
|
||||
// Takes the given list of paths and returns a normal-form SVG path string.
|
||||
|
||||
@ -2,7 +2,23 @@
|
||||
|
||||
const completionCallback = undefined;
|
||||
|
||||
const perGlyphCallback = undefined;
|
||||
const perGlyphCallback = (glyph) => {
|
||||
assert(glyph.stages.strokes !== undefined);
|
||||
let changed = 0;
|
||||
for (var i = 0; i < glyph.stages.strokes.length; i++) {
|
||||
const stroke = glyph.stages.strokes[i];
|
||||
const paths = svg.convertSVGPathToPaths(stroke);
|
||||
assert(paths.length === 1);
|
||||
const path = svg.convertPathsToSVGPath(paths);
|
||||
if (path !== stroke) {
|
||||
glyph.stages.strokes[i] = path;
|
||||
changed += 1;
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
console.log(`Flipped ${changed} path(s) in glyph: ${glyph.character}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Runs the given per-glyph callback for each glyph in the database.
|
||||
// When all the glyphs are migrated, runs the completion callback.
|
||||
|
||||
Reference in New Issue
Block a user