@code-not-art/core
Advanced tools
Comparing version 0.5.0 to 0.5.1
@@ -0,0 +0,0 @@ declare enum BlendMode { |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -5,2 +5,3 @@ import Vec2 from '../math/Vec2'; | ||
import tinycolor from 'tinycolor2'; | ||
import { Brush } from './Brush'; | ||
export declare type ColorSelection = Color | string | tinycolor.Instance; | ||
@@ -12,3 +13,35 @@ export declare type Stroke = { | ||
}; | ||
export default class Draw { | ||
declare type Styles = { | ||
fill?: ColorSelection; | ||
stroke?: Stroke; | ||
brush?: Brush; | ||
}; | ||
export declare type Bezier2 = { | ||
start: Vec2; | ||
control: Vec2; | ||
end: Vec2; | ||
}; | ||
export declare type Bezier3 = { | ||
start: Vec2; | ||
control1: Vec2; | ||
control2: Vec2; | ||
end: Vec2; | ||
}; | ||
export declare type Circle = { | ||
center: Vec2; | ||
radius: number; | ||
}; | ||
export declare type Line = { | ||
start: Vec2; | ||
end: Vec2; | ||
}; | ||
/** | ||
* Point is top left of rect | ||
*/ | ||
export declare type Rect = { | ||
point: Vec2; | ||
height: number; | ||
width: number; | ||
}; | ||
export declare class Draw { | ||
context: CanvasRenderingContext2D; | ||
@@ -19,42 +52,11 @@ constructor(context: CanvasRenderingContext2D); | ||
private stroke; | ||
circle(inputs: { | ||
center: Vec2; | ||
radius: number; | ||
fill?: ColorSelection; | ||
stroke?: Stroke; | ||
}): void; | ||
rect(inputs: { | ||
point: Vec2; | ||
height: number; | ||
width: number; | ||
stroke?: Stroke; | ||
fill?: ColorSelection; | ||
}): void; | ||
line(inputs: { | ||
start: Vec2; | ||
end: Vec2; | ||
stroke?: Stroke; | ||
fill?: ColorSelection; | ||
}): void; | ||
bezier2(inputs: { | ||
start: Vec2; | ||
control: Vec2; | ||
end: Vec2; | ||
stroke?: Stroke; | ||
fill?: ColorSelection; | ||
}): void; | ||
bezier3(inputs: { | ||
start: Vec2; | ||
control1: Vec2; | ||
control2: Vec2; | ||
end: Vec2; | ||
stroke?: Stroke; | ||
fill?: ColorSelection; | ||
}): void; | ||
circle(inputs: Circle & Styles): void; | ||
rect(inputs: Rect & Styles): void; | ||
line(inputs: Line & Styles): void; | ||
bezier2(inputs: Bezier2 & Styles): void; | ||
bezier3(inputs: Bezier3 & Styles): void; | ||
path(inputs: { | ||
path: Path; | ||
fill?: ColorSelection; | ||
stroke?: Stroke; | ||
close?: boolean; | ||
}): void; | ||
} & Styles): void; | ||
points(inputs: { | ||
@@ -65,4 +67,6 @@ points: Vec2[]; | ||
fill?: ColorSelection; | ||
brush?: Brush; | ||
}): void; | ||
} | ||
export {}; | ||
//# sourceMappingURL=Draw.d.ts.map |
"use strict"; | ||
var __assign = (this && this.__assign) || function () { | ||
__assign = Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
return __assign.apply(this, arguments); | ||
}; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
}) : function(o, v) { | ||
o["default"] = v; | ||
}); | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); | ||
__setModuleDefault(result, mod); | ||
return result; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
@@ -6,5 +36,7 @@ return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Draw = void 0; | ||
var Vec2_1 = __importDefault(require("../math/Vec2")); | ||
var color_1 = __importDefault(require("../color")); | ||
var Path_1 = require("../structures/Path"); | ||
var Path_1 = __importStar(require("../structures/Path")); | ||
var constants_1 = require("../constants"); | ||
function resolveColorSelection(selection) { | ||
@@ -56,5 +88,6 @@ if (selection instanceof color_1.default) { | ||
this.context.beginPath(); | ||
this.context.arc(inputs.center.x, inputs.center.y, inputs.radius, 0, Math.PI * 2); | ||
this.context.arc(inputs.center.x, inputs.center.y, inputs.radius, 0, constants_1.TAU); | ||
this.context.closePath(); | ||
this.draw(inputs.stroke, inputs.fill); | ||
inputs.brush && inputs.brush({ path: Path_1.default.fromCircle(inputs), draw: this }); | ||
}; | ||
@@ -78,2 +111,3 @@ // TODO: Expand rect to also handle rounded corners. Probably want to take advantage of Path API once written. | ||
this.draw(inputs.stroke, inputs.fill); | ||
inputs.brush && inputs.brush({ path: Path_1.default.fromRect(inputs), draw: this }); | ||
}; | ||
@@ -86,2 +120,3 @@ Draw.prototype.line = function (inputs) { | ||
this.draw(stroke, fill); | ||
inputs.brush && inputs.brush({ path: Path_1.default.fromLine(inputs), draw: this }); | ||
}; | ||
@@ -94,2 +129,3 @@ Draw.prototype.bezier2 = function (inputs) { | ||
this.draw(stroke, fill); | ||
inputs.brush && inputs.brush({ path: Path_1.default.fromBez2(inputs), draw: this }); | ||
}; | ||
@@ -102,2 +138,3 @@ Draw.prototype.bezier3 = function (inputs) { | ||
this.draw(stroke, fill); | ||
inputs.brush && inputs.brush({ path: Path_1.default.fromBez3(inputs), draw: this }); | ||
}; | ||
@@ -117,2 +154,8 @@ Draw.prototype.path = function (inputs) { | ||
break; | ||
case Path_1.SegmentType.Arc: | ||
var startAngle = segment.start.diff(segment.center).angle(); | ||
var endAngle = startAngle - segment.angle; | ||
var counterClockwise = segment.angle >= 0; | ||
_this.context.arc(segment.center.x, segment.center.y, segment.radius, startAngle, endAngle, counterClockwise); | ||
break; | ||
case Path_1.SegmentType.Bezier2: | ||
@@ -124,2 +167,4 @@ _this.context.quadraticCurveTo(segment.control.x, segment.control.y, segment.point.x, segment.point.y); | ||
break; | ||
default: | ||
console.warn("core.draw.path", "segment switch hit default due to unhandled segment type:", segment); | ||
} | ||
@@ -131,18 +176,13 @@ }); | ||
this.draw(stroke, fill); | ||
inputs.brush && inputs.brush({ path: inputs.path, draw: this }); | ||
}; | ||
Draw.prototype.points = function (inputs) { | ||
var _this = this; | ||
var points = inputs.points, close = inputs.close, stroke = inputs.stroke, fill = inputs.fill; | ||
this.context.beginPath(); | ||
points.forEach(function (point) { | ||
_this.context.lineTo(point.x, point.y); | ||
}); | ||
if (close) { | ||
this.context.closePath(); | ||
} | ||
this.draw(stroke, fill); | ||
var points = inputs.points; | ||
var path = new Path_1.default(points[0]); | ||
points.slice(1).forEach(function (point) { return path.line(point); }); | ||
this.path(__assign(__assign({}, inputs), { path: path })); | ||
}; | ||
return Draw; | ||
}()); | ||
exports.default = Draw; | ||
exports.Draw = Draw; | ||
//# sourceMappingURL=Draw.js.map |
@@ -1,85 +0,5 @@ | ||
import Vec2 from '../math/Vec2'; | ||
import BlendMode from './BlendMode'; | ||
import Draw, { ColorSelection } from './Draw'; | ||
export declare type CanvasTransform = { | ||
push: () => CanvasTransform; | ||
pop: () => CanvasTransform; | ||
reset: () => CanvasTransform; | ||
set: (transform: DOMMatrix) => CanvasTransform; | ||
translate: (vector: Vec2) => CanvasTransform; | ||
scale: (factor: Vec2) => CanvasTransform; | ||
rotate: (radians: number) => CanvasTransform; | ||
flipHorizontal: () => CanvasTransform; | ||
flipVertical: () => CanvasTransform; | ||
}; | ||
declare class Canvas { | ||
canvas: HTMLCanvasElement; | ||
context: CanvasRenderingContext2D; | ||
draw: Draw; | ||
private _transforms; | ||
constructor(canvas: HTMLCanvasElement); | ||
/** | ||
* Create Canvas object attached to HTML Dom element with the given ID. | ||
* @param canvasId HTML Element ID. Will find the element with this ID and return a Canvas object using that element. | ||
* TODO: Check that the element returned is a Canvas. Right now this is assumed and will break if the wrong element type is found. | ||
*/ | ||
static fromId(canvasId: string): Canvas; | ||
get: { | ||
/** | ||
* Canvas width (in pixels) | ||
* @returns {number} | ||
*/ | ||
width: () => number; | ||
/** | ||
* Canvas Height (in pixels) | ||
* @returns {number} | ||
*/ | ||
height: () => number; | ||
/** | ||
* Minimum dimension, the lesser of width and height | ||
* @returns {number} | ||
*/ | ||
minDim: () => number; | ||
/** | ||
* Maximum dimension, the greater of width and height | ||
* @returns {number} | ||
*/ | ||
maxDim: () => number; | ||
/** | ||
* The ratio of width to height | ||
* @returns {number} | ||
*/ | ||
aspectRatio: () => number; | ||
size: () => Vec2; | ||
/** | ||
* Center of the canvas (width/2, height/2) | ||
* @returns {Vec2} | ||
*/ | ||
center: () => Vec2; | ||
blendMode: () => string; | ||
transform: () => DOMMatrix; | ||
}; | ||
set: { | ||
size: (width: number, height: number) => void; | ||
blendMode: (mode: BlendMode) => void; | ||
transform: (transform?: DOMMatrix | undefined) => void; | ||
}; | ||
/** | ||
* Reset the content on the canvas. Will clear all current marks and return to full transparent. | ||
*/ | ||
clear: () => void; | ||
/** | ||
* Fill the entire canvas with a single color | ||
* @param color | ||
*/ | ||
fill: (color: ColorSelection) => void; | ||
transform: CanvasTransform; | ||
layer: () => Canvas; | ||
apply(layer: Canvas, options?: { | ||
blendMode?: BlendMode; | ||
position?: Vec2; | ||
rotation?: number; | ||
}): void; | ||
} | ||
export default Canvas; | ||
export * from './Brush'; | ||
export * from './BlendMode'; | ||
export * from './Canvas'; | ||
export * from './Draw'; | ||
//# sourceMappingURL=index.d.ts.map |
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var Vec2_1 = __importDefault(require("../math/Vec2")); | ||
var BlendMode_1 = __importDefault(require("./BlendMode")); | ||
var Draw_1 = __importDefault(require("./Draw")); | ||
var Canvas = /** @class */ (function () { | ||
function Canvas(canvas) { | ||
var _this = this; | ||
this._transforms = []; | ||
this.get = { | ||
// Canvas size | ||
/** | ||
* Canvas width (in pixels) | ||
* @returns {number} | ||
*/ | ||
width: function () { return _this.canvas.width; }, | ||
/** | ||
* Canvas Height (in pixels) | ||
* @returns {number} | ||
*/ | ||
height: function () { return _this.canvas.height; }, | ||
/** | ||
* Minimum dimension, the lesser of width and height | ||
* @returns {number} | ||
*/ | ||
minDim: function () { return Math.min(_this.canvas.width, _this.canvas.height); }, | ||
/** | ||
* Maximum dimension, the greater of width and height | ||
* @returns {number} | ||
*/ | ||
maxDim: function () { return Math.max(_this.canvas.width, _this.canvas.height); }, | ||
/** | ||
* The ratio of width to height | ||
* @returns {number} | ||
*/ | ||
aspectRatio: function () { return _this.canvas.width / _this.canvas.height; }, | ||
size: function () { return new Vec2_1.default(_this.canvas.width, _this.canvas.height); }, | ||
/** | ||
* Center of the canvas (width/2, height/2) | ||
* @returns {Vec2} | ||
*/ | ||
center: function () { return new Vec2_1.default(_this.canvas.width / 2, _this.canvas.height / 2); }, | ||
// context state | ||
blendMode: function () { return _this.context.globalCompositeOperation; }, | ||
transform: function () { return _this.context.getTransform(); }, | ||
}; | ||
this.set = { | ||
size: function (width, height) { | ||
_this.canvas.height = height; | ||
_this.canvas.width = width; | ||
}, | ||
blendMode: function (mode) { | ||
_this.context.globalCompositeOperation = mode; | ||
}, | ||
transform: function (transform) { | ||
_this.context.setTransform(transform); | ||
}, | ||
}; | ||
// TODO: Both clear and fill need to have code that resets all context translate/rotations and then restore the previous | ||
/** | ||
* Reset the content on the canvas. Will clear all current marks and return to full transparent. | ||
*/ | ||
this.clear = function () { | ||
_this.context.clearRect(0, 0, _this.get.width(), _this.get.height()); | ||
}; | ||
/** | ||
* Fill the entire canvas with a single color | ||
* @param color | ||
*/ | ||
this.fill = function (color) { | ||
var storedTransform = _this.context.getTransform(); | ||
_this.context.resetTransform(); | ||
_this.draw.rect({ | ||
point: Vec2_1.default.origin(), | ||
height: _this.get.height(), | ||
width: _this.get.width(), | ||
fill: color, | ||
}); | ||
_this.context.setTransform(storedTransform); | ||
}; | ||
this.transform = { | ||
push: function () { | ||
_this._transforms.push(_this.context.getTransform()); | ||
return _this.transform; | ||
}, | ||
pop: function () { | ||
var storedTransform = _this._transforms.pop(); | ||
if (storedTransform) { | ||
_this.context.setTransform(storedTransform); | ||
} | ||
else { | ||
_this.context.resetTransform(); | ||
} | ||
return _this.transform; | ||
}, | ||
reset: function () { | ||
_this.context.resetTransform(); | ||
return _this.transform; | ||
}, | ||
set: function (transform) { | ||
_this.context.setTransform(transform); | ||
return _this.transform; | ||
}, | ||
// ===== Move the draw position | ||
translate: function (vector) { | ||
_this.context.translate(vector.x, vector.y); | ||
return _this.transform; | ||
}, | ||
scale: function (factor) { | ||
_this.context.scale(factor.x, factor.y); | ||
return _this.transform; | ||
}, | ||
rotate: function (radians) { | ||
_this.context.rotate(radians); | ||
return _this.transform; | ||
}, | ||
flipHorizontal: function () { | ||
_this.context.scale(-1, 1); | ||
return _this.transform; | ||
}, | ||
flipVertical: function () { | ||
_this.context.scale(1, -1); | ||
return _this.transform; | ||
}, | ||
}; | ||
// ===== Get and Merge layers | ||
this.layer = function () { | ||
// probably dont need to get doc from the canvas, could probably just use `document` but maybe there is some edge condition where this matters. | ||
var doc = _this.canvas.ownerDocument; | ||
var layer = doc.createElement('canvas'); | ||
layer.width = _this.canvas.width; | ||
layer.height = _this.canvas.height; | ||
return new Canvas(layer); | ||
}; | ||
this.canvas = canvas; | ||
var maybeContext = canvas.getContext('2d'); | ||
if (!maybeContext) { | ||
// Handling potential return of `undefined` - Don't ever expect this but we're playing nice with TypeScript | ||
// If this happens then this whole class will fail everything so might as well just throw an error and break the script. | ||
throw new Error('Canvas could not return 2D Context'); | ||
} | ||
this.context = maybeContext; | ||
this.context.lineCap = 'round'; | ||
this.draw = new Draw_1.default(maybeContext); | ||
} | ||
/** | ||
* Create Canvas object attached to HTML Dom element with the given ID. | ||
* @param canvasId HTML Element ID. Will find the element with this ID and return a Canvas object using that element. | ||
* TODO: Check that the element returned is a Canvas. Right now this is assumed and will break if the wrong element type is found. | ||
*/ | ||
Canvas.fromId = function (canvasId) { | ||
var canvas = document.getElementById(canvasId); | ||
if (!canvas) { | ||
throw new Error("Could not find canvas element with id " + canvasId); | ||
} | ||
return new Canvas(canvas); | ||
}; | ||
Canvas.prototype.apply = function (layer, options) { | ||
if (options === void 0) { options = {}; } | ||
var blendMode = options.blendMode, position = options.position, rotation = options.rotation; | ||
var tempBlend = this.context.globalCompositeOperation; | ||
this.context.globalCompositeOperation = blendMode || BlendMode_1.default.default; | ||
var storedTransform = this.context.getTransform(); | ||
this.context.resetTransform(); | ||
if (rotation) { | ||
this.context.rotate(rotation); | ||
} | ||
var x = position ? position.x : 0; | ||
var y = position ? position.y : 0; | ||
this.context.drawImage(layer.canvas, x, y, layer.canvas.width, layer.canvas.height); | ||
this.context.setTransform(storedTransform); | ||
this.context.globalCompositeOperation = tempBlend; | ||
}; | ||
return Canvas; | ||
}()); | ||
exports.default = Canvas; | ||
__exportStar(require("./Brush"), exports); | ||
__exportStar(require("./BlendMode"), exports); | ||
__exportStar(require("./Canvas"), exports); | ||
__exportStar(require("./Draw"), exports); | ||
//# sourceMappingURL=index.js.map |
@@ -0,0 +0,0 @@ import Color from './'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ import tinycolor from 'tinycolor2'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ import Color from '.'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ export declare const PI: number; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -1,2 +0,2 @@ | ||
export { default as Canvas, CanvasTransform } from './canvas'; | ||
export * from './canvas'; | ||
export { default as Color } from './color'; | ||
@@ -3,0 +3,0 @@ export * as Constants from './constants'; |
@@ -14,2 +14,5 @@ "use strict"; | ||
}); | ||
var __exportStar = (this && this.__exportStar) || function(m, exports) { | ||
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); | ||
}; | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
@@ -26,5 +29,4 @@ if (mod && mod.__esModule) return mod; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Utils = exports.Structures = exports.Noise = exports.Random = exports.Vec2 = exports.Gradient = exports.Constants = exports.Color = exports.Canvas = void 0; | ||
var canvas_1 = require("./canvas"); | ||
Object.defineProperty(exports, "Canvas", { enumerable: true, get: function () { return __importDefault(canvas_1).default; } }); | ||
exports.Utils = exports.Structures = exports.Noise = exports.Random = exports.Vec2 = exports.Gradient = exports.Constants = exports.Color = void 0; | ||
__exportStar(require("./canvas"), exports); | ||
var color_1 = require("./color"); | ||
@@ -31,0 +33,0 @@ Object.defineProperty(exports, "Color", { enumerable: true, get: function () { return __importDefault(color_1).default; } }); |
@@ -34,2 +34,3 @@ /** | ||
within(max: Vec2, min?: Vec2): boolean; | ||
distance(value: Vec2 | number): number; | ||
normalize(): Vec2; | ||
@@ -36,0 +37,0 @@ /** |
@@ -78,2 +78,5 @@ "use strict"; | ||
}; | ||
Vec2.prototype.distance = function (value) { | ||
return this.diff(value).magnitude(); | ||
}; | ||
/* ===== Modify ===== */ | ||
@@ -80,0 +83,0 @@ Vec2.prototype.normalize = function () { |
declare type Distribution = (x: number) => number; | ||
export default Distribution; | ||
//# sourceMappingURL=Distribution.d.ts.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
//# sourceMappingURL=Distribution.js.map |
@@ -0,0 +0,0 @@ import Distribution from './Distribution'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ import Distribution from '.'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ import Distribution from '.'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ import Distribution from '.'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ import { Color, Vec2 } from '..'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -1,7 +0,7 @@ | ||
import { Noise3D, Noise4D } from '.'; | ||
import { Noise2D, Noise3D, Noise4D } from '.'; | ||
import Vec2 from '../../math/Vec2'; | ||
/** | ||
* 2d curl angle from 3d or 4d noise | ||
* 2d curl angle from noise | ||
*/ | ||
declare function curl(position: Vec2, noise: Noise3D | Noise4D, config: { | ||
declare function curl(position: Vec2, noise: Noise2D | Noise3D | Noise4D, config: { | ||
z?: number; | ||
@@ -8,0 +8,0 @@ w?: number; |
@@ -8,3 +8,3 @@ "use strict"; | ||
/** | ||
* 2d curl angle from 3d or 4d noise | ||
* 2d curl angle from noise | ||
*/ | ||
@@ -11,0 +11,0 @@ function curl(position, noise, config) { |
@@ -0,0 +0,0 @@ export { default as curl } from './curl'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ export declare function getWord(ratio: number): string; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ import Vec2 from '../math/Vec2'; |
@@ -0,0 +0,0 @@ "use strict"; |
export { grid, GridOptions, GridTile } from './grid'; | ||
export { default as Path, Segment, SegmentType, Bezier2Segment, Bezier3Segment, LineSegment, MoveSegment, } from './Path'; | ||
//# sourceMappingURL=index.d.ts.map |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,1 +1,2 @@ | ||
import { Bezier2, Bezier3, Circle, Line, Rect } from '../canvas'; | ||
import Vec2 from '../math/Vec2'; | ||
@@ -5,2 +6,3 @@ export declare enum SegmentType { | ||
Line = "LINE", | ||
Arc = "ARC", | ||
Bezier2 = "BEZIER2", | ||
@@ -17,2 +19,8 @@ Bezier3 = "BEZIER3" | ||
}; | ||
export declare type ArcSegment = { | ||
type: SegmentType.Arc; | ||
angle: number; | ||
radius: number; | ||
center: Vec2; | ||
}; | ||
export declare type Bezier2Segment = { | ||
@@ -29,3 +37,6 @@ type: SegmentType.Bezier2; | ||
}; | ||
export declare type Segment = MoveSegment | LineSegment | Bezier2Segment | Bezier3Segment; | ||
export declare type Segment = (MoveSegment | LineSegment | ArcSegment | Bezier2Segment | Bezier3Segment) & { | ||
start: Vec2; | ||
end: Vec2; | ||
}; | ||
export default class Path { | ||
@@ -35,2 +46,4 @@ start: Vec2; | ||
constructor(start: Vec2); | ||
getEnd(): Vec2; | ||
arc(angle: number, center: Vec2): Path; | ||
move(destination: Vec2): Path; | ||
@@ -40,3 +53,8 @@ line(destination: Vec2): Path; | ||
bez3(destination: Vec2, control1: Vec2, control2: Vec2): Path; | ||
static fromCircle(circle: Circle): Path; | ||
static fromLine(line: Line): Path; | ||
static fromRect(rect: Rect): Path; | ||
static fromBez2(bez2: Bezier2): Path; | ||
static fromBez3(bez3: Bezier3): Path; | ||
} | ||
//# sourceMappingURL=Path.d.ts.map |
"use strict"; | ||
var __assign = (this && this.__assign) || function () { | ||
__assign = Object.assign || function(t) { | ||
for (var s, i = 1, n = arguments.length; i < n; i++) { | ||
s = arguments[i]; | ||
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) | ||
t[p] = s[p]; | ||
} | ||
return t; | ||
}; | ||
return __assign.apply(this, arguments); | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.SegmentType = void 0; | ||
var constants_1 = require("../constants"); | ||
var Vec2_1 = __importDefault(require("../math/Vec2")); | ||
var SegmentType; | ||
@@ -8,2 +24,3 @@ (function (SegmentType) { | ||
SegmentType["Line"] = "LINE"; | ||
SegmentType["Arc"] = "ARC"; | ||
SegmentType["Bezier2"] = "BEZIER2"; | ||
@@ -17,2 +34,20 @@ SegmentType["Bezier3"] = "BEZIER3"; | ||
} | ||
Path.prototype.getEnd = function () { | ||
return this.segments.length | ||
? this.segments[this.segments.length - 1].end | ||
: this.start; | ||
}; | ||
Path.prototype.arc = function (angle, center) { | ||
var start = this.getEnd(); | ||
var radius = start.distance(center); | ||
var end = center.diff(start).rotate(angle); | ||
var segment = { | ||
type: SegmentType.Arc, | ||
angle: angle, | ||
radius: radius, | ||
center: center, | ||
}; | ||
this.segments.push(__assign(__assign({}, segment), { start: start, end: end })); | ||
return this; | ||
}; | ||
Path.prototype.move = function (destination) { | ||
@@ -23,3 +58,3 @@ var segment = { | ||
}; | ||
this.segments.push(segment); | ||
this.segments.push(__assign(__assign({}, segment), { start: this.getEnd(), end: destination })); | ||
return this; | ||
@@ -32,3 +67,3 @@ }; | ||
}; | ||
this.segments.push(segment); | ||
this.segments.push(__assign(__assign({}, segment), { start: this.getEnd(), end: destination })); | ||
return this; | ||
@@ -42,3 +77,3 @@ }; | ||
}; | ||
this.segments.push(segment); | ||
this.segments.push(__assign(__assign({}, segment), { start: this.getEnd(), end: destination })); | ||
return this; | ||
@@ -53,5 +88,30 @@ }; | ||
}; | ||
this.segments.push(segment); | ||
this.segments.push(__assign(__assign({}, segment), { start: this.getEnd(), end: destination })); | ||
return this; | ||
}; | ||
Path.fromCircle = function (circle) { | ||
var startPos = circle.center.add(Vec2_1.default.unit().scale(circle.radius)); | ||
return new Path(startPos).arc(constants_1.TAU, circle.center); | ||
}; | ||
Path.fromLine = function (line) { | ||
return new Path(line.start).line(line.end); | ||
}; | ||
Path.fromRect = function (rect) { | ||
var corners = [ | ||
rect.point.add(new Vec2_1.default(rect.width, 0)), | ||
rect.point.add(new Vec2_1.default(rect.width, rect.height)), | ||
rect.point.add(new Vec2_1.default(0, rect.height)), | ||
]; | ||
return new Path(rect.point) | ||
.line(corners[0]) | ||
.line(corners[1]) | ||
.line(corners[2]) | ||
.line(rect.point); | ||
}; | ||
Path.fromBez2 = function (bez2) { | ||
return new Path(bez2.start).bez2(bez2.end, bez2.control); | ||
}; | ||
Path.fromBez3 = function (bez3) { | ||
return new Path(bez3.start).bez3(bez3.end, bez3.control1, bez3.control2); | ||
}; | ||
return Path; | ||
@@ -58,0 +118,0 @@ }()); |
export declare function toDegrees(rads: number): number; | ||
export declare function toRadians(degrees: number): number; | ||
//# sourceMappingURL=angles.d.ts.map |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ export declare function array(length: number): number[]; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ export { toDegrees, toRadians } from './angles'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ /** |
@@ -0,0 +0,0 @@ "use strict"; |
{ | ||
"name": "@code-not-art/core", | ||
"version": "0.5.0", | ||
"version": "0.5.1", | ||
"description": "HTML 5 Canvas Drawing Library", | ||
@@ -19,5 +19,6 @@ "main": "dist/index.js", | ||
"art", | ||
"html5", | ||
"TypeScript", | ||
"makecodenotart", | ||
"code-not-art" | ||
"codenotart", | ||
"codeart" | ||
], | ||
@@ -24,0 +25,0 @@ "author": "Jon Eubank <joneubank@gmail.com>", |
@@ -5,2 +5,4 @@ import Vec2 from '../math/Vec2'; | ||
import tinycolor from 'tinycolor2'; | ||
import { Brush } from './Brush'; | ||
import { TAU } from '../constants'; | ||
@@ -25,3 +27,41 @@ export type ColorSelection = Color | string | tinycolor.Instance; | ||
export default class Draw { | ||
type Styles = { | ||
fill?: ColorSelection; | ||
stroke?: Stroke; | ||
brush?: Brush; | ||
}; | ||
export type Bezier2 = { | ||
start: Vec2; | ||
control: Vec2; | ||
end: Vec2; | ||
}; | ||
export type Bezier3 = { | ||
start: Vec2; | ||
control1: Vec2; | ||
control2: Vec2; | ||
end: Vec2; | ||
}; | ||
export type Circle = { | ||
center: Vec2; | ||
radius: number; | ||
}; | ||
export type Line = { | ||
start: Vec2; | ||
end: Vec2; | ||
}; | ||
/** | ||
* Point is top left of rect | ||
*/ | ||
export type Rect = { | ||
point: Vec2; | ||
height: number; | ||
width: number; | ||
}; | ||
export class Draw { | ||
context: CanvasRenderingContext2D; | ||
@@ -62,28 +102,12 @@ constructor(context: CanvasRenderingContext2D) { | ||
// -- Different geometries below | ||
circle(inputs: { | ||
center: Vec2; | ||
radius: number; | ||
fill?: ColorSelection; | ||
stroke?: Stroke; | ||
}) { | ||
circle(inputs: Circle & Styles) { | ||
this.context.beginPath(); | ||
this.context.arc( | ||
inputs.center.x, | ||
inputs.center.y, | ||
inputs.radius, | ||
0, | ||
Math.PI * 2, | ||
); | ||
this.context.arc(inputs.center.x, inputs.center.y, inputs.radius, 0, TAU); | ||
this.context.closePath(); | ||
this.draw(inputs.stroke, inputs.fill); | ||
inputs.brush && inputs.brush({ path: Path.fromCircle(inputs), draw: this }); | ||
} | ||
// TODO: Expand rect to also handle rounded corners. Probably want to take advantage of Path API once written. | ||
rect(inputs: { | ||
point: Vec2; | ||
height: number; | ||
width: number; | ||
stroke?: Stroke; | ||
fill?: ColorSelection; | ||
}) { | ||
rect(inputs: Rect & Styles) { | ||
// Map all corners except the start | ||
@@ -104,10 +128,6 @@ const corners = [ | ||
this.draw(inputs.stroke, inputs.fill); | ||
inputs.brush && inputs.brush({ path: Path.fromRect(inputs), draw: this }); | ||
} | ||
line(inputs: { | ||
start: Vec2; | ||
end: Vec2; | ||
stroke?: Stroke; | ||
fill?: ColorSelection; | ||
}) { | ||
line(inputs: Line & Styles) { | ||
const { start, end, stroke, fill } = inputs; | ||
@@ -118,11 +138,6 @@ this.context.beginPath(); | ||
this.draw(stroke, fill); | ||
inputs.brush && inputs.brush({ path: Path.fromLine(inputs), draw: this }); | ||
} | ||
bezier2(inputs: { | ||
start: Vec2; | ||
control: Vec2; | ||
end: Vec2; | ||
stroke?: Stroke; | ||
fill?: ColorSelection; | ||
}) { | ||
bezier2(inputs: Bezier2 & Styles) { | ||
const { start, control, end, stroke, fill } = inputs; | ||
@@ -133,12 +148,6 @@ this.context.beginPath(); | ||
this.draw(stroke, fill); | ||
inputs.brush && inputs.brush({ path: Path.fromBez2(inputs), draw: this }); | ||
} | ||
bezier3(inputs: { | ||
start: Vec2; | ||
control1: Vec2; | ||
control2: Vec2; | ||
end: Vec2; | ||
stroke?: Stroke; | ||
fill?: ColorSelection; | ||
}) { | ||
bezier3(inputs: Bezier3 & Styles) { | ||
const { start, control1, control2, end, stroke, fill } = inputs; | ||
@@ -156,10 +165,11 @@ this.context.beginPath(); | ||
this.draw(stroke, fill); | ||
inputs.brush && inputs.brush({ path: Path.fromBez3(inputs), draw: this }); | ||
} | ||
path(inputs: { | ||
path: Path; | ||
fill?: ColorSelection; | ||
stroke?: Stroke; | ||
close?: boolean; | ||
}) { | ||
path( | ||
inputs: { | ||
path: Path; | ||
close?: boolean; | ||
} & Styles, | ||
) { | ||
const { path, fill, stroke, close = false } = inputs; | ||
@@ -176,2 +186,15 @@ this.context.beginPath(); | ||
break; | ||
case SegmentType.Arc: | ||
const startAngle = segment.start.diff(segment.center).angle(); | ||
const endAngle = startAngle - segment.angle; | ||
const counterClockwise = segment.angle >= 0; | ||
this.context.arc( | ||
segment.center.x, | ||
segment.center.y, | ||
segment.radius, | ||
startAngle, | ||
endAngle, | ||
counterClockwise, | ||
); | ||
break; | ||
case SegmentType.Bezier2: | ||
@@ -195,2 +218,8 @@ this.context.quadraticCurveTo( | ||
break; | ||
default: | ||
console.warn( | ||
`core.draw.path`, | ||
`segment switch hit default due to unhandled segment type:`, | ||
segment, | ||
); | ||
} | ||
@@ -202,2 +231,3 @@ }); | ||
this.draw(stroke, fill); | ||
inputs.brush && inputs.brush({ path: inputs.path, draw: this }); | ||
} | ||
@@ -210,13 +240,9 @@ | ||
fill?: ColorSelection; | ||
brush?: Brush; | ||
}) { | ||
const { points, close, stroke, fill } = inputs; | ||
this.context.beginPath(); | ||
points.forEach((point) => { | ||
this.context.lineTo(point.x, point.y); | ||
}); | ||
if (close) { | ||
this.context.closePath(); | ||
} | ||
this.draw(stroke, fill); | ||
const { points } = inputs; | ||
const path = new Path(points[0]); | ||
points.slice(1).forEach((point) => path.line(point)); | ||
this.path({ ...inputs, path }); | ||
} | ||
} |
@@ -1,219 +0,4 @@ | ||
import Vec2 from '../math/Vec2'; | ||
import BlendMode from './BlendMode'; | ||
import Draw, { ColorSelection } from './Draw'; | ||
export type CanvasTransform = { | ||
push: () => CanvasTransform; | ||
pop: () => CanvasTransform; | ||
reset: () => CanvasTransform; | ||
set: (transform: DOMMatrix) => CanvasTransform; | ||
translate: (vector: Vec2) => CanvasTransform; | ||
scale: (factor: Vec2) => CanvasTransform; | ||
rotate: (radians: number) => CanvasTransform; | ||
flipHorizontal: () => CanvasTransform; | ||
flipVertical: () => CanvasTransform; | ||
}; | ||
class Canvas { | ||
canvas: HTMLCanvasElement; | ||
context: CanvasRenderingContext2D; | ||
draw: Draw; | ||
private _transforms: DOMMatrix[] = []; | ||
constructor(canvas: HTMLCanvasElement) { | ||
this.canvas = canvas; | ||
const maybeContext = canvas.getContext('2d'); | ||
if (!maybeContext) { | ||
// Handling potential return of `undefined` - Don't ever expect this but we're playing nice with TypeScript | ||
// If this happens then this whole class will fail everything so might as well just throw an error and break the script. | ||
throw new Error('Canvas could not return 2D Context'); | ||
} | ||
this.context = maybeContext; | ||
this.context.lineCap = 'round'; | ||
this.draw = new Draw(maybeContext); | ||
} | ||
/** | ||
* Create Canvas object attached to HTML Dom element with the given ID. | ||
* @param canvasId HTML Element ID. Will find the element with this ID and return a Canvas object using that element. | ||
* TODO: Check that the element returned is a Canvas. Right now this is assumed and will break if the wrong element type is found. | ||
*/ | ||
static fromId(canvasId: string) { | ||
const canvas = document.getElementById(canvasId); | ||
if (!canvas) { | ||
throw new Error(`Could not find canvas element with id ${canvasId}`); | ||
} | ||
return new Canvas(canvas as HTMLCanvasElement); | ||
} | ||
get = { | ||
// Canvas size | ||
/** | ||
* Canvas width (in pixels) | ||
* @returns {number} | ||
*/ | ||
width: () => this.canvas.width, | ||
/** | ||
* Canvas Height (in pixels) | ||
* @returns {number} | ||
*/ | ||
height: () => this.canvas.height, | ||
/** | ||
* Minimum dimension, the lesser of width and height | ||
* @returns {number} | ||
*/ | ||
minDim: () => Math.min(this.canvas.width, this.canvas.height), | ||
/** | ||
* Maximum dimension, the greater of width and height | ||
* @returns {number} | ||
*/ | ||
maxDim: () => Math.max(this.canvas.width, this.canvas.height), | ||
/** | ||
* The ratio of width to height | ||
* @returns {number} | ||
*/ | ||
aspectRatio: () => this.canvas.width / this.canvas.height, | ||
size: () => new Vec2(this.canvas.width, this.canvas.height), | ||
/** | ||
* Center of the canvas (width/2, height/2) | ||
* @returns {Vec2} | ||
*/ | ||
center: () => new Vec2(this.canvas.width / 2, this.canvas.height / 2), | ||
// context state | ||
blendMode: () => this.context.globalCompositeOperation, | ||
transform: () => this.context.getTransform(), | ||
}; | ||
set = { | ||
size: (width: number, height: number) => { | ||
this.canvas.height = height; | ||
this.canvas.width = width; | ||
}, | ||
blendMode: (mode: BlendMode) => { | ||
this.context.globalCompositeOperation = mode; | ||
}, | ||
transform: (transform?: DOMMatrix) => { | ||
this.context.setTransform(transform); | ||
}, | ||
}; | ||
// TODO: Both clear and fill need to have code that resets all context translate/rotations and then restore the previous | ||
/** | ||
* Reset the content on the canvas. Will clear all current marks and return to full transparent. | ||
*/ | ||
clear = () => { | ||
this.context.clearRect(0, 0, this.get.width(), this.get.height()); | ||
}; | ||
/** | ||
* Fill the entire canvas with a single color | ||
* @param color | ||
*/ | ||
fill = (color: ColorSelection) => { | ||
const storedTransform = this.context.getTransform(); | ||
this.context.resetTransform(); | ||
this.draw.rect({ | ||
point: Vec2.origin(), | ||
height: this.get.height(), | ||
width: this.get.width(), | ||
fill: color, | ||
}); | ||
this.context.setTransform(storedTransform); | ||
}; | ||
transform: CanvasTransform = { | ||
push: (): CanvasTransform => { | ||
this._transforms.push(this.context.getTransform()); | ||
return this.transform; | ||
}, | ||
pop: (): CanvasTransform => { | ||
const storedTransform = this._transforms.pop(); | ||
if (storedTransform) { | ||
this.context.setTransform(storedTransform); | ||
} else { | ||
this.context.resetTransform(); | ||
} | ||
return this.transform; | ||
}, | ||
reset: (): CanvasTransform => { | ||
this.context.resetTransform(); | ||
return this.transform; | ||
}, | ||
set: (transform: DOMMatrix): CanvasTransform => { | ||
this.context.setTransform(transform); | ||
return this.transform; | ||
}, | ||
// ===== Move the draw position | ||
translate: (vector: Vec2): CanvasTransform => { | ||
this.context.translate(vector.x, vector.y); | ||
return this.transform; | ||
}, | ||
scale: (factor: Vec2): CanvasTransform => { | ||
this.context.scale(factor.x, factor.y); | ||
return this.transform; | ||
}, | ||
rotate: (radians: number): CanvasTransform => { | ||
this.context.rotate(radians); | ||
return this.transform; | ||
}, | ||
flipHorizontal: (): CanvasTransform => { | ||
this.context.scale(-1, 1); | ||
return this.transform; | ||
}, | ||
flipVertical: (): CanvasTransform => { | ||
this.context.scale(1, -1); | ||
return this.transform; | ||
}, | ||
}; | ||
// ===== Get and Merge layers | ||
layer = () => { | ||
// probably dont need to get doc from the canvas, could probably just use `document` but maybe there is some edge condition where this matters. | ||
const doc = this.canvas.ownerDocument; | ||
const layer = doc.createElement('canvas'); | ||
layer.width = this.canvas.width; | ||
layer.height = this.canvas.height; | ||
return new Canvas(layer); | ||
}; | ||
apply( | ||
layer: Canvas, | ||
options: { blendMode?: BlendMode; position?: Vec2; rotation?: number } = {}, | ||
) { | ||
const { blendMode, position, rotation } = options; | ||
const tempBlend = this.context.globalCompositeOperation; | ||
this.context.globalCompositeOperation = blendMode || BlendMode.default; | ||
const storedTransform = this.context.getTransform(); | ||
this.context.resetTransform(); | ||
if (rotation) { | ||
this.context.rotate(rotation); | ||
} | ||
const x = position ? position.x : 0; | ||
const y = position ? position.y : 0; | ||
this.context.drawImage( | ||
layer.canvas, | ||
x, | ||
y, | ||
layer.canvas.width, | ||
layer.canvas.height, | ||
); | ||
this.context.setTransform(storedTransform); | ||
this.context.globalCompositeOperation = tempBlend; | ||
} | ||
} | ||
export default Canvas; | ||
export * from './Brush'; | ||
export * from './BlendMode'; | ||
export * from './Canvas'; | ||
export * from './Draw'; |
@@ -1,2 +0,2 @@ | ||
export { default as Canvas, CanvasTransform } from './canvas'; | ||
export * from './canvas'; | ||
export { default as Color } from './color'; | ||
@@ -3,0 +3,0 @@ export * as Constants from './constants'; |
@@ -23,3 +23,3 @@ /** | ||
/* ===== Properties ===== */ | ||
magnitude() { | ||
magnitude(): number { | ||
return Math.sqrt(this.x * this.x + this.y * this.y); | ||
@@ -31,3 +31,3 @@ } | ||
*/ | ||
angle() { | ||
angle(): number { | ||
return Math.atan2(this.y, this.x); | ||
@@ -37,3 +37,3 @@ } | ||
/* ===== Base Maths ===== */ | ||
add(value: Vec2 | number) { | ||
add(value: Vec2 | number): Vec2 { | ||
if (value instanceof Vec2) { | ||
@@ -46,3 +46,3 @@ return new Vec2(this.x + value.x, this.y + value.y); | ||
diff(value: Vec2 | number) { | ||
diff(value: Vec2 | number): Vec2 { | ||
if (value instanceof Vec2) { | ||
@@ -55,3 +55,3 @@ return new Vec2(this.x - value.x, this.y - value.y); | ||
scale(value: number | Vec2) { | ||
scale(value: number | Vec2): Vec2 { | ||
if (typeof value === 'object') { | ||
@@ -64,3 +64,3 @@ return new Vec2(this.x * value.x, this.y * value.y); | ||
dot(vector: Vec2) { | ||
dot(vector: Vec2): number { | ||
return this.x * vector.x + this.y * vector.y; | ||
@@ -74,7 +74,7 @@ } | ||
*/ | ||
cross(vector: Vec2) { | ||
cross(vector: Vec2): number { | ||
return this.x * vector.y - this.y * vector.x; | ||
} | ||
within(max: Vec2, min?: Vec2) { | ||
within(max: Vec2, min?: Vec2): boolean { | ||
const _min = min || Vec2.origin(); | ||
@@ -86,4 +86,8 @@ return ( | ||
distance(value: Vec2 | number): number { | ||
return this.diff(value).magnitude(); | ||
} | ||
/* ===== Modify ===== */ | ||
normalize() { | ||
normalize(): Vec2 { | ||
const M = this.magnitude(); | ||
@@ -97,3 +101,3 @@ return new Vec2(this.x / M, this.y / M); | ||
*/ | ||
rotate(angle: number) { | ||
rotate(angle: number): Vec2 { | ||
return this.toPolar().add(new Vec2(0, angle)).toCoords(); | ||
@@ -113,3 +117,3 @@ } | ||
*/ | ||
toPolar() { | ||
toPolar(): Vec2 { | ||
return new Vec2(this.magnitude(), this.angle()); | ||
@@ -121,3 +125,3 @@ } | ||
*/ | ||
toCoords() { | ||
toCoords(): Vec2 { | ||
return new Vec2(Math.cos(this.y) * this.x, Math.sin(this.y) * this.x); | ||
@@ -132,3 +136,3 @@ } | ||
*/ | ||
static origin() { | ||
static origin(): Vec2 { | ||
return new Vec2(0, 0); | ||
@@ -141,3 +145,3 @@ } | ||
*/ | ||
static zero() { | ||
static zero(): Vec2 { | ||
return Vec2.origin(); | ||
@@ -150,3 +154,3 @@ } | ||
*/ | ||
static unit() { | ||
static unit(): Vec2 { | ||
return new Vec2(1, 0); | ||
@@ -159,3 +163,3 @@ } | ||
*/ | ||
static ones() { | ||
static ones(): Vec2 { | ||
return new Vec2(1, 1); | ||
@@ -170,3 +174,3 @@ } | ||
*/ | ||
static from(prototype: Vec2 | number) { | ||
static from(prototype: Vec2 | number): Vec2 { | ||
if (prototype instanceof Vec2) { | ||
@@ -173,0 +177,0 @@ return new Vec2(prototype.x, prototype.y); |
@@ -1,10 +0,10 @@ | ||
import { Noise3D, Noise4D } from '.'; | ||
import { Noise2D, Noise3D, Noise4D } from '.'; | ||
import Vec2 from '../../math/Vec2'; | ||
/** | ||
* 2d curl angle from 3d or 4d noise | ||
* 2d curl angle from noise | ||
*/ | ||
function curl( | ||
position: Vec2, | ||
noise: Noise3D | Noise4D, | ||
noise: Noise2D | Noise3D | Noise4D, | ||
config: { | ||
@@ -11,0 +11,0 @@ z?: number; |
@@ -0,1 +1,3 @@ | ||
import { Bezier2, Bezier3, Circle, Line, Rect } from '../canvas'; | ||
import { TAU } from '../constants'; | ||
import Vec2 from '../math/Vec2'; | ||
@@ -6,2 +8,3 @@ | ||
Line = 'LINE', | ||
Arc = 'ARC', | ||
Bezier2 = 'BEZIER2', | ||
@@ -21,2 +24,9 @@ Bezier3 = 'BEZIER3', | ||
export type ArcSegment = { | ||
type: SegmentType.Arc; | ||
angle: number; | ||
radius: number; | ||
center: Vec2; | ||
}; | ||
export type Bezier2Segment = { | ||
@@ -35,7 +45,12 @@ type: SegmentType.Bezier2; | ||
export type Segment = | ||
export type Segment = ( | ||
| MoveSegment | ||
| LineSegment | ||
| ArcSegment | ||
| Bezier2Segment | ||
| Bezier3Segment; | ||
| Bezier3Segment | ||
) & { | ||
start: Vec2; | ||
end: Vec2; | ||
}; | ||
@@ -50,2 +65,24 @@ export default class Path { | ||
getEnd(): Vec2 { | ||
return this.segments.length | ||
? this.segments[this.segments.length - 1].end | ||
: this.start; | ||
} | ||
arc(angle: number, center: Vec2): Path { | ||
const start = this.getEnd(); | ||
const radius = start.distance(center); | ||
const end = center.diff(start).rotate(angle); | ||
const segment: ArcSegment = { | ||
type: SegmentType.Arc, | ||
angle, | ||
radius, | ||
center, | ||
}; | ||
this.segments.push({ ...segment, start, end }); | ||
return this; | ||
} | ||
move(destination: Vec2): Path { | ||
@@ -57,3 +94,3 @@ const segment: MoveSegment = { | ||
this.segments.push(segment); | ||
this.segments.push({ ...segment, start: this.getEnd(), end: destination }); | ||
return this; | ||
@@ -68,3 +105,3 @@ } | ||
this.segments.push(segment); | ||
this.segments.push({ ...segment, start: this.getEnd(), end: destination }); | ||
return this; | ||
@@ -80,3 +117,3 @@ } | ||
this.segments.push(segment); | ||
this.segments.push({ ...segment, start: this.getEnd(), end: destination }); | ||
return this; | ||
@@ -93,5 +130,35 @@ } | ||
this.segments.push(segment); | ||
this.segments.push({ ...segment, start: this.getEnd(), end: destination }); | ||
return this; | ||
} | ||
static fromCircle(circle: Circle): Path { | ||
const startPos = circle.center.add(Vec2.unit().scale(circle.radius)); | ||
return new Path(startPos).arc(TAU, circle.center); | ||
} | ||
static fromLine(line: Line): Path { | ||
return new Path(line.start).line(line.end); | ||
} | ||
static fromRect(rect: Rect): Path { | ||
const corners = [ | ||
rect.point.add(new Vec2(rect.width, 0)), | ||
rect.point.add(new Vec2(rect.width, rect.height)), | ||
rect.point.add(new Vec2(0, rect.height)), | ||
]; | ||
return new Path(rect.point) | ||
.line(corners[0]) | ||
.line(corners[1]) | ||
.line(corners[2]) | ||
.line(rect.point); | ||
} | ||
static fromBez2(bez2: Bezier2): Path { | ||
return new Path(bez2.start).bez2(bez2.end, bez2.control); | ||
} | ||
static fromBez3(bez3: Bezier3): Path { | ||
return new Path(bez3.start).bez3(bez3.end, bez3.control1, bez3.control2); | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
262626
152
4067