Add opaque tiles
This commit is contained in:
parent
0929a98e2a
commit
b51028568b
|
@ -0,0 +1,21 @@
|
|||
#version 300 es
|
||||
|
||||
// pathfinder/demo2/opaque.fs.glsl
|
||||
//
|
||||
// Copyright © 2018 The Pathfinder Project Developers.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
precision highp float;
|
||||
|
||||
in vec4 vColor;
|
||||
|
||||
out vec4 oFragColor;
|
||||
|
||||
void main() {
|
||||
oFragColor = vColor;
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
#version 300 es
|
||||
|
||||
// pathfinder/demo2/opaque.vs.glsl
|
||||
//
|
||||
// Copyright © 2018 The Pathfinder Project Developers.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
precision highp float;
|
||||
|
||||
uniform vec2 uFramebufferSize;
|
||||
uniform vec2 uTileSize;
|
||||
|
||||
in vec2 aTessCoord;
|
||||
in vec2 aTileOrigin;
|
||||
in vec4 aColor;
|
||||
|
||||
out vec4 vColor;
|
||||
|
||||
void main() {
|
||||
vec2 position = aTileOrigin + uTileSize * aTessCoord;
|
||||
vColor = aColor;
|
||||
gl_Position = vec4((position / uFramebufferSize * 2.0 - 1.0) * vec2(1.0, -1.0), 0.0, 1.0);
|
||||
}
|
||||
|
|
@ -11,11 +11,44 @@
|
|||
"commander": "^2.15.1"
|
||||
}
|
||||
},
|
||||
"@types/bezier-js": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/bezier-js/-/bezier-js-0.0.6.tgz",
|
||||
"integrity": "sha1-DZdtaBY8SVUzLveYohoLPWOB0Ss="
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "7.10.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-7.10.2.tgz",
|
||||
"integrity": "sha512-RO4ig5taKmcrU4Rex8ojG1gpwFkjddzug9iPQSDvbewHN9vDpcFewevkaOK+KT+w1LeZnxbgOyfXwV4pxsQ4GQ=="
|
||||
},
|
||||
"@types/opentype.js": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "http://registry.npmjs.org/@types/opentype.js/-/opentype.js-0.0.0.tgz",
|
||||
"integrity": "sha1-AvZD18Y8Pr9PZG/GBa5Gg/hJrxs="
|
||||
},
|
||||
"@types/pdfkit": {
|
||||
"version": "0.7.36",
|
||||
"resolved": "https://registry.npmjs.org/@types/pdfkit/-/pdfkit-0.7.36.tgz",
|
||||
"integrity": "sha512-9eRA6MuW+n78yU3HhoIrDxjyAX2++B5MpLDYqHOnaRTquCw+5sYXT+QN8E1eSaxvNUwlRfU3tOm4UzTeGWmBqg==",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/webgl2": {
|
||||
"version": "0.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/webgl2/-/webgl2-0.0.4.tgz",
|
||||
"integrity": "sha512-PACt1xdErJbMUOUweSrbVM7gSIYm1vTncW2hF6Os/EeWi6TXYAYMPp+8v6rzHmypE5gHrxaxZNXgMkJVIdZpHw=="
|
||||
},
|
||||
"bezier-js": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/bezier-js/-/bezier-js-2.3.1.tgz",
|
||||
"integrity": "sha512-nFpFL9tuayvlHfWh6xM7OHeTZvwr74+6KnzO3eNZMt0BC0cqb9lCTc9C8OVzrHBvbrNwriTw7XaF2SBsWQJLZA=="
|
||||
},
|
||||
"clone": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
|
||||
"integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4="
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "0.5.3",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz",
|
||||
|
@ -180,6 +213,11 @@
|
|||
"integrity": "sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA==",
|
||||
"dev": true
|
||||
},
|
||||
"graham_scan": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/graham_scan/-/graham_scan-1.0.4.tgz",
|
||||
"integrity": "sha1-OZZR3R+DU+GID1nqjl+Uud9Mkoo="
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||
|
@ -192,6 +230,20 @@
|
|||
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=",
|
||||
"dev": true
|
||||
},
|
||||
"makerjs": {
|
||||
"version": "0.9.93",
|
||||
"resolved": "https://registry.npmjs.org/makerjs/-/makerjs-0.9.93.tgz",
|
||||
"integrity": "sha512-XztS2tJEpL01dsZ5P11kGO2MitrD6MsSvkNDS3uUDVN9SVDJn33Q2j9DFruJsiRtqNVRseLK52vkkbWKtLB/tA==",
|
||||
"requires": {
|
||||
"@types/bezier-js": "^0.0.6",
|
||||
"@types/node": "^7.0.5",
|
||||
"@types/opentype.js": "^0.0.0",
|
||||
"@types/pdfkit": "^0.7.34",
|
||||
"bezier-js": "^2.1.0",
|
||||
"clone": "^1.0.2",
|
||||
"graham_scan": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"map-limit": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/map-limit/-/map-limit-0.0.1.tgz",
|
||||
|
@ -263,6 +315,14 @@
|
|||
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
|
||||
"dev": true
|
||||
},
|
||||
"svg-path-outline": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/svg-path-outline/-/svg-path-outline-1.0.1.tgz",
|
||||
"integrity": "sha1-w5Zk922IdGW4voXER3jIg0Jomas=",
|
||||
"requires": {
|
||||
"makerjs": "^0.9.39"
|
||||
}
|
||||
},
|
||||
"svgpath": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/svgpath/-/svgpath-2.2.1.tgz",
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
"dependencies": {
|
||||
"@types/webgl2": "0.0.4",
|
||||
"parse-color": "^1.0.0",
|
||||
"svg-path-outline": "^1.0.1",
|
||||
"svgpath": "^2.2.1"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
|
||||
import COVER_VERTEX_SHADER_SOURCE from "./cover.vs.glsl";
|
||||
import COVER_FRAGMENT_SHADER_SOURCE from "./cover.fs.glsl";
|
||||
import OPAQUE_VERTEX_SHADER_SOURCE from "./opaque.vs.glsl";
|
||||
import OPAQUE_FRAGMENT_SHADER_SOURCE from "./opaque.fs.glsl";
|
||||
import STENCIL_VERTEX_SHADER_SOURCE from "./stencil.vs.glsl";
|
||||
import STENCIL_FRAGMENT_SHADER_SOURCE from "./stencil.fs.glsl";
|
||||
import SVG from "../resources/svg/Ghostscript_Tiger.svg";
|
||||
|
@ -20,6 +22,7 @@ import {staticCast, unwrapNull} from "./util";
|
|||
|
||||
const SVGPath: (path: string) => SVGPath = require('svgpath');
|
||||
const parseColor: (color: string) => any = require('parse-color');
|
||||
const svgPathOutline: any = require('svg-path-outline');
|
||||
|
||||
const SVG_NS: string = "http://www.w3.org/2000/svg";
|
||||
|
||||
|
@ -58,6 +61,8 @@ class App {
|
|||
private stencilFramebuffer: WebGLFramebuffer;
|
||||
private stencilProgram: Program<'FramebufferSize' | 'TileSize' | 'AreaLUT',
|
||||
'TessCoord' | 'From' | 'Ctrl' | 'To' | 'TileIndex'>;
|
||||
private opaqueProgram: Program<'FramebufferSize' | 'TileSize',
|
||||
'TessCoord' | 'TileOrigin' | 'Color'>;
|
||||
private coverProgram:
|
||||
Program<'FramebufferSize' | 'TileSize' | 'StencilTexture' | 'StencilTextureSize',
|
||||
'TessCoord' | 'TileOrigin' | 'TileIndex' | 'Color'>;
|
||||
|
@ -65,12 +70,15 @@ class App {
|
|||
private stencilVertexPositionsBuffer: WebGLBuffer;
|
||||
private stencilVertexTileIndicesBuffer: WebGLBuffer;
|
||||
private stencilVertexArray: WebGLVertexArrayObject;
|
||||
private opaqueVertexBuffer: WebGLBuffer;
|
||||
private opaqueVertexArray: WebGLVertexArrayObject;
|
||||
private coverVertexBuffer: WebGLBuffer;
|
||||
private coverVertexArray: WebGLVertexArrayObject;
|
||||
|
||||
private scene: Scene | null;
|
||||
private primitiveCount: number;
|
||||
private tileCount: number;
|
||||
private opaqueTileCount: number;
|
||||
|
||||
constructor(svg: XMLDocument, areaLUT: HTMLImageElement) {
|
||||
const canvas = staticCast(document.getElementById('canvas'), HTMLCanvasElement);
|
||||
|
@ -135,6 +143,13 @@ class App {
|
|||
['TessCoord', 'TileOrigin', 'TileIndex', 'Color']);
|
||||
this.coverProgram = coverProgram;
|
||||
|
||||
const opaqueProgram = new Program(gl,
|
||||
OPAQUE_VERTEX_SHADER_SOURCE,
|
||||
OPAQUE_FRAGMENT_SHADER_SOURCE,
|
||||
['FramebufferSize', 'TileSize'],
|
||||
['TessCoord', 'TileOrigin', 'Color']);
|
||||
this.opaqueProgram = opaqueProgram;
|
||||
|
||||
const stencilProgram = new Program(gl,
|
||||
STENCIL_VERTEX_SHADER_SOURCE,
|
||||
STENCIL_FRAGMENT_SHADER_SOURCE,
|
||||
|
@ -182,6 +197,29 @@ class App {
|
|||
gl.enableVertexAttribArray(stencilProgram.attributes.To);
|
||||
gl.enableVertexAttribArray(stencilProgram.attributes.TileIndex);
|
||||
|
||||
// Initialize opaque VBO.
|
||||
this.opaqueVertexBuffer = unwrapNull(gl.createBuffer());
|
||||
|
||||
// Initialize opaque VAO.
|
||||
this.opaqueVertexArray = unwrapNull(gl.createVertexArray());
|
||||
gl.bindVertexArray(this.opaqueVertexArray);
|
||||
gl.useProgram(this.opaqueProgram.program);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.quadVertexBuffer);
|
||||
gl.vertexAttribPointer(opaqueProgram.attributes.TessCoord,
|
||||
2,
|
||||
gl.UNSIGNED_BYTE,
|
||||
false,
|
||||
0,
|
||||
0);
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.opaqueVertexBuffer);
|
||||
gl.vertexAttribPointer(opaqueProgram.attributes.TileOrigin, 2, gl.SHORT, false, 10, 0);
|
||||
gl.vertexAttribDivisor(opaqueProgram.attributes.TileOrigin, 1);
|
||||
gl.vertexAttribPointer(opaqueProgram.attributes.Color, 4, gl.UNSIGNED_BYTE, true, 10, 6);
|
||||
gl.vertexAttribDivisor(opaqueProgram.attributes.Color, 1);
|
||||
gl.enableVertexAttribArray(opaqueProgram.attributes.TessCoord);
|
||||
gl.enableVertexAttribArray(opaqueProgram.attributes.TileOrigin);
|
||||
gl.enableVertexAttribArray(opaqueProgram.attributes.Color);
|
||||
|
||||
// Initialize cover VBO.
|
||||
this.coverVertexBuffer = unwrapNull(gl.createBuffer());
|
||||
|
||||
|
@ -214,6 +252,7 @@ class App {
|
|||
this.scene = null;
|
||||
this.primitiveCount = 0;
|
||||
this.tileCount = 0;
|
||||
this.opaqueTileCount = 0;
|
||||
}
|
||||
|
||||
redraw(): void {
|
||||
|
@ -276,13 +315,23 @@ class App {
|
|||
//console.log(stencilData);
|
||||
*/
|
||||
|
||||
// Cover.
|
||||
// Draw opaque tiles.
|
||||
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
|
||||
const framebufferSize = {width: canvas.width, height: canvas.height};
|
||||
gl.viewport(0, 0, framebufferSize.width, framebufferSize.height);
|
||||
gl.clearColor(1.0, 1.0, 1.0, 1.0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||
|
||||
gl.bindVertexArray(this.opaqueVertexArray);
|
||||
gl.useProgram(this.opaqueProgram.program);
|
||||
gl.uniform2f(this.opaqueProgram.uniforms.FramebufferSize,
|
||||
framebufferSize.width,
|
||||
framebufferSize.height);
|
||||
gl.uniform2f(this.opaqueProgram.uniforms.TileSize, TILE_SIZE.width, TILE_SIZE.height);
|
||||
gl.disable(gl.BLEND);
|
||||
gl.drawArraysInstanced(gl.TRIANGLE_FAN, 0, 4, this.opaqueTileCount);
|
||||
|
||||
// Cover.
|
||||
gl.bindVertexArray(this.coverVertexArray);
|
||||
gl.useProgram(this.coverProgram.program);
|
||||
gl.uniform2f(this.coverProgram.uniforms.FramebufferSize,
|
||||
|
@ -315,6 +364,35 @@ class App {
|
|||
prepare(): void {
|
||||
const gl = this.gl, scene = unwrapNull(this.scene);
|
||||
|
||||
// Construct opaque tile VBOs.
|
||||
this.opaqueTileCount = 0;
|
||||
const opaqueVertexData: number[] = [];
|
||||
const opaqueTiles: number[][] = [];
|
||||
for (let pathIndex = scene.pathTileStrips.length - 1; pathIndex >= 0; pathIndex--) {
|
||||
const pathTileStrips = scene.pathTileStrips[pathIndex];
|
||||
for (const tileStrip of pathTileStrips) {
|
||||
for (const tile of tileStrip.tiles) {
|
||||
// TODO(pcwalton)
|
||||
const color = scene.pathColors[pathIndex];
|
||||
if (!tile.isFilled())
|
||||
continue;
|
||||
|
||||
if (opaqueTiles[tile.tileLeft] == null)
|
||||
opaqueTiles[tile.tileLeft] = [];
|
||||
if (opaqueTiles[tile.tileLeft][tileStrip.tileTop] != null)
|
||||
continue;
|
||||
opaqueTiles[tile.tileLeft][tileStrip.tileTop] = pathIndex;
|
||||
|
||||
opaqueVertexData.push(Math.floor(tile.tileLeft),
|
||||
Math.floor(tileStrip.tileTop),
|
||||
0,
|
||||
color.r | (color.g << 8),
|
||||
color.b | (color.a << 8));
|
||||
this.opaqueTileCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Construct stencil and cover VBOs.
|
||||
this.tileCount = 0;
|
||||
let primitiveCount = 0;
|
||||
|
@ -324,10 +402,16 @@ class App {
|
|||
const pathTileStrips = scene.pathTileStrips[pathIndex];
|
||||
for (const tileStrip of pathTileStrips) {
|
||||
for (const tile of tileStrip.tiles) {
|
||||
// TODO(pcwalton)
|
||||
const color = scene.pathColors[pathIndex];
|
||||
if (tile.isFilled())
|
||||
continue;
|
||||
|
||||
if (opaqueTiles[tile.tileLeft] != null &&
|
||||
opaqueTiles[tile.tileLeft][tileStrip.tileTop] != null &&
|
||||
pathIndex <= opaqueTiles[tile.tileLeft][tileStrip.tileTop]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const edge of tile.edges) {
|
||||
let ctrl;
|
||||
if (edge.ctrl == null)
|
||||
|
@ -341,7 +425,6 @@ class App {
|
|||
primitiveCount++;
|
||||
}
|
||||
|
||||
const color = scene.pathColors[pathIndex];
|
||||
coverVertexData.push(Math.floor(tile.tileLeft),
|
||||
Math.floor(tileStrip.tileTop),
|
||||
this.tileCount,
|
||||
|
@ -359,6 +442,10 @@ class App {
|
|||
gl.bindBuffer(gl.ARRAY_BUFFER, this.stencilVertexTileIndicesBuffer);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, new Uint16Array(stencilVertexTileIndices), gl.STATIC_DRAW);
|
||||
|
||||
// Populate the opaque VBO.
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.opaqueVertexBuffer);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, new Int16Array(opaqueVertexData), gl.DYNAMIC_DRAW);
|
||||
|
||||
// Populate the cover VBO.
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, this.coverVertexBuffer);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, new Int16Array(coverVertexData), gl.DYNAMIC_DRAW);
|
||||
|
@ -382,46 +469,34 @@ class Scene {
|
|||
document.body.appendChild(svgElement);
|
||||
|
||||
const pathElements = Array.from(document.getElementsByTagName('path'));
|
||||
const pathColors = [];
|
||||
const pathColors: any[] = [];
|
||||
|
||||
this.pathTileStrips = [];
|
||||
|
||||
//const tileDebugger = new TileDebugger(document);
|
||||
|
||||
const paths = [];
|
||||
const paths: SVGPath[] = [];
|
||||
for (let pathElementIndex = 0;
|
||||
pathElementIndex < pathElements.length;
|
||||
pathElementIndex++) {
|
||||
const pathElement = pathElements[pathElementIndex];
|
||||
const pathString = unwrapNull(pathElement.getAttribute('d'));
|
||||
|
||||
const style = window.getComputedStyle(pathElement);
|
||||
let paint: string;
|
||||
if (style.fill != null && style.fill !== 'none') {
|
||||
paint = style.fill;
|
||||
/*} else if (style.stroke != null && style.stroke !== 'none') {
|
||||
paint = style.stroke;*/
|
||||
} else {
|
||||
continue;
|
||||
this.addPath(paths, pathColors, style.fill, pathString);
|
||||
}
|
||||
if (style.stroke != null && style.stroke !== 'none') {
|
||||
/*
|
||||
const strokeWidth =
|
||||
style.strokeWidth == null ? 1.0 : parseFloat(style.strokeWidth);
|
||||
console.log("stroking path:", pathString, strokeWidth);
|
||||
try {
|
||||
const strokedPathString = svgPathOutline(pathString, strokeWidth, {joints: 1});
|
||||
this.addPath(paths, pathColors, style.stroke, strokedPathString);
|
||||
} catch (e) {}
|
||||
*/
|
||||
}
|
||||
|
||||
const color = parseColor(paint).rgba;
|
||||
pathColors.push({
|
||||
r: color[0],
|
||||
g: color[1],
|
||||
b: color[2],
|
||||
a: Math.round(color[3] * 255.),
|
||||
});
|
||||
|
||||
let path = SVGPath(unwrapNull(pathElement.getAttribute('d')));
|
||||
path = path.matrix([
|
||||
GLOBAL_TRANSFORM.a, GLOBAL_TRANSFORM.b,
|
||||
GLOBAL_TRANSFORM.c, GLOBAL_TRANSFORM.d,
|
||||
GLOBAL_TRANSFORM.tx, GLOBAL_TRANSFORM.ty,
|
||||
]);
|
||||
|
||||
path = flattenPath(path);
|
||||
path = canonicalizePath(path);
|
||||
paths.push(path);
|
||||
}
|
||||
|
||||
const startTime = window.performance.now();
|
||||
|
@ -474,6 +549,27 @@ class Scene {
|
|||
|
||||
this.pathColors = pathColors;
|
||||
}
|
||||
|
||||
private addPath(paths: SVGPath[], pathColors: any[], paint: string, pathString: string): void {
|
||||
const color = parseColor(paint).rgba;
|
||||
pathColors.push({
|
||||
r: color[0],
|
||||
g: color[1],
|
||||
b: color[2],
|
||||
a: Math.round(color[3] * 255.),
|
||||
});
|
||||
|
||||
let path = SVGPath(pathString);
|
||||
path = path.matrix([
|
||||
GLOBAL_TRANSFORM.a, GLOBAL_TRANSFORM.b,
|
||||
GLOBAL_TRANSFORM.c, GLOBAL_TRANSFORM.d,
|
||||
GLOBAL_TRANSFORM.tx, GLOBAL_TRANSFORM.ty,
|
||||
]);
|
||||
|
||||
path = flattenPath(path);
|
||||
path = canonicalizePath(path);
|
||||
paths.push(path);
|
||||
}
|
||||
}
|
||||
|
||||
class Program<U extends string, A extends string> {
|
||||
|
@ -540,6 +636,11 @@ function flattenPath(path: SVGPath): SVGPath {
|
|||
const ctrl = new Point2D(0.5 * (ctrl0.x + ctrl1.x), 0.5 * (ctrl0.y + ctrl1.y));
|
||||
return [['Q', "" + ctrl.x, "" + ctrl.y, "" + to.x, "" + to.y]];
|
||||
}
|
||||
if (segment[0] === 'A') {
|
||||
const to = new Point2D(parseFloat(segment[segment.length - 2]),
|
||||
parseFloat(segment[segment.length - 1]));
|
||||
return [['L', "" + to.x, "" + to.y]];
|
||||
}
|
||||
return [segment];
|
||||
});
|
||||
}
|
||||
|
@ -566,23 +667,6 @@ function waitForQuery(gl: WebGL2RenderingContext, disjointTimerQueryExt: any, qu
|
|||
console.log(elapsed + "ms elapsed");
|
||||
}
|
||||
|
||||
function pathIsRect(path: SVGPath, rectSize: Size2D): boolean {
|
||||
let result = true;
|
||||
path.iterate((segment, index) => {
|
||||
if (segment.length < 3)
|
||||
return;
|
||||
const point = new Point2D(parseFloat(segment[1]), parseFloat(segment[2]));
|
||||
result = result &&
|
||||
(approxEq(point.x, 0.0) || approxEq(point.x, rectSize.width)) &&
|
||||
(approxEq(point.y, 0.0) || approxEq(point.y, rectSize.height));
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function sampleBezier(from: Point2D, ctrl: Point2D, to: Point2D, t: number): Point2D {
|
||||
return from.lerp(ctrl, t).lerp(ctrl.lerp(to, t), t);
|
||||
}
|
||||
|
||||
function main(): void {
|
||||
window.fetch(SVG).then(svg => {
|
||||
svg.text().then(svgText => {
|
||||
|
|
Loading…
Reference in New Issue