Inflection cleanup improvements. LGTM at least. :/

This commit is contained in:
be5invis 2015-12-20 19:35:45 +08:00
parent 822d0dd285
commit 5df9fd6e50
6 changed files with 106 additions and 59 deletions

View File

@ -287,6 +287,7 @@ define [halfXStrand _leftx lefty rightx righty turn straight tension _fine] : gl
quadcontrols 0 ((cyleft - turnyleft) / (straightyleft - turnyleft)) 24
flat straightxleft straightyleft
curl rightx righty
end [function : set this.cleanmore true]
define [xStrand _leftx lefty _rightx righty turn straight tension] : glyph-construction

View File

@ -433,20 +433,21 @@ define VShape : symbol-block 'V, v and nu'
widths.rhs
straight.left.start (RIGHTSB + HOOKX / 3) (top - STROKE)
g4 (RIGHTSB - HOOKX / 3) (top - HALFSTROKE - HOOK)
quadcontrols 0.4 0.75 10 unimportant g2
quadcontrols 0.4 0.75 64 unimportant
g4 (MIDDLE + cornerdist) 0 [widths.rhs VShape.fine]
#end [function : set this.dense true]
create-glyph 'vhooktop' : glyph-construction
assign-unicode 0x2C71
include eMarks
include : VHooktopShape XH
create-glyph 'cyrIzhitsa' : glyph-construction
assign-unicode 0x474
include capitalMarks
include : VHooktopShape CAP
alias 'cyrizhitsa' 0x475 'vhooktop'
turned 'turnv' 0x28C 'v' MIDDLE (XH / 2)
create-glyph 'nu' : glyph-construction
@ -655,7 +656,7 @@ symbol-block 'X'
include : AIHSerifs CAP
alias 'Chi' 0x3A7 'X'
alias 'cyrHa' 0x425 'X'
###console.log 'x'
create-glyph 'x' : glyph-construction
include eMarks
assign-unicode 'x'
@ -664,7 +665,7 @@ symbol-block 'X'
include : xStrand SB 0 RIGHTSB XH 0.02 0.4 0.14
include : xStrand SB XH RIGHTSB 0 0.02 0.4 0.14
include : AIHSerifs XH
#if [not recursive] : throw 'xxx'
create-glyph 'chi' : glyph-construction
set-width WIDTH
assign-unicode 0x3C7
@ -707,8 +708,9 @@ symbol-block 'Y'
widths.rhs
straight.left.start (RIGHTSB + HOOKX / 3) (CAP - STROKE)
g4 (RIGHTSB - HOOKX / 3) (CAP - HOOK)
quadcontrols 0.55 0.7 8 unimportant
g4 (MIDDLE + STROKE / 2 * HVCONTRAST) cross
quadcontrols 0.55 0.7 32 unimportant
g4 (MIDDLE + STROKE / 2 * HVCONTRAST) cross
end [function : set this.loose true]
create-glyph 'cyrue' : glyph-construction
assign-unicode 0x4AF
@ -723,13 +725,16 @@ symbol-block 'Y'
### y
symbol-block 'y'
local px1 0.84
local py1 0.76
local py1 : linreg 18 0.8 126 0.76 STROKE
local px2 0.95
local py2 : if SLAB [linreg 18 0.97 126 0.85 STROKE] 0.88
local py20 0.88
local py2 : if SLAB [linreg 18 0.97 126 0.85 STROKE] py20
local pds 0.05
local pds2 0.01
local dpy1 : (1 - [linreg (1 - px2) (1 - py2) px1 py1 1]) / (1 - py1)
local dpy20 : (1 - [linreg (1 - px1) (1 - py1) px2 py20 1]) / (1 - py20)
local dpy2 : (1 - [linreg (1 - px1) (1 - py1) px2 py2 1]) / (1 - py2)
local yrstrokel0 : MIDDLE - WIDTH * 0.1
local yrstrokel : MIDDLE - WIDTH * [if SLAB 0.15 0.1] + [if SLAB HALFSTROKE 0]
local yrstroker : RIGHTSB - HALFSTROKE * HVCONTRAST
local yshrink : linreg 18 1 126 0.85 STROKE
@ -758,13 +763,15 @@ symbol-block 'y'
curl yrstroker (top - ds) [heading DOWNWARD]
quadcontrols 0 dpy1 16
yBaseKnots top bottom
end [function : set this.cleanmore true]
include : dispiro
widths.center
flat (WIDTH - yrstroker) top [heading DOWNWARD]
curl (WIDTH - yrstroker) (top - ds) [heading DOWNWARD]
quadcontrols 0 dpy1 16
flat [mix (WIDTH - yrstrokel) (WIDTH - yrstroker) px1] [mix (bottom + ds) (top - ds) py1]
flat [mix (WIDTH - yrstrokel) (WIDTH - yrstroker) px1] [mix (bottom + ds2) (top - ds) py1]
curl MIDDLE [mix (bottom + ds2) (top - ds) [linreg (1 - px2) (1 - py2) px1 py1 ((MIDDLE - yrstrokel) / (yrstroker - yrstrokel))]] [widths.center (STROKE * yshrink)]
end [function : set this.cleanmore true]
if SLAB : begin
include : AIVSerifs top
create-glyph 'y.upright' : glyph-construction
@ -803,6 +810,7 @@ symbol-block 'y'
widths.center
straight.left.start ([mix yrstrokel yrstroker px1] + HOOKX) (XH - HALFSTROKE)
yBaseKnots XH DESCENDER
end [function : set this.cleanmore true]
create-glyph 'cyrU' : glyph-construction
assign-unicode 0x423
@ -812,14 +820,30 @@ symbol-block 'y'
create-glyph 'lambda' : glyph-construction
assign-unicode 0x3BB
include bMarks
local top CAP
local bottom 0
local ds : (top - bottom) * pds
local ds2 : (top - bottom) * pds2
include : tagged 'strokeR' : dispiro
widths.center
flat yrstroker bottom [heading UPWARD]
curl yrstroker (bottom + ds) [heading UPWARD]
quadcontrols 0 dpy1 16
flat [mix yrstroker yrstrokel0 (1 - px1)] [mix (top - ds2) (bottom + ds) py1]
curl [mix yrstroker yrstrokel0 px2] [mix (top - ds2) (bottom + ds) (1 - py20)]
quadcontrols 1 (1 - dpy20) 16
flat yrstrokel0 (top - ds2) [heading UPWARD]
curl yrstrokel0 top [heading UPWARD]
end [function : set this.cleanmore true]
local xTop : mix SB RIGHTSB 0.28
local turnp : XH / (XH - DESCENDER)
local xb : mix SB RIGHTSB 0.51
local yb : mix XH 0 (0.05 * turnp)
include : xStrand xTop CAP RIGHTSB 0 0.1 0.6 0.14
include : halfXStrand SB 0 xb yb (0.1 * turnp) 0.4 (0.14 * turnp)
include : dispiro
widths.center
flat (WIDTH - yrstroker) bottom [heading UPWARD]
curl (WIDTH - yrstroker) (bottom + ds) [heading UPWARD]
quadcontrols 0 dpy1 16
flat [mix (WIDTH - yrstrokel0) (WIDTH - yrstroker) px1] [mix (top - ds2) (bottom + ds) py1]
curl MIDDLE [mix (top - ds2) (bottom + ds) [linreg (1 - px2) (1 - py20) px1 py1 ((MIDDLE - yrstrokel0) / (yrstroker - yrstrokel0))]]
end [function : set this.cleanmore true]
create-glyph 'lambdaslash' : glyph-construction
assign-unicode 0x19B
@ -1550,7 +1574,6 @@ define {CShape} : symbol-block 'C'
arcvh
flat MIDDLE (XH - HALFSTROKE - O) [widths HALFSTROKE 0]
curl (MIDDLE - 1) (XH - HALFSTROKE - O)
create-glyph 'sigmafinal' : glyph-construction
assign-unicode 0x3C2
include pMarks
@ -1696,7 +1719,6 @@ symbol-block 'O'
assign-unicode 'o'
include eMarks
include : OShape XH 0 SB RIGHTSB nothing nothing nothing true
alias 'omicron' 0x3BF 'o'
alias 'cyro' 0x43e 'o'

View File

@ -194,6 +194,7 @@ symbol-block 'Delta and cyrbe'
curl (SB + OX) (XH - SMALLSMOOTHA)
alsothruthem [list {0.25 0.79} {0.5 0.87}] important
g4 (RIGHTSB - HALFSTROKE * HVCONTRAST) CAP
#end : function : set this.dense true
symbol-block 'epsilon and cyrze'
define [SmallEpsilonShape top hook] : glyph-construction

View File

@ -108,8 +108,8 @@ symbol-block 'Above marks'
local top : ttop + (markFine * 2)
local bot : tbot - (markFine * 2)
local tildeWave [linreg 16 2.5 90 1.36 (markHalfStroke * 2)]
local tildeWaveX 0.52
local tildeWave [linreg 16 2.6 90 1.36 (markHalfStroke * 2)]
local tildeWaveX [linreg 16 0.5 90 0.52 (markHalfStroke * 2)]
local tildeWaveEnd : [smoothreg {16 60 90} {(-0.025) (-0.05) 0}] (markHalfStroke * 2)
include : dispiro
@ -118,9 +118,9 @@ symbol-block 'Above marks'
bezcontrols.absolute
* [mix leftEnd rightEnd tildeWaveX]; * [mix bot top tildeWave]
* [mix leftEnd rightEnd (1 - tildeWaveX)]; * [mix bot top (1 - tildeWave)]
* 128; * important; * g2
* 256; * important; * g2
g2 rightEnd [mix tbot ttop (1 - tildeWaveEnd)]
end [function : set this.dense true]
#end [function : set this.dense true]
set this.dense true

View File

@ -2,10 +2,12 @@ var Transform = require('./transform.js');
var ANGLES = 12;
var VERYCROWD = 2;
var SMALLANGLE = 0.1;
var SMALLANGLE = 0.05;
var SMALLANGLE_CLEANMORE = 0.075;
var CROWD = 4;
var LOOSE = 6;
var SMALL = 1e-4
var CLOSE = 1;
function cross(z1, z2, z3){
return (z2.x - z1.x) * (z3.y - z1.y) - (z3.x - z1.x) * (z2.y - z1.y)
};
@ -161,24 +163,32 @@ function fitpts(p1, c1, c2, p2){
}
return [mix(mid, p1, 1/3), mix(mid, p2, 1/3)]
};
function distance(z1, z2){
return Math.sqrt((z1.x - z2.x) * (z1.x - z2.x) + (z1.y - z2.y) * (z1.y - z2.y))
}
function veryclose(z1, z2){
return (z1.x - z2.x) * (z1.x - z2.x) + (z1.y - z2.y) * (z1.y - z2.y) <= SMALL
};
function angleBetween(z1, z2, z3, z4){
return Math.atan2(z2.y - z1.y, z2.x - z1.x) - Math.atan2(z4.y - z3.y, z4.x - z3.x);
return (Math.atan2(z2.y - z1.y, z2.x - z1.x) - Math.atan2(z4.y - z3.y, z4.x - z3.x)) % Math.PI;
};
function pldistance(z1, z2, z){
return Math.abs((z2.y - z1.y) * z.x - (z2.x - z1.x) * z.y + z2.x * z1.y - z2.y * z1.x) / Math.sqrt((z2.x - z1.x) * (z2.x - z1.x) + (z2.y - z1.y) * (z2.y - z1.y))
}
function estimateSegments(z1, z2){
var hspan = Math.abs(z1.x - z2.x);
var vspan = Math.abs(z1.y - z2.y);
return hspan <= 5 * CROWD || vspan <= 5 * CROWD ? VERYCROWD : hspan <= 10 * LOOSE || vspan <= 10 * LOOSE ? CROWD : LOOSE;
};
function enoughRotate(bef, z0, z1, z2, aft){
var angleRotatedBefore = Math.abs(angleBetween(z1, bef, z1, z0));
var angleRotatedAfter = Math.abs(angleBetween(z1, aft, z1, z2));
return !((angleRotatedBefore < SMALLANGLE || angleRotatedBefore > Math.PI - SMALLANGLE)
|| (angleRotatedAfter < SMALLANGLE || angleRotatedAfter > Math.PI - SMALLANGLE))
}
function fairify(scurve, gizmo, denseQ){
function enoughRotate(bef, z0, z1, z2, aft, cleanMore, flagl, flagr){
var angleRotatedBefore = Math.abs(angleBetween(bef.next || z1, bef, z1, z0));
var angleRotatedAfter = Math.abs(angleBetween(aft.prev || z1, aft, z1, z2));
var smallanglel = (cleanMore ? SMALLANGLE_CLEANMORE : SMALLANGLE) * (flagl && cleanMore ? 1 : 2);
var smallangler = (cleanMore ? SMALLANGLE_CLEANMORE : SMALLANGLE) * (flagr && cleanMore ? 1 : 2);
return !((angleRotatedBefore < smallanglel || angleRotatedBefore > Math.PI - smallanglel) && pldistance(z1, z0, bef) <= CLOSE && distance(z1, bef) <= 5 * CROWD
|| (flagr ? false : (angleRotatedAfter < smallangler || angleRotatedAfter > Math.PI - smallangler) && pldistance(z1, z2, aft) <= CLOSE && distance(z1, aft) <= 5 * CROWD))
};
function fairify(scurve, gizmo, denseQ, cleanMore){
for(var j = 0; j < scurve.length; j++){
scurve[j] = Transform.untransform(gizmo, scurve[j])
}
@ -207,6 +217,12 @@ function fairify(scurve, gizmo, denseQ){
};
// Mark corners and extremae
for(var j = 1; j < splitpoints.length - 1; j++) {
if(splitpoints[j].onCurve && !splitpoints[j - 1].onCurve){
splitpoints[j].prev = splitpoints[j - 1]
}
if(splitpoints[j].onCurve && !splitpoints[j + 1].onCurve){
splitpoints[j].next = splitpoints[j + 1]
}
if(splitpoints[j].onCurve && !splitpoints[j - 1].onCurve && !splitpoints[j + 1].onCurve){
var z1 = splitpoints[j], z0 = splitpoints[j - 1], z2 = splitpoints[j + 1]
if(cross(z1, z0, z2) < 1e-6 && dot(z1, z0, z2) < 0){
@ -214,6 +230,20 @@ function fairify(scurve, gizmo, denseQ){
var angle = Math.abs(angle0 / Math.PI * 2 % 1);
if(Math.abs(Math.abs(angle0) - Math.PI / 2) <= SMALL || angle <= SMALL || angle >= 1 - SMALL){
z1.mark = true; // extrema
z1.inflect = false;
} else {
var isInflection = false;
if(j > 2 && j < splitpoints.length - 2){
var za = bez3(z1, z0, splitpoints[j - 2], splitpoints[j - 3], SMALL);
var zb = bez3(z1, z2, splitpoints[j + 2], splitpoints[j + 3], SMALL);
var inflect = ((z0.y-z2.y)*(za.x-z0.x) + (z2.x-z0.x)*(za.y-z0.y))
* ((z0.y-z2.y)*(zb.x-z0.x) + (z2.x-z0.x)*(zb.y-z0.y));
if(inflect < 0) isInflection = true;
};
if(z1.inflect || isInflection) {
z1.mark = true;
z1.asinflect = true;
}
}
} else {
z1.mark = true; // also corner
@ -223,31 +253,24 @@ function fairify(scurve, gizmo, denseQ){
}
};
splitpoints[0].mark = splitpoints[splitpoints.length - 1].mark = true;
// Mark infections
var lastmark = splitpoints[0];
for(var k = 1; k < splitpoints.length && !splitpoints[k].mark; k++);
var nextmark = splitpoints[k];
for(var j = 1; j < splitpoints.length - 1; j++) {
if(splitpoints[j].mark){
lastmark = splitpoints[j];
for(var k = j + 1; k < splitpoints.length && !splitpoints[k].mark; k++);
nextmark = splitpoints[k];
}
if(splitpoints[j].onCurve && !splitpoints[j - 1].onCurve && !splitpoints[j + 1].onCurve){
var z1 = splitpoints[j], z0 = splitpoints[j - 1], z2 = splitpoints[j + 1]
if(cross(z1, z0, z2) < 1e-6 && dot(z1, z0, z2) < 0){
var isInflection = false;
if(j > 2 && j < splitpoints.length - 2){
var za = bez3(z1, z0, splitpoints[j - 2], splitpoints[j - 3], SMALL);
var zb = bez3(z1, z2, splitpoints[j + 2], splitpoints[j + 3], SMALL);
var inflect = ((z0.y-z2.y)*(za.x-z0.x) + (z2.x-z0.x)*(za.y-z0.y)) * ((z0.y-z2.y)*(zb.x-z0.x) + (z2.x-z0.x)*(zb.y-z0.y));
if(inflect < 0) isInflection = true;
};
if((z1.inflect || isInflection) && (denseQ || enoughRotate(lastmark, z0, z1, z2, nextmark))) {
z1.mark = true;
// Mark cleanup inflections
for(var pass = 0; pass < 2; pass++){
for(var j = 1; j < splitpoints.length - 1; j++) {
if(splitpoints[j].mark){
for(var k = j - 1; k >= 0 && !splitpoints[k].mark; k--);
lastmark = splitpoints[k];
for(var k = j + 1; k < splitpoints.length && !splitpoints[k].mark; k++);
nextmark = splitpoints[k];
}
if(splitpoints[j].mark && splitpoints[j].asinflect){
var z1 = splitpoints[j], z0 = splitpoints[j - 1], z2 = splitpoints[j + 1]
if(!(denseQ || enoughRotate(lastmark, z0, z1, z2, nextmark, cleanMore, lastmark.asinflect, nextmark.asinflect))) {
//z1.mark = false;
z0.remove = z1.remove = z2.remove = true;
}
}
}
};
for(var j = 0; j < splitpoints.length; j++) if(splitpoints[j].remove) splitpoints[j].mark = false;
};
// Mark diagonals
var lastmark = splitpoints[0];
@ -267,8 +290,8 @@ function fairify(scurve, gizmo, denseQ){
var angle = Math.abs(angle0 / Math.PI * segments % 1);
var angleRotatedBefore = Math.abs(angleBetween(z1, lastmark, z1, z0));
var angleRotatedAfter = Math.abs(angleBetween(z1, nextmark, z1, z2));
if(!(denseQ || enoughRotate(lastmark, z0, z1, z2, nextmark))
|| !(Math.abs(Math.abs(angle0) - Math.PI / 2) <= SMALL || angle <= SMALL || angle >= 1 - SMALL)){
if(!(Math.abs(Math.abs(angle0) - Math.PI / 2) <= SMALL || angle <= SMALL || angle >= 1 - SMALL)
|| !(denseQ || enoughRotate(lastmark, z0, z1, z2, nextmark))){
z1.remove = z0.remove = z2.remove = true;
}
}

View File

@ -165,7 +165,7 @@ export : define [SetupBuilders args] : begin
lhs.0.type = rhs.0.type = lhs.(lhs.length - 1).type = rhs.(rhs.length - 1).type = 'corner'
libspiro.spiroToBezierOnContext [lhs.concat : rhs.reverse] true g QUAD PRECISION
if ([not s.unfair] && [not para.unfair]) : foreach [j : range 0 g.contours.length] : begin
set g.contours.(j) : fairify g.contours.(j) globalTransform s.dense
set g.contours.(j) : fairify g.contours.(j) globalTransform s.dense s.cleanmore
set g.knots knots
set g.lhsknots lhs
set g.rhsknots rhs
@ -177,7 +177,7 @@ export : define [SetupBuilders args] : begin
libspiro.spiroToBezierOnContext knots closed g QUAD PRECISION
foreach af [items-of lastafs] : if af : af.call g
if [not para.unfair] : foreach [j : range 0 g.contours.length] : begin
set g.contours.(j) : fairify g.contours.(j) (g.fairGizmo || g.gizmo) g.dense
set g.contours.(j) : fairify g.contours.(j) (g.fairGizmo || g.gizmo) g.dense g.cleanmore
this.include g
return [object