๐Canvas+ext
replit โฉ Canvas+ext (v1.1) -> require Rect, Vector, Number+ext, Iterable+ext
// (Canvas+ext v1.1)
// 2023.01.22 - 21:29 (/) .drawPath() -> 1. opts destructuring, 2. line dash
// (/) .moveToPoint() -> remove `newPath` param
// (/) fix .drawArcTo(), .drawPolylineTo()
// (+) .drawPolyline(), .drawQuadraticCurve(), .drawCubicBezierCurve()
// 2023.01.21 - 09:02 (/) .drawPolygon() - fix start angle
// 2023.01.21 - 08:14 (+) .drawPolygon()
// 2023.01.20 - 23:36 (โข) first version
// -----------------------------------------------------------------
const {PI} = Math;
// โญ import
import { deg } from './Number+ext.js'; // ๐ Number+ext
import { vec, polar } from './Vector.js'; // ๐ Vector
import { range } from './Iterable+ext.js'; // ๐ Iterable+ext
// -----------------------------------------------------------------
// โญ HTMLCanvasElement + extensions
// -----------------------------------------------------------------
// ๐น .draw2D()
//
Object.defineProperties(HTMLCanvasElement.prototype, {
// ๐น .draw2D()
draw2D: {
value: function(draw){
draw(this.getContext("2d"));
},
},
});
// โญ CanvasRenderingContext2D + extensions
// -----------------------------------------------------------------
// โญ .drawPath() // general method
// ๐น .drawCircle()
// ๐น .drawWedge()
// ๐น .drawEllipse()
// -----------------------------------------------------------------
// ๐น .moveToPoint()
// -----------------------------------------------------------------
// ๐น .drawArc()
// ๐น .drawArcTo() // for rounded corners
// ๐น .drawPolyline()
// ๐น .drawPolylineTo()
// ๐น .drawQuadraticCurve()
// ๐น .drawQuadraticCurveTo()
// ๐น .drawCubicBezierCurve()
// ๐น .drawCubicBezierCurveTo()
// -----------------------------------------------------------------
// ๐น .drawRect()
// ๐น .drawPolygon()
//
Object.defineProperties(CanvasRenderingContext2D.prototype, {
// ๐น .moveToPoint()
moveToPoint: {
value: function(p, {
// newPath = false, // begin new subpath ? (default = false)
}={}) {
// if (newPath) this.beginPath();
this.moveTo(...p.coords); // ๐ Vector
}
},
// โญ .drawPath()
drawPath: {
value: function(draw, {
// path
newPath = true, // begin new subpath ?
closePath = true, // close subpath ?
// fill/stroke
fill = true,
stroke = true,
fillStyle,
strokeStyle,
// line dash
dash,
}={}){
// new subpath ?
if (newPath) this.beginPath(); // start new path if necessary
// draw path
draw();
// close path ?
if (closePath) this.closePath();
// fill ?
if (fill) {
if (fillStyle) this.fillStyle = fillStyle;
this.fill();
};
// stroke ?
if (stroke) {
if (strokeStyle) this.strokeStyle = strokeStyle;
if (dash) this.setLineDash(dash);
this.stroke();
};
},
},
// --------------------------
// arc/circle related
// --------------------------
// ๐น .drawArc()
drawArc: {
value: function(center, radius, {
// path: arc related
start = 0, // start angle
end = 2 * PI, // end angle
clockwise = true,
// path: general
...opts
}={}){
this.drawPath(() => {
this.arc(...center.coords, radius, start, end, !clockwise);
}, { ...opts });
},
},
// ๐น .drawArcTo()
// - for rounded corners
drawArcTo: {
value: function(control, target, radius, {
// arc specific
start, // start point
// path: general
...opts
}={}){
// arc specific
// โญโโโโโโโโโโ default โโโโโโโโโโโฎ
opts = {newPath: false, closePath:false, ...opts};
// if (start) { opts.newPath = true } // new subpath if has start point
// draw arc to ...
this.drawPath(() => {
// move to start point if necessary
if (start) this.moveToPoint(start);
// connect arc
this.arcTo(...control.coords, ...target.coords, radius);
}, { ...opts });
},
},
// ๐น .drawCircle()
drawCircle: {
value: function(center, radius, {
// path: general
newPath = true, // start new path?
closePath = true,
// fill/stroke
fill = true, // fill?
stroke = true, // stroke?
fillStyle,
strokeStyle,
}={}){
this.drawPath(() => {
this.arc(...center.coords, radius, 0, 2*PI);
}, { newPath, closePath, fill, stroke, fillStyle, strokeStyle });
},
},
// ๐น .drawEllipse()
drawEllipse: {
value: function(center, radiusX, radiusY, {
// path: ellipse related
start = 0,
end = 2 * PI,
rotation = 0,
clockwise = true,
// path: general
newPath = true,
closePath = true,
// fill/stroke
fill = true,
stroke = true,
fillStyle,
strokeStyle,
}={}){
this.drawPath(() => {
this.ellipse(
...center.coords, radiusX, radiusY,
rotation, start, end, !clockwise
);
}, { newPath, closePath, fill, stroke, fillStyle, strokeStyle });
},
},
// ๐น .drawWedge()
drawWedge: {
value: function(center, radius, {
// path: arc related
start = 0, // start angle
end = 90 * deg, // end angle // ๐ Number+ext
clockwise = true,
// path: general
...opts
}={}){
// wedge specific parameters
opts = {
...opts,
newPath: true,
closePath: true, // add line back to center
};
// draw wedge
this.drawPath(() => {
this.moveTo(...center.coords); // center
// โญ arc() adds a line from `center` to arc start.
this.arc(...center.coords, radius, start, end, !clockwise);
}, { ...opts });
},
},
// --------------------------
// bezier curve
// --------------------------
// ๐น .drawQuadraticCurveTo()
drawQuadraticCurveTo: {
value: function(control, target, {
// bezier curve specific
start, // start point
// path: general
newPath = false, // start new path?
closePath = false,
// fill/stroke
fill = false, // fill?
stroke = false, // stroke?
fillStyle,
strokeStyle,
}={}){
this.drawPath(() => {
// move to start point if necessary
if (start) this.moveToPoint(start, {newPath});
// connect bezier curve
this.quadraticCurveTo(...control.coords, ...target.coords);
}, { newPath: false, closePath, fill, stroke, fillStyle, strokeStyle });
},
},
// ๐น .drawQuadraticCurve()
drawQuadraticCurve: {
value: function(p1, ctrl, p2, {
...opts
}={}){
// curve specific
// โญโโโโโโโโโโโโโโโโโ default โโโโโโโโโโโโโโโโโฎ
opts = {newPath: true, fill: false, closePath: false, ...opts};
this.drawPath(() => {
// move to start point
this.moveToPoint(p1);
// connect bezier curve
this.quadraticCurveTo(...ctrl.coords, ...p2.coords);
}, { ...opts });
},
},
// ๐น .drawCubicBezierCurveTo()
drawCubicBezierCurveTo: {
value: function(control1, control2, target, {
// bezier curve specific
start, // start point
// path: general
newPath = false, // start new path ? (default: false)
closePath = false, // close subpath ? (default: false)
// fill/stroke
fill = false, // fill ? (default: false)
stroke = false, // stroke ? (default: false)
fillStyle,
strokeStyle,
}={}){
this.drawPath(() => {
// move to start point if necessary
if (start) this.moveToPoint(start, {newPath});
// connect bezier curve
this.bezierCurveTo(
...control1.coords, ...control2.coords,
...target.coords,
);
}, { newPath: false, closePath, fill, stroke, fillStyle, strokeStyle });
},
},
// ๐น .drawCubicBezierCurve()
drawCubicBezierCurve: {
value: function(p1, c1, c2, p2, {
...opts
}={}){
// curve specific
// โญโโโโโโโโโโโโโโโโโ default โโโโโโโโโโโโโโโโโฎ
opts = {newPath: true, fill: false, closePath: false, ...opts};
this.drawPath(() => {
// move to start point
this.moveToPoint(p1);
// connect bezier curve
this.bezierCurveTo(
...c1.coords, ...c2.coords,
...p2.coords,
);
}, { ...opts });
},
},
// --------------------------
// polygon
// --------------------------
// ๐น .drawRect()
drawRect: {
value: function(rect, {...opts}={}){
this.drawPath(() => {
this.rect(...rect.coords); // ๐ Rect
}, { ...opts });
},
},
// ๐น .drawPolygon()
drawPolygon: {
value: function(n, {
// polygon specific
center = vec(100, 100), // ๐ Vector
radius = 50,
rotate = 0, // rotation
clockwise = true, // path direction
// path: general
...opts
}={}){
this.drawPath(() => {
// dฮธ = ยฑ 2ฯ / n
const dt = 2 * Math.PI / n * (clockwise ? 1 : -1);
// start angle
const a0 = -90 * deg + rotate + (n.isEven ? dt/2 : 0);
// vertices
const points = range(0, n-1)
.map(i => center.plus(polar(radius, a0 + dt * i)))
.array;
// draw polyline
this.drawPolylineTo(points.slice(1), {
start: points[0],
});
}, { ...opts });
},
},
// ๐น .drawPolyline()
drawPolyline: {
value: function(points, {
...opts
}={}){
// polyline specific
// โญโโโโโโโโโ default โโโโโโโโโโฎ
opts = {fill: false, closePath: false, ...opts};
this.drawPath(() => {
this.drawPolylineTo(points.slice(1), {
start: points[0],
});
}, { ...opts });
},
},
// ๐น .drawPolylineTo()
drawPolylineTo: {
value: function(points, {
// polyline specific
start, // start point
newPath = false, // start new path?
closePath = false,
// path: general
...opts
}={}){
// polyline specific
if (start) newPath = true; // new subpath if has start point
// โญโโโโโโโโโ default โโโโโโโโโโฎ
opts = {fill: false, closePath: false, ...opts, newPath};
// draw polyline to ...
this.drawPath(() => {
// move to start point if necessary
if (start) this.moveToPoint(start);
// connect arc
points.forEach(p => {
this.lineTo(...p.coords);
});
}, { ...opts });
},
},
});
// โญ export
// -----------------------------------------------------------------
export {}; // ES module export
History
0: (โข) first version
1: (+) .drawPolygon()
Last updated
Was this helpful?