Skip to content

Instantly share code, notes, and snippets.

@giautm
Last active July 6, 2018 07:09
Show Gist options
  • Save giautm/8cb0a793de57244c8dd00a10d2ee7fe7 to your computer and use it in GitHub Desktop.
Save giautm/8cb0a793de57244c8dd00a10d2ee7fe7 to your computer and use it in GitHub Desktop.
import { vec2 } from 'gl-matrix';
const ZERO_VECTOR = vec2.create();
function linePerpendicularToLine(out, vec, middlePoint, weight) {
if (weight <= 0 || vec2.equals(vec, ZERO_VECTOR)) {
vec2.copy(out[0], middlePoint);
vec2.copy(out[1], middlePoint);
} else {
const perpendicular = vec2.fromValues(vec[1], -vec[0]);
vec2.normalize(perpendicular, perpendicular);
const haflWeight = weight * 0.5;
vec2.scaleAndAdd(out[0], middlePoint, perpendicular, +haflWeight);
vec2.scaleAndAdd(out[1], middlePoint, perpendicular, -haflWeight);
}
return out;
}
export function lineCreate() {
return [vec2.create(), vec2.create()];
}
export function lineAverage(out, lineA, lineB) {
vec2.scale(out[0], vec2.add(out[0], lineA[0], lineB[0]), 0.5);
vec2.scale(out[1], vec2.add(out[1], lineA[1], lineB[1]), 0.5);
return out;
}
export function linesPerpendicularToLine(pointA, pointB) {
const lineVec = vec2.subtract(vec2.create(), pointB.point, pointA.point);
return {
first: linePerpendicularToLine(lineCreate(), lineVec, pointA.point, pointA.weight),
second: linePerpendicularToLine(lineCreate(), lineVec, pointB.point, pointB.weight),
};
}
import { EventEmitter } from 'fbemitter';
import { vec2 } from 'gl-matrix';
class SignatureBezierProvider extends EventEmitter {
static dotWeight = 3;
static pointsPerLine = 4;
static touchDistanceThreshold = 2;
static events = ['doDot', 'doLine', 'doQuadCurve', 'doBezierCurve'];
static signatureWeightForLine(pointA, pointB) {
/**
* The is the maximum length that will vary weight.
* Anything higher will return the same weight.
*/
const maxLengthRange = 50.0;
/**
* These are based on having a minimum line thickness of 2.0 and maximum of 7.0,
* linearly over line lengths 0-maxLengthRange.
* They fit into a typical linear equation: y = mx + c
*
* Note: Only the points of the two parallel bezier curves will be
* at least as thick as the constant. The bezier curves themselves
* could still be drawn with sharp angles, meaning there is no true
* 'minimum thickness' of the signature.
*/
const gradient = 0.1;
const constant = 2.0;
const length = vec2.length(pointA, pointB);
const inversedLength = Math.max(maxLengthRange - length, 0);
return inversedLength * gradient + constant;
}
points = new Array(SignatureBezierProvider.pointsPerLine)
.fill(SignatureBezierProvider.dotWeight)
.map(weight => ({ point: vec2.create(), weight }));
addPointToSignature(point) {
if (this.isFirstPoint) {
this.startNewLine(point, SignatureBezierProvider.dotWeight);
} else {
let previousPoint = this.previousPoint;
if (vec2.length(previousPoint, point) < SignatureBezierProvider.touchDistanceThreshold) {
return;
}
if (this.isStartOfNextLine) {
this.finalizeBezier(point);
this.startNewLine(this.points[3].point, this.points[3].weight);
}
this.addPointAndWeight(
point,
SignatureBezierProvider.signatureWeightForLine(previousPoint, point)
);
}
this.generateBezierPath(this.nextPointIndex - 1);
}
finalizeBezier(point3rd) {
/**
* Smooth the join between beziers by modifying the last point of the current bezier
* to equal the average of the points either side of it.
*/
const point2nd = this.points[2].point;
const pointAvg = this.points[3].point;
vec2.scale(pointAvg, vec2.add(pointAvg, point2nd, point3rd), 0.5);
this.points[3].weight = SignatureBezierProvider.signatureWeightForLine(point2nd, pointAvg);
this.generateBezierPath(3, true);
}
generateBezierPath(index, finalized = false) {
this.emit(SignatureBezierProvider.events[index], this.points, finalized);
}
startNewLine(point, weight) {
this.nextPointIndex = 0;
this.addPointAndWeight(point, weight);
}
addPointAndWeight(point, weight) {
vec2.copy(this.points[this.nextPointIndex].point, point);
this.points[this.nextPointIndex].weight = weight;
this.nextPointIndex += 1;
}
get isFirstPoint() {
return this.nextPointIndex === 0;
}
get isStartOfNextLine() {
return this.nextPointIndex >= SignatureBezierProvider.pointsPerLine;
}
get previousPoint() {
return this.points[this.nextPointIndex - 1].point;
}
}
export default SignatureBezierProvider;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment