├── types
├── constructor
├── general
│ ├── cdata.d.ts
│ ├── string.d.ts
│ ├── transforms.d.ts
│ ├── viewBox.d.ts
│ ├── path.d.ts
│ ├── dom.d.ts
│ ├── algebra.d.ts
│ └── index.d.ts
├── spec
│ ├── namespace.d.ts
│ ├── nodes.d.ts
│ ├── classes_attributes.d.ts
│ ├── classes_nodes.d.ts
│ ├── nodes_attributes.d.ts
│ └── nodes_children.d.ts
├── environment
│ ├── lib.d.ts
│ ├── messages.d.ts
│ ├── detect.d.ts
│ ├── window.d.ts
│ └── strings.d.ts
├── arguments
│ ├── semiFlattenArrays.d.ts
│ ├── makeViewBox.d.ts
│ └── makeCoordinates.d.ts
├── colors
│ ├── parseColor.d.ts
│ └── convert.d.ts
└── elements
│ └── constructors.d.ts
├── .travis.yml
├── .gitignore
├── src
├── environment
│ ├── lib.js
│ ├── messages.js
│ ├── detect.js
│ ├── strings.js
│ └── window.js
├── spec
│ ├── nodes.js
│ ├── namespace.js
│ ├── classes_nodes.js
│ ├── nodes_children.js
│ └── nodes_attributes.js
├── colors
│ ├── index.js
│ ├── parseColor.js
│ └── convert.js
├── constructor
├── general
│ ├── index.js
│ ├── cdata.js
│ ├── string.js
│ ├── algebra.js
│ ├── viewBox.js
│ ├── transforms.js
│ └── path.js
├── arguments
│ ├── makeViewBox.js
│ ├── makeCoordinates.js
│ └── semiFlattenArrays.js
└── index.js
├── examples
├── empty.html
├── code
│ ├── images
│ │ ├── play.svg
│ │ ├── pause.svg
│ │ ├── moon.svg
│ │ ├── download.svg
│ │ ├── share.svg
│ │ ├── question.svg
│ │ ├── dice.svg
│ │ └── font-size.svg
│ ├── examples
│ │ ├── albers2.js
│ │ ├── spiral.js
│ │ ├── parabola.js
│ │ ├── polygon.js
│ │ ├── arrows.js
│ │ ├── text.js
│ │ ├── clip-path.js
│ │ ├── ten-print.js
│ │ ├── curves.js
│ │ ├── astroid.js
│ │ ├── riley1.js
│ │ ├── albers1.js
│ │ ├── draw.js
│ │ ├── bezier.js
│ │ ├── mask.js
│ │ ├── dragon.js
│ │ ├── star.js
│ │ ├── clock.js
│ │ ├── test1.js
│ │ ├── bugs.js
│ │ └── hanoi.js
│ ├── index.html
│ ├── js
│ │ └── queryWatcher.js
│ └── css
│ │ └── style.css
├── load-file.html
├── japanese-flag.html
├── albers.html
├── random-walker.html
├── mask.html
├── compare-with.html
├── ten-print.html
├── mystify.html
└── compare-without.html
├── .npmignore
├── tests
├── index.html
├── environment.test.js
├── window.test.js
├── clean.js
├── svg.animation.test.js
├── svg.background.test.js
├── svg.controls.test.js
├── use.test.js
├── object.assign.test.js
├── stylesheet.test.js
├── class.id.test.js
├── arrow.test.js
├── svg.viewbox.test.js
├── ear.js
├── urls.test.js
├── coordinates.test.js
├── dom.test.js
├── ellipse.test.js
├── types.test.js
├── lineno.js
├── polygon.test.js
├── svg.args.test.js
├── transforms.test.js
├── svg.save.test.js
├── rect.test.js
├── circle.test.js
├── line.test.js
├── svg.load.test.js
├── path.test.js
└── nodes.test.js
├── tsconfig.json
├── rollup.config.js
├── .eslintrc.json
├── package.json
└── readme.md
/types/constructor/extensions/arrow/template.d.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - lts/*
--------------------------------------------------------------------------------
/types/constructor/extensions/svg/controls-rewrite.d.ts:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.DS_Store
2 | node_modules/
3 | coverage/
4 | module/
5 | package-lock.json
6 |
--------------------------------------------------------------------------------
/types/general/cdata.d.ts:
--------------------------------------------------------------------------------
1 | export function makeCDATASection(text: string): CDATASection;
2 |
--------------------------------------------------------------------------------
/types/constructor/extensions/shared/urls.d.ts:
--------------------------------------------------------------------------------
1 | export default methods;
2 | declare const methods: {};
3 |
--------------------------------------------------------------------------------
/types/spec/namespace.d.ts:
--------------------------------------------------------------------------------
1 | declare const _default: "http://www.w3.org/2000/svg";
2 | export default _default;
3 |
--------------------------------------------------------------------------------
/src/environment/lib.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Rabbit Ear (c) Kraft
3 | */
4 | const lib = {};
5 |
6 | export default lib;
7 |
--------------------------------------------------------------------------------
/types/constructor/extensions/arrow/init.d.ts:
--------------------------------------------------------------------------------
1 | export default init;
2 | declare function init(...args: any[]): any;
3 |
--------------------------------------------------------------------------------
/types/environment/lib.d.ts:
--------------------------------------------------------------------------------
1 | export default lib;
2 | /**
3 | * Rabbit Ear (c) Kraft
4 | */
5 | declare const lib: {};
6 |
--------------------------------------------------------------------------------
/types/environment/messages.d.ts:
--------------------------------------------------------------------------------
1 | declare namespace _default {
2 | let window: string;
3 | }
4 | export default _default;
5 |
--------------------------------------------------------------------------------
/types/constructor/extensions/origami/init.d.ts:
--------------------------------------------------------------------------------
1 | export default init;
2 | declare function init(graph: any, ...args: any[]): any;
3 |
--------------------------------------------------------------------------------
/types/constructor/extensions/svg/animation.d.ts:
--------------------------------------------------------------------------------
1 | export default Animation;
2 | declare function Animation(element: any): void;
3 |
--------------------------------------------------------------------------------
/types/constructor/extensions/svg/touch.d.ts:
--------------------------------------------------------------------------------
1 | export default TouchEvents;
2 | declare function TouchEvents(element: any): void;
3 |
--------------------------------------------------------------------------------
/types/constructor/extensions/svg/getSVGFrame.d.ts:
--------------------------------------------------------------------------------
1 | export default getSVGFrame;
2 | declare function getSVGFrame(element: any): any;
3 |
--------------------------------------------------------------------------------
/types/constructor/extensions/svg/controls.d.ts:
--------------------------------------------------------------------------------
1 | export default applyControlsToSVG;
2 | declare function applyControlsToSVG(svg: any): void;
3 |
--------------------------------------------------------------------------------
/types/arguments/semiFlattenArrays.d.ts:
--------------------------------------------------------------------------------
1 | export function svgSemiFlattenArrays(...args: any[]): any[][];
2 | export default svgSemiFlattenArrays;
3 |
--------------------------------------------------------------------------------
/types/constructor/extensions/curve/arguments.d.ts:
--------------------------------------------------------------------------------
1 | export default curveArguments;
2 | declare function curveArguments(...args: any[]): string[];
3 |
--------------------------------------------------------------------------------
/types/spec/nodes.d.ts:
--------------------------------------------------------------------------------
1 | export const nodeNames: string[];
2 | export default classes_nodes;
3 | import classes_nodes from "./classes_nodes.js";
4 |
--------------------------------------------------------------------------------
/types/constructor/extensions/curve/getCurveEndpoints.d.ts:
--------------------------------------------------------------------------------
1 | export default getCurveEndpoints;
2 | declare function getCurveEndpoints(d: any): any[];
3 |
--------------------------------------------------------------------------------
/types/constructor/extensions/svg/makeBackground.d.ts:
--------------------------------------------------------------------------------
1 | export default makeBackground;
2 | declare function makeBackground(element: any, color: any): any;
3 |
--------------------------------------------------------------------------------
/src/environment/messages.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Rabbit Ear (c) Kraft
3 | */
4 | export default {
5 | window: "window not set; svg.window = @xmldom/xmldom",
6 | };
7 |
--------------------------------------------------------------------------------
/types/colors/parseColor.d.ts:
--------------------------------------------------------------------------------
1 | export function parseColorToRgb(string: string): number[] | undefined;
2 | export function parseColorToHex(string: string): string;
3 |
--------------------------------------------------------------------------------
/examples/empty.html:
--------------------------------------------------------------------------------
1 |
2 |
.
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/types/constructor/extensions/curve/makeCurvePath.d.ts:
--------------------------------------------------------------------------------
1 | export default makeCurvePath;
2 | declare function makeCurvePath(endpoints?: any[], bend?: number, pinch?: number): string;
3 |
--------------------------------------------------------------------------------
/types/constructor/extensions/shared/transforms.d.ts:
--------------------------------------------------------------------------------
1 | export default TransformMethods;
2 | declare namespace TransformMethods {
3 | function clearTransform(el: any): any;
4 | }
5 |
--------------------------------------------------------------------------------
/types/arguments/makeViewBox.d.ts:
--------------------------------------------------------------------------------
1 | export default makeViewBox;
2 | /**
3 | * @returns {string | undefined}
4 | */
5 | declare function makeViewBox(...args: any[]): string | undefined;
6 |
--------------------------------------------------------------------------------
/types/environment/detect.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Rabbit Ear (c) Kraft
3 | */
4 | export const isBrowser: boolean;
5 | export const isBackend: boolean;
6 | export const isWebWorker: boolean;
7 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .git
3 | .gitignore
4 | .travis.yml
5 | .eslintrc.js
6 | rollup.config.js
7 | coverage/
8 | docs/
9 | examples/
10 | module/
11 | include/
12 | src/
13 | tests/
14 |
15 |
--------------------------------------------------------------------------------
/types/constructor/extensions/shared/makeArcPath.d.ts:
--------------------------------------------------------------------------------
1 | export default arcPath;
2 | declare function arcPath(x: any, y: any, radius: any, startAngle: any, endAngle: any, includeCenter?: boolean): string;
3 |
--------------------------------------------------------------------------------
/types/elements/constructors.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @type {{[key: string]: Function }}
3 | */
4 | export const constructors: {
5 | [key: string]: Function;
6 | };
7 | export function SVG(...args: any[]): any;
8 |
--------------------------------------------------------------------------------
/types/general/string.d.ts:
--------------------------------------------------------------------------------
1 | export function makeUUID(): string;
2 | export function toCamel(s: string): string;
3 | export function toKebab(s: string): string;
4 | export function capitalized(s: string): string;
5 |
--------------------------------------------------------------------------------
/types/general/transforms.d.ts:
--------------------------------------------------------------------------------
1 | export function parseTransform(transform: string): {
2 | transform: string;
3 | parameters: number[];
4 | }[];
5 | export function transformStringToMatrix(string: any): any;
6 |
--------------------------------------------------------------------------------
/src/spec/nodes.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Rabbit Ear (c) Kraft
3 | */
4 | import classes_nodes from "./classes_nodes.js";
5 |
6 | export const nodeNames = Object.values(classes_nodes).flat();
7 |
8 | export default classes_nodes;
9 |
--------------------------------------------------------------------------------
/types/constructor/extensions/shared/dom.d.ts:
--------------------------------------------------------------------------------
1 | export function removeChildren(element: any): any;
2 | export function appendTo(element: any, parent: any): any;
3 | export function setAttributes(element: any, attrs: any): any;
4 |
--------------------------------------------------------------------------------
/examples/code/images/play.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/tests/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/tests/environment.test.js:
--------------------------------------------------------------------------------
1 | import { expect, test } from "vitest";
2 |
3 | test("environment", () => {
4 | expect(true).toBe(true);
5 | // console.log("window", window);
6 | // console.log("window.document", window.document);
7 | });
8 |
--------------------------------------------------------------------------------
/tests/window.test.js:
--------------------------------------------------------------------------------
1 | import { expect, test } from "vitest";
2 | import xmldom from "@xmldom/xmldom";
3 | import SVG from "../src/index.js";
4 |
5 | SVG.window = xmldom;
6 |
7 | test("environment", () => {
8 | expect(true).toBe(true);
9 | });
10 |
--------------------------------------------------------------------------------
/src/spec/namespace.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Rabbit Ear (c) Kraft
3 | */
4 | /**
5 | * @description The XML namespace for SVG. The value of the SVG attribute xmlns.
6 | * @constant {string}
7 | * @default
8 | */
9 | export default "http://www.w3.org/2000/svg";
10 |
--------------------------------------------------------------------------------
/tests/clean.js:
--------------------------------------------------------------------------------
1 | import fs from "fs";
2 | // const fs = require("fs");
3 |
4 | // fs.existsSync(path)
5 | fs.readdirSync("./")
6 | .filter(s => s.match(/svg(.*).js/))
7 | // .forEach(path => console.log(path));
8 | .forEach(path => fs.unlinkSync(`./${path}`));
9 |
--------------------------------------------------------------------------------
/types/spec/classes_attributes.d.ts:
--------------------------------------------------------------------------------
1 | export default classes_attributes;
2 | declare namespace classes_attributes {
3 | let presentation: string[];
4 | let animation: string[];
5 | let effects: string[];
6 | let text: string[];
7 | let gradient: string[];
8 | }
9 |
--------------------------------------------------------------------------------
/types/environment/window.d.ts:
--------------------------------------------------------------------------------
1 | export function setWindow(newWindow: any): any;
2 | export default RabbitEarWindow;
3 | /**
4 | * @description get the "window" object, which should have
5 | * DOMParser, XMLSerializer, and document.
6 | */
7 | declare function RabbitEarWindow(): any;
8 |
--------------------------------------------------------------------------------
/src/colors/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Rabbit Ear (c) Kraft
3 | */
4 | import cssColors from "./cssColors.js";
5 | import * as convert from "./convert.js";
6 | import * as parseColor from "./parseColor.js";
7 |
8 | export default {
9 | cssColors,
10 | ...convert,
11 | ...parseColor,
12 | };
13 |
--------------------------------------------------------------------------------
/src/constructor/extensions/origami/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Rabbit Ear (c) Kraft
3 | */
4 | import init from "./init.js";
5 | import methods from "./methods.js";
6 |
7 | export default {
8 | origami: {
9 | nodeName: "g",
10 | init,
11 | args: () => [],
12 | methods,
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
/tests/svg.animation.test.js:
--------------------------------------------------------------------------------
1 | import { expect, test } from "vitest";
2 | import xmldom from "@xmldom/xmldom";
3 | import SVG from "../src/index.js";
4 |
5 | SVG.window = xmldom;
6 |
7 | test("animation", () => {
8 | const svg = SVG();
9 | svg.play = e => {};
10 | svg.stop();
11 | });
12 |
--------------------------------------------------------------------------------
/types/colors/convert.d.ts:
--------------------------------------------------------------------------------
1 | export function hslToRgb(hue: number, saturation: number, lightness: number, alpha: number | undefined): number[];
2 | export function hexToRgb(string: string): number[];
3 | export function rgbToHex(red: number, green: number, blue: number, alpha: number | undefined): string;
4 |
--------------------------------------------------------------------------------
/types/constructor/extensions/style.d.ts:
--------------------------------------------------------------------------------
1 | declare namespace _default {
2 | namespace style {
3 | function init(text: any): any;
4 | namespace methods {
5 | function setTextContent(el: any, text: any): any;
6 | }
7 | }
8 | }
9 | export default _default;
10 |
--------------------------------------------------------------------------------
/types/constructor/extensions/curve/methods.d.ts:
--------------------------------------------------------------------------------
1 | declare const _default: {
2 | clearTransform: (el: any) => any;
3 | setPoints: (element: any, ...args: any[]) => any;
4 | bend: (element: any, amount: any) => any;
5 | pinch: (element: any, amount: any) => any;
6 | };
7 | export default _default;
8 |
--------------------------------------------------------------------------------
/types/constructor/extensions/origami/methods.d.ts:
--------------------------------------------------------------------------------
1 | declare const _default: {
2 | removeChildren: (element: any) => any;
3 | appendTo: (element: any, parent: any) => any;
4 | setAttributes: (element: any, attrs: any) => any;
5 | clearTransform: (el: any) => any;
6 | };
7 | export default _default;
8 |
--------------------------------------------------------------------------------
/types/general/viewBox.d.ts:
--------------------------------------------------------------------------------
1 | export function setViewBox(element: any, ...args: any[]): any;
2 | export function getViewBox(element: any): any;
3 | export function convertToViewBox(svg: any, x: any, y: any): any[];
4 | export function foldToViewBox({ vertices_coords }: {
5 | vertices_coords: any;
6 | }): string;
7 |
--------------------------------------------------------------------------------
/examples/code/images/pause.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/types/constructor/extensions/svg/index.d.ts:
--------------------------------------------------------------------------------
1 | declare namespace _default {
2 | namespace svg {
3 | export function args(...args: any[]): string[];
4 | export { methods };
5 | export function init(...args: any[]): any;
6 | }
7 | }
8 | export default _default;
9 | import methods from "./methods.js";
10 |
--------------------------------------------------------------------------------
/examples/code/examples/albers2.js:
--------------------------------------------------------------------------------
1 | svg.size(50, 60);
2 | svg.background("white");
3 |
4 | svg.rect(50, 30).fill("steelblue");
5 | svg.rect(50, 30).origin(0, 30).fill("peru");
6 | svg.rect(10, 40).origin(20, 10).fill("#963"); // comment out this line
7 | svg.rect(50, 10).origin(0, 20).fill("midnightblue");
8 | svg.rect(50, 10).origin(0, 30).fill("#fe6");
9 |
--------------------------------------------------------------------------------
/src/constructor/extensions/curve/arguments.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Rabbit Ear (c) Kraft
3 | */
4 | import makeCoordinates from "../../../arguments/makeCoordinates.js";
5 | import makeCurvePath from "./makeCurvePath.js";
6 |
7 | const curveArguments = (...args) => [
8 | makeCurvePath(makeCoordinates(...args.flat())),
9 | ];
10 |
11 | export default curveArguments;
12 |
--------------------------------------------------------------------------------
/src/constructor/extensions/arrow/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Rabbit Ear (c) Kraft
3 | */
4 | import ArrowMethods from "./methods.js";
5 | import init from "./init.js";
6 |
7 | export default {
8 | arrow: {
9 | nodeName: "g",
10 | attributes: [],
11 | args: () => [], // one function
12 | methods: ArrowMethods, // object of functions
13 | init,
14 | },
15 | };
16 |
--------------------------------------------------------------------------------
/tests/svg.background.test.js:
--------------------------------------------------------------------------------
1 | import { expect, test } from "vitest";
2 | import xmldom from "@xmldom/xmldom";
3 | import SVG from "../src/index.js";
4 |
5 | SVG.window = xmldom;
6 |
7 | test("set background", () => {
8 | const svg = SVG();
9 | svg.background("black", true);
10 | svg.background("#332698", false);
11 | expect(svg.childNodes.length).toBe(1);
12 | });
13 |
--------------------------------------------------------------------------------
/types/constructor/extensions/origami/index.d.ts:
--------------------------------------------------------------------------------
1 | declare namespace _default {
2 | namespace origami {
3 | export let nodeName: string;
4 | export { init };
5 | export function args(): any[];
6 | export { methods };
7 | }
8 | }
9 | export default _default;
10 | import init from "./init.js";
11 | import methods from "./methods.js";
12 |
--------------------------------------------------------------------------------
/types/constructor/extensions/g.d.ts:
--------------------------------------------------------------------------------
1 | declare namespace _default {
2 | namespace g {
3 | let methods: {
4 | removeChildren: (element: any) => any;
5 | appendTo: (element: any, parent: any) => any;
6 | setAttributes: (element: any, attrs: any) => any;
7 | clearTransform: (el: any) => any;
8 | };
9 | }
10 | }
11 | export default _default;
12 |
--------------------------------------------------------------------------------
/examples/code/images/moon.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/code/examples/spiral.js:
--------------------------------------------------------------------------------
1 | svg.size(-10, -10, 20, 20);
2 | svg.background('white');
3 |
4 | var phipi = Math.PI / (Math.sqrt(5) + 1) * 4;
5 |
6 | var radius = 1 / 40;
7 | var size = 1 / 30;
8 |
9 | for(var i = 1; i < 360; i += 1) {
10 | var x = i * radius;
11 | var r = Math.pow(i, 0.5) * size;
12 | svg.regularPolygon(3, x, 0, r)
13 | .rotate(i * phipi * 180 / Math.PI)
14 | .fill("#000");
15 | }
16 |
--------------------------------------------------------------------------------
/src/constructor/extensions/curve/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Rabbit Ear (c) Kraft
3 | */
4 | import args from "./arguments.js";
5 | import curve_methods from "./methods.js";
6 | import { str_path } from "../../../environment/strings.js";
7 |
8 | export default {
9 | curve: {
10 | nodeName: str_path,
11 | attributes: ["d"],
12 | args, // one function
13 | methods: curve_methods, // object of functions
14 | },
15 | };
16 |
--------------------------------------------------------------------------------
/src/constructor/extensions/arrow/options.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Rabbit Ear (c) Kraft
3 | */
4 | const endOptions = () => ({
5 | visible: false,
6 | width: 8,
7 | height: 10,
8 | padding: 0.0,
9 | });
10 |
11 | const makeArrowOptions = () => ({
12 | head: endOptions(),
13 | tail: endOptions(),
14 | bend: 0.0,
15 | padding: 0.0,
16 | pinch: 0.618,
17 | points: [],
18 | });
19 |
20 | export {
21 | makeArrowOptions,
22 | };
23 |
--------------------------------------------------------------------------------
/examples/code/examples/parabola.js:
--------------------------------------------------------------------------------
1 | svg.size(-50, -50, 100, 100);
2 |
3 | svg.parabola().fill("#000a").rotate(0).translate(0, -30).scale(30, 60);
4 | svg.parabola().fill("#ec3a").rotate(90).translate(0, -30).scale(30, 60);
5 | svg.parabola().fill("#158a").rotate(270).translate(0, -30).scale(30, 60);
6 | svg.parabola().fill("#e53a").rotate(180).translate(0, -30).scale(30, 60);
7 |
8 | svg.rect(-30, -30, 60, 60).fill("none").stroke("black");
9 |
--------------------------------------------------------------------------------
/types/constructor/extensions/arrow/index.d.ts:
--------------------------------------------------------------------------------
1 | declare namespace _default {
2 | namespace arrow {
3 | export let nodeName: string;
4 | export let attributes: any[];
5 | export function args(): any[];
6 | export { ArrowMethods as methods };
7 | export { init };
8 | }
9 | }
10 | export default _default;
11 | import ArrowMethods from "./methods.js";
12 | import init from "./init.js";
13 |
--------------------------------------------------------------------------------
/src/constructor/extensions/origami/init.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Rabbit Ear (c) Kraft
3 | */
4 | import NS from "../../../spec/namespace.js";
5 | import window from "../../../environment/window.js";
6 | import lib from "../../../environment/lib.js";
7 |
8 | const init = (graph, ...args) => {
9 | const g = window().document.createElementNS(NS, "g");
10 | lib.ear.convert.foldToSvg.render(graph, g, ...args);
11 | return g;
12 | };
13 |
14 | export default init;
15 |
--------------------------------------------------------------------------------
/examples/code/examples/polygon.js:
--------------------------------------------------------------------------------
1 | svg.size(-1, -1, 2, 2);
2 | svg.background("white");
3 |
4 | // all children will inherit this style
5 | svg.stroke("black")
6 | .strokeWidth(0.001)
7 | .fill("none");
8 |
9 | // Math.tan(Math.PI/i) / Math.sin(Math.PI/i);
10 |
11 | for (var i = 3; i < 36; i += 1) {
12 | // all polygons are vertex-aligned along the +X axis
13 | // a -90 degree rotation aligns it with the Y
14 | svg.regularPolygon(i).rotate(-90);
15 | }
16 |
--------------------------------------------------------------------------------
/types/constructor/extensions/curve/index.d.ts:
--------------------------------------------------------------------------------
1 | declare namespace _default {
2 | namespace curve {
3 | export { str_path as nodeName };
4 | export let attributes: string[];
5 | export { args };
6 | export { curve_methods as methods };
7 | }
8 | }
9 | export default _default;
10 | import { str_path } from "../../../environment/strings.js";
11 | import args from "./arguments.js";
12 | import curve_methods from "./methods.js";
13 |
--------------------------------------------------------------------------------
/src/general/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Rabbit Ear (c) Kraft
3 | */
4 | import * as svgMath from "./algebra.js";
5 | import * as dom from "./dom.js";
6 | import * as cdata from "./cdata.js";
7 | import * as pathMethods from "./path.js";
8 | import * as transforms from "./transforms.js";
9 | import * as viewBox from "./viewBox.js";
10 |
11 | export default {
12 | ...svgMath,
13 | ...dom,
14 | ...cdata,
15 | ...pathMethods,
16 | ...transforms,
17 | ...viewBox,
18 | };
19 |
--------------------------------------------------------------------------------
/types/constructor/extensions/arrow/options.d.ts:
--------------------------------------------------------------------------------
1 | export function makeArrowOptions(): {
2 | head: {
3 | visible: boolean;
4 | width: number;
5 | height: number;
6 | padding: number;
7 | };
8 | tail: {
9 | visible: boolean;
10 | width: number;
11 | height: number;
12 | padding: number;
13 | };
14 | bend: number;
15 | padding: number;
16 | pinch: number;
17 | points: any[];
18 | };
19 |
--------------------------------------------------------------------------------
/types/spec/classes_nodes.d.ts:
--------------------------------------------------------------------------------
1 | export default classes_nodes;
2 | declare namespace classes_nodes {
3 | let svg: string[];
4 | let defs: string[];
5 | let header: string[];
6 | let cdata: string[];
7 | let group: string[];
8 | let visible: string[];
9 | let text: string[];
10 | let invisible: string[];
11 | let patterns: string[];
12 | let childrenOfText: string[];
13 | let gradients: string[];
14 | let filter: string[];
15 | }
16 |
--------------------------------------------------------------------------------
/types/arguments/makeCoordinates.d.ts:
--------------------------------------------------------------------------------
1 | export default makeCoordinates;
2 | /**
3 | * this will extract coordinates from a set of inputs
4 | * and present them as a stride-2 flat array. length % 2 === 0
5 | * a 1D array of numbers, alternating x y
6 | *
7 | * use flatten() everytime you call this!
8 | * it's necessary the entries sit at the top level of ...args
9 | * findCoordinates(...flatten(...args));
10 | */
11 | declare function makeCoordinates(...args: any[]): any[];
12 |
--------------------------------------------------------------------------------
/types/constructor/extensions/text.d.ts:
--------------------------------------------------------------------------------
1 | declare namespace _default {
2 | namespace text {
3 | function args(a: any, b: any, c: any): any[];
4 | function init(a: any, b: any, c: any, d: any): any;
5 | let methods: {
6 | appendTo: (element: any, parent: any) => any;
7 | setAttributes: (element: any, attrs: any) => any;
8 | clearTransform: (el: any) => any;
9 | };
10 | }
11 | }
12 | export default _default;
13 |
--------------------------------------------------------------------------------
/examples/code/examples/arrows.js:
--------------------------------------------------------------------------------
1 | svg.size(1, 1);
2 | svg.background("black");
3 |
4 | let arrowhead = svg.marker()
5 | .setViewBox(0, -1, 2, 2)
6 | .orient("auto-start-reverse");
7 |
8 | arrowhead.polygon(0, 1, 2, 0, 0, -1).fill("white");
9 |
10 | for (var i = 0; i < 10; i += 1) {
11 | svg.curve(Math.random(), Math.random(), Math.random(), Math.random())
12 | .fill("none")
13 | .stroke("white")
14 | .strokeWidth(0.02)
15 | .bend(0.5)
16 | .markerEnd(arrowhead)
17 | .markerStart(arrowhead);
18 | }
19 |
--------------------------------------------------------------------------------
/examples/load-file.html:
--------------------------------------------------------------------------------
1 |
2 | SVG example: load an SVG file
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/types/constructor/extensions/arrow/makeArrowPaths.d.ts:
--------------------------------------------------------------------------------
1 | export default makeArrowPaths;
2 | /**
3 | * @description
4 | * @param {{
5 | * points: [number, number, number, number],
6 | * padding: number,
7 | * bend: number,
8 | * pinch: number,
9 | * }} options
10 | */
11 | declare function makeArrowPaths(options: {
12 | points: [number, number, number, number];
13 | padding: number;
14 | bend: number;
15 | pinch: number;
16 | }): {
17 | line: string;
18 | tail: string;
19 | head: string;
20 | };
21 |
--------------------------------------------------------------------------------
/examples/code/examples/text.js:
--------------------------------------------------------------------------------
1 | svg.size(100, 100);
2 | svg.background("black", true);
3 |
4 | var style = {
5 | fontFamily: "avenir next, helvetica neue, arial",
6 | fontWeight: 400,
7 | fontSize: "16px",
8 | textAnchor: "middle"
9 | };
10 |
11 | for (var j = 0; j < 7; j += 1) {
12 | for (var i = 0; i < 9; i += 1) {
13 | var x = 50 + 2 - 0.5 * i;
14 | var y = 0 + j * 18 + 0.5 * i;
15 | svg.text(["los angeles", "new york"][j % 2], x, y)
16 | .setAttributes(style)
17 | .fill(`rgba(255, 0, ${150 + j * 15}, 0.25)`);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/examples/code/images/download.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/types/constructor/extensions/line.d.ts:
--------------------------------------------------------------------------------
1 | declare namespace _default {
2 | namespace line {
3 | export { Args as args };
4 | export let methods: {
5 | removeChildren: (element: any) => any;
6 | appendTo: (element: any, parent: any) => any;
7 | setAttributes: (element: any, attrs: any) => any;
8 | clearTransform: (el: any) => any;
9 | setPoints: (element: any, ...args: any[]) => any;
10 | };
11 | }
12 | }
13 | export default _default;
14 | declare function Args(...args: any[]): any[];
15 |
--------------------------------------------------------------------------------
/types/general/path.d.ts:
--------------------------------------------------------------------------------
1 | export namespace pathCommandNames {
2 | let m: string;
3 | let l: string;
4 | let v: string;
5 | let h: string;
6 | let a: string;
7 | let c: string;
8 | let s: string;
9 | let q: string;
10 | let t: string;
11 | let z: string;
12 | }
13 | export function parsePathCommands(d: string): {
14 | command: string;
15 | values: number[];
16 | }[];
17 | export function parsePathCommandsWithEndpoints(d: any): {
18 | end: any;
19 | start: any;
20 | command: string;
21 | values: number[];
22 | }[];
23 |
--------------------------------------------------------------------------------
/types/constructor/extensions/arc/index.d.ts:
--------------------------------------------------------------------------------
1 | declare namespace _default {
2 | namespace arc {
3 | export { str_path as nodeName };
4 | export let attributes: string[];
5 | export { arcArguments as args };
6 | export let methods: {
7 | clearTransform: (el: any) => any;
8 | setArc: (el: any, ...args: any[]) => any;
9 | };
10 | }
11 | }
12 | export default _default;
13 | import { str_path } from "../../../environment/strings.js";
14 | declare function arcArguments(a: any, b: any, c: any, d: any, e: any): string[];
15 |
--------------------------------------------------------------------------------
/types/general/dom.d.ts:
--------------------------------------------------------------------------------
1 | export function xmlStringToElement(input: string, mimeType?: string): Element | null;
2 | export function getRootParent(el: Element): Element;
3 | export function findElementTypeInParents(element: Element, nodeName: string): Element | null;
4 | export function addClass(el: Element, ...classes: string[]): void;
5 | export function flattenDomTree(el: Element | ChildNode): (Element | ChildNode)[];
6 | export function flattenDomTreeWithStyle(element: Element | ChildNode, attributes?: object): {
7 | element: Element | ChildNode;
8 | attributes: object;
9 | }[];
10 |
--------------------------------------------------------------------------------
/types/constructor/extensions/wedge/index.d.ts:
--------------------------------------------------------------------------------
1 | declare namespace _default {
2 | namespace wedge {
3 | export { str_path as nodeName };
4 | export { wedgeArguments as args };
5 | export let attributes: string[];
6 | export let methods: {
7 | clearTransform: (el: any) => any;
8 | setArc: (el: any, ...args: any[]) => any;
9 | };
10 | }
11 | }
12 | export default _default;
13 | import { str_path } from "../../../environment/strings.js";
14 | declare function wedgeArguments(a: any, b: any, c: any, d: any, e: any): string[];
15 |
--------------------------------------------------------------------------------
/src/constructor/extensions/arc/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Rabbit Ear (c) Kraft
3 | */
4 | import makeArcPath from "../shared/makeArcPath.js";
5 | import { str_path } from "../../../environment/strings.js";
6 | import TransformMethods from "../shared/transforms.js";
7 |
8 | const arcArguments = (a, b, c, d, e) => [makeArcPath(a, b, c, d, e, false)];
9 |
10 | export default {
11 | arc: {
12 | nodeName: str_path,
13 | attributes: ["d"],
14 | args: arcArguments,
15 | methods: {
16 | setArc: (el, ...args) => el.setAttribute("d", arcArguments(...args)),
17 | ...TransformMethods,
18 | },
19 | },
20 | };
21 |
--------------------------------------------------------------------------------
/examples/code/examples/clip-path.js:
--------------------------------------------------------------------------------
1 | svg.size(100, 100);
2 | svg.background("#edb");
3 |
4 | // a clip-path will hide or show parts of shapes.
5 | // presence of a filled-shape indicates visibility
6 | var clip = svg.clipPath();
7 | clip.circle(70.7).origin(50, 0); // the visible portion
8 |
9 | // three circles, untouched
10 | svg.circle(0, 50, 50).fill("#e53");
11 | svg.circle(50, 50, 50).fill("#158").opacity(0.75);
12 | svg.circle(100, 50, 50).fill("#ec3");
13 |
14 | // this circle will be clipped by the clip-path
15 | svg.circle(50, 100, 70.7)
16 | .fill("#e53")
17 | .opacity(0.75)
18 | .clipPath(clip);
19 |
--------------------------------------------------------------------------------
/src/constructor/extensions/svg/getSVGFrame.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Rabbit Ear (c) Kraft
3 | */
4 | import {
5 | str_function,
6 | } from "../../../environment/strings.js";
7 | import { getViewBox } from "../../../general/viewBox.js";
8 |
9 | const getSVGFrame = function (element) {
10 | const viewBox = getViewBox(element);
11 | if (viewBox !== undefined) {
12 | return viewBox;
13 | }
14 | if (typeof element.getBoundingClientRect === str_function) {
15 | const rr = element.getBoundingClientRect();
16 | return [rr.x, rr.y, rr.width, rr.height];
17 | }
18 | return [];
19 | };
20 |
21 | export default getSVGFrame;
22 |
--------------------------------------------------------------------------------
/src/constructor/extensions/wedge/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Rabbit Ear (c) Kraft
3 | */
4 | import makeArcPath from "../shared/makeArcPath.js";
5 | import { str_path } from "../../../environment/strings.js";
6 | import TransformMethods from "../shared/transforms.js";
7 |
8 | const wedgeArguments = (a, b, c, d, e) => [makeArcPath(a, b, c, d, e, true)];
9 |
10 | export default {
11 | wedge: {
12 | nodeName: str_path,
13 | args: wedgeArguments,
14 | attributes: ["d"],
15 | methods: {
16 | setArc: (el, ...args) => el.setAttribute("d", wedgeArguments(...args)),
17 | ...TransformMethods,
18 | },
19 | },
20 | };
21 |
--------------------------------------------------------------------------------
/src/general/cdata.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Rabbit Ear (c) Kraft
3 | */
4 | import window from "../environment/window.js";
5 |
6 | /**
7 | * @description Create a CDATASection containing text from the method
8 | * parameter. The CDATA is useful to wrap text which may contain
9 | * invalid characters or characters in need of escaping.
10 | * @param {string} text the text content to be placed inside the CData
11 | * @returns {CDATASection} a CDATA containing the given text.
12 | */
13 | export const makeCDATASection = (text) => (new (window()).DOMParser())
14 | .parseFromString("", "text/xml")
15 | .createCDATASection(text);
16 |
--------------------------------------------------------------------------------
/types/spec/nodes_attributes.d.ts:
--------------------------------------------------------------------------------
1 | export default nodes_attributes;
2 | declare namespace nodes_attributes {
3 | let svg: string[];
4 | let line: string[];
5 | let rect: string[];
6 | let circle: string[];
7 | let ellipse: string[];
8 | let polygon: string[];
9 | let polyline: string[];
10 | let path: string[];
11 | let text: string[];
12 | let mask: string[];
13 | let symbol: string[];
14 | let clipPath: string[];
15 | let marker: string[];
16 | let linearGradient: string[];
17 | let radialGradient: string[];
18 | let stop: string[];
19 | let pattern: string[];
20 | }
21 |
--------------------------------------------------------------------------------
/types/constructor/extensions/arrow/methods.d.ts:
--------------------------------------------------------------------------------
1 | declare const _default: {
2 | clearTransform: (el: any) => any;
3 | setPoints: (element: any, ...args: any[]) => any;
4 | points: (element: any, ...args: any[]) => any;
5 | bend: (element: any, amount: any) => any;
6 | pinch: (element: any, amount: any) => any;
7 | padding: (element: any, amount: any) => any;
8 | head: (element: any, options: any) => any;
9 | tail: (element: any, options: any) => any;
10 | getLine: (element: any) => any;
11 | getHead: (element: any) => any;
12 | getTail: (element: any) => any;
13 | };
14 | export default _default;
15 |
--------------------------------------------------------------------------------
/types/spec/nodes_children.d.ts:
--------------------------------------------------------------------------------
1 | export default nodes_children;
2 | declare namespace nodes_children {
3 | export let svg: string[];
4 | export { headerStuff as defs };
5 | export let filter: string[];
6 | export { drawingShapes as g };
7 | export let text: string[];
8 | export { drawingShapes as marker };
9 | export { drawingShapes as symbol };
10 | export { drawingShapes as clipPath };
11 | export { drawingShapes as mask };
12 | export let linearGradient: string[];
13 | export let radialGradient: string[];
14 | }
15 | declare const headerStuff: string[];
16 | declare const drawingShapes: string[];
17 |
--------------------------------------------------------------------------------
/tests/svg.controls.test.js:
--------------------------------------------------------------------------------
1 | import { expect, test } from "vitest";
2 | import xmldom from "@xmldom/xmldom";
3 | import SVG from "../src/index.js";
4 |
5 | SVG.window = xmldom;
6 |
7 | test("deprecated", () => expect(true).toBe(true));
8 |
9 | // test("controls", () => {
10 | // const svg = SVG();
11 | // const controlLayer = svg.g();
12 | // svg.controls(3)
13 | // .svg(() => SVG.circle(svg.getWidth() * 0.1).fill("gray"))
14 | // .position(() => [svg.getWidth() * 0.5, svg.getHeight() * 0.5])
15 | // .parent(controlLayer)
16 | // .onChange((point) => {
17 | // point.svg.setRadius(Math.random() * 10);
18 | // }, true);
19 | // });
20 |
--------------------------------------------------------------------------------
/examples/code/examples/ten-print.js:
--------------------------------------------------------------------------------
1 | svg.size(40, 25);
2 | svg.background("#329");
3 |
4 | // apply style to the layer and all children will inherit
5 | var layer = svg.g()
6 | .stroke("#76d")
7 | .strokeWidth("1%")
8 | .strokeLinecap("square");
9 |
10 | // a 2d-grid of either / or \
11 | // the column and row sizes are 1px
12 | for (var y = 0; y < 25; y += 1) {
13 | for (var x = 0; x < 40; x += 1) {
14 | // a random 0 or 1, and "b" will be the opposite
15 | var a = Math.random() - 0.5 > 0 ? 0 : 1;
16 | var b = a ? 0 : 1;
17 | // a diagonal line, upwards or downwards
18 | layer.line(x, y + a, x + 1, y + b);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/examples/japanese-flag.html:
--------------------------------------------------------------------------------
1 |
2 | SVG example: Japanese flag
3 |
14 |
15 |
26 |
--------------------------------------------------------------------------------
/src/constructor/extensions/shared/dom.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Rabbit Ear (c) Kraft
3 | */
4 | import { toKebab } from "../../../general/string.js";
5 |
6 | export const removeChildren = (element) => {
7 | while (element.lastChild) {
8 | element.removeChild(element.lastChild);
9 | }
10 | return element;
11 | };
12 |
13 | export const appendTo = (element, parent) => {
14 | if (parent && parent.appendChild) {
15 | parent.appendChild(element);
16 | }
17 | return element;
18 | };
19 |
20 | export const setAttributes = (element, attrs) => {
21 | Object.keys(attrs)
22 | .forEach(key => element.setAttribute(toKebab(key), attrs[key]));
23 | return element;
24 | };
25 |
--------------------------------------------------------------------------------
/examples/code/examples/curves.js:
--------------------------------------------------------------------------------
1 | svg.size(100, 100);
2 | svg.background("white");
3 |
4 | var colors = ["#e53", "#158", "#ec3"];
5 |
6 | var pts = [];
7 | for (var i = 0; i < 8; i += 1) {
8 | pts.push([Math.random() * 100, Math.random() * 100]);
9 | }
10 |
11 | for (var i = 0; i < pts.length - 1; i += 1) {
12 | var color = colors[Math.floor(Math.random()*3)];
13 | var width = Math.random() * 12;
14 | var rand1 = Math.random() < 0.5;
15 | var rand2 = Math.random() < 0.8;
16 |
17 | svg.curve(pts[i], pts[i+1])
18 | .fill("none")
19 | .stroke(color)
20 | .strokeWidth(width)
21 | .strokeDasharray(rand2 ? "none" : Math.random() * 8 + 2)
22 | .bend(rand1 ? 0 : 0.5);
23 | }
24 |
--------------------------------------------------------------------------------
/src/constructor/extensions/style.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Rabbit Ear (c) Kraft
3 | */
4 | import svgNS from "../../spec/namespace.js";
5 | import window from "../../environment/window.js";
6 | import { makeCDATASection } from "../../general/cdata.js";
7 |
8 | export default {
9 | style: {
10 | init: (text) => {
11 | const el = window().document.createElementNS(svgNS, "style");
12 | el.setAttribute("type", "text/css");
13 | el.textContent = "";
14 | el.appendChild(makeCDATASection(text));
15 | return el;
16 | },
17 | methods: {
18 | setTextContent: (el, text) => {
19 | el.textContent = "";
20 | el.appendChild(makeCDATASection(text));
21 | return el;
22 | },
23 | },
24 | },
25 | };
26 |
--------------------------------------------------------------------------------
/examples/code/examples/astroid.js:
--------------------------------------------------------------------------------
1 | svg.size(-256, -256, 512, 512);
2 | svg.background("black");
3 |
4 | // all children of this group inherit this style
5 | var layer = svg.g().stroke("white");
6 |
7 | var segments = 30;
8 |
9 | var w = svg.getWidth() / 2 / segments;
10 | var h = svg.getHeight() / 2 / segments;
11 |
12 | // "i" will increment from 0 to (segments - 1)
13 | for (var i = 0; i < segments; i += 1) {
14 | // "j" will decrement from segments to 1
15 | var j = segments - i;
16 | layer.line(-w * i, 0, 0, -h * j); // top left
17 | layer.line(w * j, 0, 0, -h * i); // top right
18 | layer.line(w * i, 0, 0, h * j); // bottom right
19 | layer.line(-w * j, 0, 0, h * i); // bottom left
20 | }
21 |
--------------------------------------------------------------------------------
/examples/albers.html:
--------------------------------------------------------------------------------
1 |
2 | SVG example: introduction
3 |
4 |
5 |
6 |
25 |
--------------------------------------------------------------------------------
/examples/code/examples/riley1.js:
--------------------------------------------------------------------------------
1 | svg.size(100, 100);
2 |
3 | var space = random(3, 6);
4 | var clip = svg.clipPath();
5 | clip.rect(100, 100);
6 |
7 | for (var i = 0; i < (100 + space) / space; i += 1) {
8 | let p = svg.path()
9 | .fill("none")
10 | .strokeWidth(space)
11 | .strokeLinecap("square")
12 | .stroke(i % 2 === 0 ? "#000" : "#fff")
13 | .clipPath(clip)
14 | .Move(i * space, 0);
15 |
16 | for (var j = 0; j < 10; j += 1) {
17 | var dir = (j % 2) ? 0.5 : -0.5;
18 | var controlA = [(i + dir) * space, (j + 0.333) * 10];
19 | var controlB = [(i + dir) * space, (j + 0.666) * 10];
20 | var end = [i * space, (j + 1) * 10];
21 | p.Curve(controlA, controlB, end);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/environment/detect.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Rabbit Ear (c) Kraft
3 | */
4 |
5 | const isBrowser = typeof window === "object"
6 | && typeof window.document === "object";
7 |
8 | const isNodeOrBun = typeof process === "object"
9 | && typeof process.versions === "object"
10 | && (process.versions.node != null || process.versions.bun != null);
11 |
12 | const isDeno = typeof window === "object"
13 | && "Deno" in window
14 | && typeof window.Deno === "object";
15 |
16 | const isBackend = isNodeOrBun || isDeno;
17 |
18 | const isWebWorker = typeof self === "object"
19 | && self.constructor
20 | && self.constructor.name === "DedicatedWorkerGlobalScope";
21 |
22 | export {
23 | isBrowser,
24 | isBackend,
25 | isWebWorker,
26 | };
27 |
--------------------------------------------------------------------------------
/types/general/algebra.d.ts:
--------------------------------------------------------------------------------
1 | export function svg_add2(a: [number, number], b: [number, number]): [number, number];
2 | export function svg_sub2(a: [number, number], b: [number, number]): [number, number];
3 | export function svg_scale2(a: [number, number], s: number): [number, number];
4 | export function svg_magnitudeSq2(a: [number, number]): number;
5 | export function svg_magnitude2(a: [number, number]): number;
6 | export function svg_distanceSq2(a: [number, number], b: [number, number]): number;
7 | export function svg_distance2(a: [number, number], b: [number, number]): number;
8 | export function svg_polar_to_cart(a: number, d: number): [number, number];
9 | export function svg_multiplyMatrices2(m1: number[], m2: number[]): number[];
10 |
--------------------------------------------------------------------------------
/examples/code/images/share.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/code/examples/albers1.js:
--------------------------------------------------------------------------------
1 | svg.size(90, 90);
2 | svg.background("#9590c0");
3 |
4 | // all shapes will be drawn at the point 0, 0
5 | // shifting the layer will center the shapes
6 | var g = svg.g().translate(35, 15);
7 |
8 | // these attributes will be applied to every shape
9 | // this is useful when you have many similar-looking shapes
10 | var style = { fill: "#2c266d", opacity: 0.4 };
11 |
12 | // transform-origin moves the center of rotation
13 | // right rect
14 | g.rect(30, 50)
15 | .setAttributes(style)
16 | .transformOrigin("0 50")
17 | .rotate(25);
18 |
19 | // no rotation
20 | // center rect
21 | g.rect(30, 50).setAttributes(style);
22 |
23 | // left rect
24 | g.rect(30, 50)
25 | .setAttributes(style)
26 | .transformOrigin("0 50")
27 | .rotate(-25);
28 |
--------------------------------------------------------------------------------
/examples/code/examples/draw.js:
--------------------------------------------------------------------------------
1 | svg.size(100, 100);
2 |
3 | // keep track of every touch
4 | var points = [];
5 |
6 | // a polygon is a filled shape
7 | // defined by an array of points
8 | var p = svg.polygon()
9 | .fillRule("evenodd")
10 | .fill("#e53")
11 | .stroke("#158");
12 | // "evenodd" is a cool effect for
13 | // when the polygon self-intersects
14 |
15 | // every time a touch moves, add a point to the array
16 | svg.onMove = function (mouse) {
17 | points.push([mouse.x, mouse.y]);
18 | // only 100 points are allowed
19 | if (points.length > 100) { points.shift(); }
20 | // update the polygon with the current set of points
21 | p.setPoints(points);
22 | };
23 |
24 | ///////////////
25 | // //
26 | // draw 🌀🖌 //
27 | // //
28 | ///////////////
29 |
--------------------------------------------------------------------------------
/types/constructor/extensions/svg/methods.d.ts:
--------------------------------------------------------------------------------
1 | export function findOneElement(element: any, nodeName: any): any;
2 | declare const _default: {
3 | removeChildren: (element: any) => any;
4 | appendTo: (element: any, parent: any) => any;
5 | setAttributes: (element: any, attrs: any) => any;
6 | clearTransform: (el: any) => any;
7 | clear: (element: any) => any;
8 | size: (element: any, ...args: any[]) => any;
9 | setViewBox: (element: any, ...args: any[]) => any;
10 | getViewBox: (element: any) => any;
11 | padding: (element: any, padding: any) => any;
12 | background: (element: any, color: any) => any;
13 | getWidth: (el: any) => any;
14 | getHeight: (el: any) => any;
15 | stylesheet: (el: any, text: any) => any;
16 | };
17 | export default _default;
18 |
--------------------------------------------------------------------------------
/tests/use.test.js:
--------------------------------------------------------------------------------
1 | import { expect, test } from "vitest";
2 | import xmldom from "@xmldom/xmldom";
3 | import SVG from "../src/index.js";
4 |
5 | SVG.window = xmldom;
6 | // const ear = require("./ear");
7 |
8 | test("", () => expect(true).toBe(true));
9 |
10 | // test("use with rabbit ear", () => {
11 | // ear.use(SVG);
12 | // expect(typeof ear.segment.svg).toBe("function");
13 | // expect(typeof ear.segment.svg).toBe("function");
14 | // expect(typeof ear.circle.svg).toBe("function");
15 | // expect(typeof ear.ellipse.svg).toBe("function");
16 | // expect(typeof ear.rect.svg).toBe("function");
17 | // expect(typeof ear.polygon.svg).toBe("function");
18 | // ear.segment.svg();
19 | // ear.circle.svg();
20 | // ear.ellipse.svg();
21 | // ear.rect.svg();
22 | // ear.polygon.svg();
23 | // });
24 |
--------------------------------------------------------------------------------
/src/constructor/extensions/shared/transforms.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Rabbit Ear (c) Kraft
3 | */
4 | import { str_transform } from "../../../environment/strings.js";
5 |
6 | const getAttr = (element) => {
7 | const t = element.getAttribute(str_transform);
8 | return (t == null || t === "") ? undefined : t;
9 | };
10 |
11 | const TransformMethods = {
12 | clearTransform: (el) => { el.removeAttribute(str_transform); return el; },
13 | };
14 |
15 | ["translate", "rotate", "scale", "matrix"].forEach(key => {
16 | TransformMethods[key] = (element, ...args) => {
17 | element.setAttribute(
18 | str_transform,
19 | [getAttr(element), `${key}(${args.join(" ")})`]
20 | .filter(a => a !== undefined)
21 | .join(" "),
22 | );
23 | return element;
24 | };
25 | });
26 |
27 | export default TransformMethods;
28 |
--------------------------------------------------------------------------------
/examples/random-walker.html:
--------------------------------------------------------------------------------
1 |
2 | SVG example: random walker
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/types/constructor/extensions/rect.d.ts:
--------------------------------------------------------------------------------
1 | declare namespace _default {
2 | namespace rect {
3 | function args(a: any, b: any, c: any, d: any): any;
4 | let methods: {
5 | removeChildren: (element: any) => any;
6 | appendTo: (element: any, parent: any) => any;
7 | setAttributes: (element: any, attrs: any) => any;
8 | clearTransform: (el: any) => any;
9 | origin: (el: any, a: any, b: any) => any;
10 | setOrigin: (el: any, a: any, b: any) => any;
11 | center: (el: any, a: any, b: any) => any;
12 | setCenter: (el: any, a: any, b: any) => any;
13 | size: (el: any, rx: any, ry: any) => any;
14 | setSize: (el: any, rx: any, ry: any) => any;
15 | };
16 | }
17 | }
18 | export default _default;
19 |
--------------------------------------------------------------------------------
/types/environment/strings.d.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Rabbit Ear (c) Kraft
3 | */
4 | export const str_class: "class";
5 | export const str_function: "function";
6 | export const str_undefined: "undefined";
7 | export const str_boolean: "boolean";
8 | export const str_number: "number";
9 | export const str_string: "string";
10 | export const str_object: "object";
11 | export const str_svg: "svg";
12 | export const str_path: "path";
13 | export const str_id: "id";
14 | export const str_style: "style";
15 | export const str_viewBox: "viewBox";
16 | export const str_transform: "transform";
17 | export const str_points: "points";
18 | export const str_stroke: "stroke";
19 | export const str_fill: "fill";
20 | export const str_none: "none";
21 | export const str_arrow: "arrow";
22 | export const str_head: "head";
23 | export const str_tail: "tail";
24 |
--------------------------------------------------------------------------------
/tests/object.assign.test.js:
--------------------------------------------------------------------------------
1 | import { expect, test } from "vitest";
2 |
3 | const test1 = {
4 | a: {
5 | one: {
6 | what: true,
7 | },
8 | two: {
9 | whatagain: true,
10 | },
11 | },
12 | b: {
13 | a: {
14 | what: true,
15 | },
16 | },
17 | };
18 |
19 | const test2 = {
20 | a: {
21 | three: {
22 | hi: true,
23 | },
24 | four: {
25 | whatagain: true,
26 | },
27 | },
28 | b: {
29 | b: {
30 | okay: true,
31 | },
32 | },
33 | };
34 |
35 | test("object assign", () => {
36 | Object.assign(test1, test2);
37 |
38 | expect(test1.a.one == null).toBe(true);
39 | expect(test1.a.two == null).toBe(true);
40 | expect(test1.a.three != null).toBe(true);
41 | expect(test1.a.four != null).toBe(true);
42 | expect(test1.b.a == null).toBe(true);
43 | expect(test1.b.b != null).toBe(true);
44 | });
45 |
--------------------------------------------------------------------------------
/types/constructor/extensions/path.d.ts:
--------------------------------------------------------------------------------
1 | declare namespace _default {
2 | namespace path {
3 | export { path_methods as methods };
4 | }
5 | }
6 | export default _default;
7 | declare const path_methods: {
8 | removeChildren: (element: any) => any;
9 | appendTo: (element: any, parent: any) => any;
10 | setAttributes: (element: any, attrs: any) => any;
11 | clearTransform: (el: any) => any;
12 | addCommand: (el: any, command: any, ...args: any[]) => any;
13 | appendCommand: (el: any, command: any, ...args: any[]) => any;
14 | clear: (element: any) => any;
15 | getCommands: (element: any) => {
16 | command: string;
17 | values: number[];
18 | }[];
19 | get: (element: any) => {
20 | command: string;
21 | values: number[];
22 | }[];
23 | getD: (el: any) => any;
24 | };
25 |
--------------------------------------------------------------------------------
/examples/code/examples/bezier.js:
--------------------------------------------------------------------------------
1 | svg.size(800, 800);
2 |
3 | // create a layer behind everything
4 | var back = svg.g();
5 |
6 | // shapes on top
7 | var curve = svg.path();
8 | var l1 = svg.line().stroke("black");
9 | var l2 = svg.line().stroke("black");
10 |
11 | // these are control points
12 | // 1.create an svg element to track with each point
13 | // 2.assign a position
14 | // 3.append to a parent
15 | // 4.onChange event handler with "this" bound to the controls
16 | svg.controls(4)
17 | .svg(function () { return SVG.circle(svg.getWidth() * 0.05).fill("#e53"); })
18 | .position(function () { return [random(svg.getWidth()), random(svg.getHeight())]; })
19 | .parent(back)
20 | .onChange(function () {
21 | l1.setPoints(this[0], this[1]);
22 | l2.setPoints(this[3], this[2]);
23 | curve.clear().Move(this[0]).Curve(this[1], this[2], this[3]);
24 | }, true);
25 |
--------------------------------------------------------------------------------
/examples/mask.html:
--------------------------------------------------------------------------------
1 |
2 | SVG example: introduction
3 |
4 |
5 |
26 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": ["src/**/*"],
3 | "exclude": ["src/primitives/**/*", "src/sketches/**/*"],
4 | "compilerOptions": {
5 | // not sure if these should be here
6 | "target": "ES2015",
7 | // "module": "es2015",
8 | "lib": ["es2015", "DOM"],
9 | // Tells TypeScript to read JS files, as
10 | // normally they are ignored as source files
11 | "allowJs": true,
12 | "checkJs": true,
13 | // Generate d.ts files
14 | "declaration": true,
15 | // This compiler run should
16 | // only output d.ts files
17 | "emitDeclarationOnly": true,
18 | // Types should go into this directory.
19 | // Removing this would place the .d.ts files
20 | // next to the .js files
21 | "outDir": "types"
22 | // go to js file when using IDE functions like
23 | // "Go to Definition" in VSCode
24 | // "declarationMap": true
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/environment/strings.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Rabbit Ear (c) Kraft
3 | */
4 | // frequently-used strings
5 | export const str_class = "class";
6 | export const str_function = "function";
7 | export const str_undefined = "undefined";
8 | export const str_boolean = "boolean";
9 | export const str_number = "number";
10 | export const str_string = "string";
11 | export const str_object = "object";
12 |
13 | export const str_svg = "svg";
14 | export const str_path = "path";
15 |
16 | export const str_id = "id";
17 | export const str_style = "style";
18 | export const str_viewBox = "viewBox";
19 | export const str_transform = "transform";
20 | export const str_points = "points";
21 | export const str_stroke = "stroke";
22 | export const str_fill = "fill";
23 | export const str_none = "none";
24 |
25 | export const str_arrow = "arrow";
26 | export const str_head = "head";
27 | export const str_tail = "tail";
28 |
--------------------------------------------------------------------------------
/examples/code/examples/mask.js:
--------------------------------------------------------------------------------
1 | svg.size(1, 1);
2 | svg.background("#edb");
3 |
4 | // a mask will hide or show parts of shapes
5 | // depending on the contents. white: visible, black: invisible
6 | var maskA = svg.mask();
7 | var maskB = svg.mask();
8 |
9 | // a polygon from 100 connected random points
10 | var points = Array.from(Array(100))
11 | .map(() => [random(-1, 2), random(-1, 2)]);
12 |
13 | // each mask gets the same polygon
14 | // but the polygon and background color alternate
15 | // white/black and black/white
16 | maskA.polygon(points).fill("white").fillRule("evenodd");
17 | maskB.rect(1, 1).fill("white");
18 | maskB.polygon(points).fill("black").fillRule("evenodd");
19 |
20 | // two circles, assigned to different masks
21 | svg.circle(random(), random(), 0.5)
22 | .fill("black")
23 | .mask(maskA);
24 |
25 | svg.circle(random(), random(), 0.5)
26 | .fill("#e53")
27 | .mask(maskB);
28 |
--------------------------------------------------------------------------------
/tests/stylesheet.test.js:
--------------------------------------------------------------------------------
1 | import { expect, test } from "vitest";
2 | import xmldom from "@xmldom/xmldom";
3 | import SVG from "../src/index.js";
4 |
5 | SVG.window = xmldom;
6 |
7 | test("style", () => {
8 | const styleString = "line{stroke:purple};";
9 | const svg = SVG();
10 | svg.stylesheet(styleString);
11 | const style = Array.from(svg.childNodes).filter(a => a.nodeName === "style").shift();
12 | expect(style.childNodes[0].textContent).toBe(styleString);
13 | });
14 |
15 | test("style setTextContent", () => {
16 | const styleString = "line{stroke:purple};";
17 | const styleString2 = "circle { fill: '#000' }";
18 | const svg = SVG();
19 | const stylesheet = svg.stylesheet(styleString);
20 | stylesheet.setTextContent(styleString2);
21 | const style = Array.from(svg.childNodes).filter(a => a.nodeName === "style").shift();
22 | expect(style.childNodes[0].textContent).toBe(styleString2);
23 | });
24 |
--------------------------------------------------------------------------------
/src/constructor/extensions/shared/makeArcPath.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Rabbit Ear (c) Kraft
3 | */
4 | import { svg_polar_to_cart } from "../../../general/algebra.js";
5 |
6 | const arcPath = (x, y, radius, startAngle, endAngle, includeCenter = false) => {
7 | if (endAngle == null) { return ""; }
8 | const start = svg_polar_to_cart(startAngle, radius);
9 | const end = svg_polar_to_cart(endAngle, radius);
10 | const arcVec = [end[0] - start[0], end[1] - start[1]];
11 | const py = start[0] * end[1] - start[1] * end[0];
12 | const px = start[0] * end[0] + start[1] * end[1];
13 | const arcdir = (Math.atan2(py, px) > 0 ? 0 : 1);
14 | let d = (includeCenter
15 | ? `M ${x},${y} l ${start[0]},${start[1]} `
16 | : `M ${x + start[0]},${y + start[1]} `);
17 | d += ["a ", radius, radius, 0, arcdir, 1, arcVec[0], arcVec[1]].join(" ");
18 | if (includeCenter) { d += " Z"; }
19 | return d;
20 | };
21 |
22 | export default arcPath;
23 |
--------------------------------------------------------------------------------
/examples/code/images/question.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/tests/class.id.test.js:
--------------------------------------------------------------------------------
1 | import { expect, test } from "vitest";
2 | import xmldom from "@xmldom/xmldom";
3 | import SVG from "../src/index.js";
4 |
5 | SVG.window = xmldom;
6 |
7 | test("class", () => {
8 | const svg = SVG();
9 | if (typeof svg.classList === "undefined") {
10 | expect(true).toBe(true);
11 | return;
12 | }
13 | svg.classList.add("big");
14 | expect(svg.getAttribute("class")).toBe("big");
15 | svg.classList.add("medium");
16 | expect(svg.getAttribute("class")).toBe("big medium");
17 | svg.classList.remove("big");
18 | expect(svg.getAttribute("class")).toBe("medium");
19 | svg.className = "small";
20 | expect(svg.getAttribute("class")).toBe("small");
21 |
22 | const line = SVG.line();
23 | line.id = "five";
24 | expect(line.getAttribute("id")).toBe("five");
25 |
26 | const circle = SVG.circle();
27 | circle.classList.remove("apple");
28 | expect(circle.getAttribute("class")).toBe("");
29 | });
30 |
--------------------------------------------------------------------------------
/types/constructor/extensions/circle.d.ts:
--------------------------------------------------------------------------------
1 | declare namespace _default {
2 | namespace circle {
3 | function args(a: any, b: any, c: any, d: any): any[];
4 | let methods: {
5 | removeChildren: (element: any) => any;
6 | appendTo: (element: any, parent: any) => any;
7 | setAttributes: (element: any, attrs: any) => any;
8 | clearTransform: (el: any) => any;
9 | radius: (el: any, r: any) => any;
10 | setRadius: (el: any, r: any) => any;
11 | origin: (el: any, a: any, b: any) => any;
12 | setOrigin: (el: any, a: any, b: any) => any;
13 | center: (el: any, a: any, b: any) => any;
14 | setCenter: (el: any, a: any, b: any) => any;
15 | position: (el: any, a: any, b: any) => any;
16 | setPosition: (el: any, a: any, b: any) => any;
17 | };
18 | }
19 | }
20 | export default _default;
21 |
--------------------------------------------------------------------------------
/tests/arrow.test.js:
--------------------------------------------------------------------------------
1 | import { expect, test } from "vitest";
2 | import SVG from "../src/index.js";
3 |
4 | test("arrow", () => {
5 | // svg.size(2.5, 1)
6 | // .strokeWidth(0.01)
7 | // .padding(0.25);
8 |
9 | // const options = {
10 | // bend: 0.3,
11 | // head: {
12 | // width: 0.1,
13 | // height: 0.13,
14 | // },
15 | // fill: "red",
16 | // };
17 |
18 | // const layer = svg.g();
19 |
20 | // svg.controls(2)
21 | // .svg(() => svg.circle(0.04).fill("blue"))
22 | // .position(() => [Math.random(), Math.random()])
23 | // .onChange((p, i, pts) => {
24 | // layer.removeChildren();
25 | // const arrow = layer.arrow(options)
26 | // .points(...pts)
27 | // arrow.getLine()
28 | // .strokeWidth(0.03)
29 | // .stroke("black");
30 |
31 | // }, true);
32 | expect(true).toBe(true);
33 | });
34 |
35 | test("arrow object", () => {
36 | expect(true).toBe(true);
37 | });
38 |
--------------------------------------------------------------------------------
/tests/svg.viewbox.test.js:
--------------------------------------------------------------------------------
1 | import { expect, test } from "vitest";
2 | import xmldom from "@xmldom/xmldom";
3 | import SVG from "../src/index.js";
4 |
5 | SVG.window = xmldom;
6 |
7 | test("viewBox get and set", () => {
8 | const svg = SVG();
9 | svg.setViewBox(1, 2, 3, 4);
10 | expect(svg.getAttribute("viewBox")).toBe("1 2 3 4");
11 | svg.setViewBox("-10 -10 400 500");
12 | expect(svg.getAttribute("viewBox")).toBe("-10 -10 400 500");
13 | svg.setViewBox(300, 200);
14 | expect(svg.getAttribute("viewBox")).toBe("0 0 300 200");
15 | });
16 |
17 | test("get width and height", () => {
18 | const svg = SVG();
19 | expect(svg.getWidth()).toBe(undefined);
20 | expect(svg.getHeight()).toBe(undefined);
21 | const svg2 = SVG(400, 300);
22 | expect(svg2.getWidth()).toBe(400);
23 | expect(svg2.getHeight()).toBe(300);
24 | const svg3 = SVG(0, 0, 800, 600);
25 | expect(svg3.getWidth()).toBe(800);
26 | expect(svg3.getHeight()).toBe(600);
27 | });
28 |
--------------------------------------------------------------------------------
/examples/code/examples/dragon.js:
--------------------------------------------------------------------------------
1 | svg.size(600, 600);
2 | svg.background("white", true);
3 |
4 | function dragon(x1, y1, x2, y2, turn, i) {
5 | if (i < 0) { return [[x1, y1], [x2, y2]]; }
6 | var midX = x1 + (x2 - x1) * 0.5 + turn * (y2 - y1) * 0.5;
7 | var midY = y1 + (y2 - y1) * 0.5 + (-1 * turn) * (x2 - x1) * 0.5;
8 | var first = dragon(x1, y1, midX, midY, 1, i - 1);
9 | if (first.length > 1) { first.pop(); }
10 | return first.concat(dragon(midX, midY, x2, y2, -1, i - 1));
11 | }
12 |
13 | var attrs = { strokeLinecap: "square", fill: "none" };
14 |
15 | var x1 = svg.getWidth() * 0.25;
16 | var y1 = svg.getHeight() * 0.6;
17 | var x2 = svg.getWidth() * 0.85;
18 | var y2 = svg.getHeight() * 0.6;
19 |
20 | svg.polyline(dragon(x1, y1, x2, y2, 1, random(4, 8)))
21 | .setAttributes(attrs).stroke("#ec3").strokeWidth(7);
22 | svg.polyline(dragon(x1, y1, x2, y2, 1, random(10, 12)))
23 | .setAttributes(attrs).stroke("#158").strokeWidth(3);
24 |
--------------------------------------------------------------------------------
/examples/code/examples/star.js:
--------------------------------------------------------------------------------
1 | var frametime = 0;
2 | var framerates = [];
3 |
4 | svg.size(-5, -5, 10, 10);
5 |
6 | svg.play = function (t) {
7 | var rate = t.time - frametime;
8 | framerates.push(rate);
9 | while(framerates.length > 60) {
10 | framerates.shift();
11 | }
12 | var avg = framerates
13 | .reduce(function(a, b) { return a+b; },0)
14 | / framerates.length;
15 | frametime = t.time;
16 |
17 | var starpoints = Array.from(Array(1000)).map(function() {
18 | return [Math.random()*0.02, Math.random()*0.02];
19 | });
20 |
21 | svg.removeChildren();
22 | svg.background("black");
23 | svg.polyline(starpoints)
24 | .stroke("white")
25 | .strokeWidth(Math.cos(t.time) + 1.5);
26 |
27 | // framerate
28 | var frameString = String((1/avg).toFixed(1));
29 | svg.text(frameString,-5,-5)
30 | .dominantBaseline("hanging")
31 | .fill("white")
32 | .fontSize(0.4)
33 | .fontWeight(900);
34 | };
--------------------------------------------------------------------------------
/types/constructor/extensions/ellipse.d.ts:
--------------------------------------------------------------------------------
1 | declare namespace _default {
2 | namespace ellipse {
3 | function args(a: any, b: any, c: any, d: any): any[];
4 | let methods: {
5 | removeChildren: (element: any) => any;
6 | appendTo: (element: any, parent: any) => any;
7 | setAttributes: (element: any, attrs: any) => any;
8 | clearTransform: (el: any) => any;
9 | radius: (el: any, rx: any, ry: any) => any;
10 | setRadius: (el: any, rx: any, ry: any) => any;
11 | origin: (el: any, a: any, b: any) => any;
12 | setOrigin: (el: any, a: any, b: any) => any;
13 | center: (el: any, a: any, b: any) => any;
14 | setCenter: (el: any, a: any, b: any) => any;
15 | position: (el: any, a: any, b: any) => any;
16 | setPosition: (el: any, a: any, b: any) => any;
17 | };
18 | }
19 | }
20 | export default _default;
21 |
--------------------------------------------------------------------------------
/src/constructor/extensions/curve/getCurveEndpoints.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Rabbit Ear (c) Kraft
3 | */
4 | const getNumbersFromPathCommand = str => str
5 | .slice(1)
6 | .split(/[, ]+/)
7 | .map(s => parseFloat(s));
8 |
9 | // this gets the parameter numbers, in an array
10 | const getCurveTos = d => d
11 | .match(/[Cc][(0-9), .-]+/)
12 | .map(curve => getNumbersFromPathCommand(curve));
13 |
14 | const getMoveTos = d => d
15 | .match(/[Mm][(0-9), .-]+/)
16 | .map(curve => getNumbersFromPathCommand(curve));
17 |
18 | const getCurveEndpoints = (d) => {
19 | // get only the first Move and Curve commands
20 | const move = getMoveTos(d).shift();
21 | const curve = getCurveTos(d).shift();
22 | const start = move
23 | ? [move[move.length - 2], move[move.length - 1]]
24 | : [0, 0];
25 | const end = curve
26 | ? [curve[curve.length - 2], curve[curve.length - 1]]
27 | : [0, 0];
28 | return [...start, ...end];
29 | };
30 |
31 | export default getCurveEndpoints;
32 |
--------------------------------------------------------------------------------
/src/constructor/extensions/curve/methods.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Rabbit Ear (c) Kraft
3 | */
4 | import makeCoordinates from "../../../arguments/makeCoordinates.js";
5 | import makeCurvePath from "./makeCurvePath.js";
6 | import getCurveEndpoints from "./getCurveEndpoints.js";
7 | import TransformMethods from "../shared/transforms.js";
8 |
9 | const setPoints = (element, ...args) => {
10 | const coords = makeCoordinates(...args.flat()).slice(0, 4);
11 | element.setAttribute("d", makeCurvePath(coords, element._bend, element._pinch));
12 | return element;
13 | };
14 |
15 | const bend = (element, amount) => {
16 | element._bend = amount;
17 | return setPoints(element, ...getCurveEndpoints(element.getAttribute("d")));
18 | };
19 |
20 | const pinch = (element, amount) => {
21 | element._pinch = amount;
22 | return setPoints(element, ...getCurveEndpoints(element.getAttribute("d")));
23 | };
24 |
25 | export default {
26 | setPoints,
27 | bend,
28 | pinch,
29 | ...TransformMethods,
30 | };
31 |
--------------------------------------------------------------------------------
/tests/ear.js:
--------------------------------------------------------------------------------
1 | (function (global, factory) {
2 | typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
3 | typeof define === 'function' && define.amd ? define(factory) :
4 | (global = global || self, global.ear = factory());
5 | }(this, (function () { 'use strict';
6 |
7 | const obj = { svg: () => {} };
8 | obj.prototype = Object.prototype;
9 |
10 | const rabbitEar = {
11 | origami: () => {},
12 | graph: () => {},
13 | cp: () => {},
14 | vector: obj,
15 | line: obj,
16 | ray: obj,
17 | segment: obj,
18 | circle: obj,
19 | ellipse: obj,
20 | rect: obj,
21 | polygon: obj,
22 | matrix: obj,
23 | plane: obj,
24 | };
25 | const use = function (library) {
26 | if (typeof library !== "function"
27 | || library === null
28 | || typeof library.core.attach !== "function") {
29 | return;
30 | }
31 | library.core.attach(this);
32 | };
33 | rabbitEar.use = use.bind(rabbitEar);
34 | return rabbitEar;
35 | })));
36 |
--------------------------------------------------------------------------------
/examples/code/images/dice.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/code/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | ✨SVG✨
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/examples/compare-with.html:
--------------------------------------------------------------------------------
1 |
6 |
7 | SVG example: introduction
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import cleanup from "rollup-plugin-cleanup";
2 | import terser from "@rollup/plugin-terser";
3 |
4 | const input = "src/index.js";
5 | const name = "SVG";
6 | const banner = "/* SVG (c) Kraft */";
7 |
8 | export default [{
9 | input,
10 | output: {
11 | name,
12 | file: "svg.js",
13 | format: "umd",
14 | banner,
15 | compact: true,
16 | generatedCode: {
17 | constBindings: true,
18 | objectShorthand: true,
19 | },
20 | },
21 | plugins: [cleanup(), terser()],
22 | // plugins: [cleanup()],
23 | }, {
24 | input,
25 | output: {
26 | name,
27 | file: "svg.module.js",
28 | format: "es",
29 | banner,
30 | generatedCode: {
31 | constBindings: true,
32 | objectShorthand: true,
33 | },
34 | },
35 | plugins: [cleanup()],
36 | }, {
37 | input,
38 | output: {
39 | name,
40 | dir: "module/",
41 | format: "es",
42 | banner,
43 | preserveModules: true,
44 | generatedCode: {
45 | constBindings: true,
46 | objectShorthand: true,
47 | },
48 | },
49 | }];
50 |
--------------------------------------------------------------------------------
/src/arguments/makeViewBox.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Rabbit Ear (c) Kraft
3 | */
4 | import makeCoordinates from "./makeCoordinates.js";
5 |
6 | /**
7 | * @param {number} x
8 | * @param {number} y
9 | * @param {number} width
10 | * @param {number} height
11 | * @param {number} [padding=0]
12 | * @returns {string}
13 | */
14 | const viewBoxValuesToString = function (x, y, width, height, padding = 0) {
15 | const scale = 1.0;
16 | const d = (width / scale) - width;
17 | const X = (x - d) - padding;
18 | const Y = (y - d) - padding;
19 | const W = (width + d * 2) + padding * 2;
20 | const H = (height + d * 2) + padding * 2;
21 | return [X, Y, W, H].join(" ");
22 | };
23 |
24 | /**
25 | * @returns {string | undefined}
26 | */
27 | const makeViewBox = (...args) => {
28 | const nums = makeCoordinates(...args.flat());
29 | if (nums.length === 2) { nums.unshift(0, 0); }
30 | return nums.length === 4
31 | ? viewBoxValuesToString(nums[0], nums[1], nums[2], nums[3])
32 | : undefined;
33 | };
34 |
35 | export default makeViewBox;
36 |
--------------------------------------------------------------------------------
/src/constructor/extensions/line.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Rabbit Ear (c) Kraft
3 | */
4 | import semiFlattenArrays from "../../arguments/semiFlattenArrays.js";
5 | import makeCoordinates from "../../arguments/makeCoordinates.js";
6 | import nodes_attributes from "../../spec/nodes_attributes.js";
7 | import TransformMethods from "./shared/transforms.js";
8 | import URLMethods from "./shared/urls.js";
9 | import * as DOM from "./shared/dom.js";
10 |
11 | const Args = (...args) => makeCoordinates(...semiFlattenArrays(...args)).slice(0, 4);
12 |
13 | const setPoints = (element, ...args) => {
14 | Args(...args).forEach((value, i) => element.setAttribute(nodes_attributes.line[i], value));
15 | return element;
16 | };
17 | /**
18 | * @name line
19 | * @description SVG Line element
20 | * @memberof SVG
21 | * @linkcode SVG ./src/nodes/spec/line.js 18
22 | */
23 | export default {
24 | line: {
25 | args: Args,
26 | methods: {
27 | setPoints,
28 | ...TransformMethods,
29 | ...URLMethods,
30 | ...DOM,
31 | },
32 | },
33 | };
34 |
--------------------------------------------------------------------------------
/src/arguments/makeCoordinates.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Rabbit Ear (c) Kraft
3 | */
4 | import {
5 | str_number,
6 | str_object,
7 | } from "../environment/strings.js";
8 | /**
9 | * this will extract coordinates from a set of inputs
10 | * and present them as a stride-2 flat array. length % 2 === 0
11 | * a 1D array of numbers, alternating x y
12 | *
13 | * use flatten() everytime you call this!
14 | * it's necessary the entries sit at the top level of ...args
15 | * findCoordinates(...flatten(...args));
16 | */
17 | const makeCoordinates = (...args) => args
18 | .filter(a => typeof a === str_number)
19 | .concat(args
20 | .filter(a => typeof a === str_object && a !== null)
21 | .map((el) => {
22 | if (typeof el.x === str_number) { return [el.x, el.y]; }
23 | if (typeof el[0] === str_number) { return [el[0], el[1]]; }
24 | return undefined;
25 | }).filter(a => a !== undefined)
26 | .reduce((a, b) => a.concat(b), []));
27 | // [top-level numbers] concat [{x:,y:} and [0,1]] style
28 |
29 | export default makeCoordinates;
30 |
--------------------------------------------------------------------------------
/examples/code/examples/clock.js:
--------------------------------------------------------------------------------
1 | svg.size(-1, -1, 2, 2);
2 | svg.background("#888");
3 | var radius = svg.getWidth() * 0.48;
4 |
5 | for (var i = 0; i < 12; i += 1) {
6 | svg.text(String((i + 11) % 12 + 1))
7 | .fontFamily("Times New Roman")
8 | .fontSize(svg.getWidth() / 8)
9 | .textAnchor("middle")
10 | .setAttribute("style", "transform: rotate("+(i*30)+"deg) translate(0, -"+radius*0.75+"px)");
11 | }
12 |
13 | var pies = [
14 | svg.wedge().fill("#0008"),
15 | svg.wedge().fill("transparent"),
16 | svg.wedge().fill("#fff8")
17 | ];
18 |
19 | svg.play = function (time) {
20 | var d = new Date();
21 | var s = (d.getSeconds() + d.getMilliseconds() / 1000) / 60;
22 | var m = d.getMinutes() / 60;
23 | var h = (d.getHours() % 12) / 12;
24 | [(s), (m + s / 60), (h + m / 12 + s / 720)]
25 | .sort(function(a, b) { return a - b; })
26 | .forEach(function(a, i, arr) {
27 | var a1 = -PI / 2 + 2 * PI * a;
28 | var a2 = -PI / 2 + 2 * PI * arr[(i + 1) % arr.length];
29 | pies[i].setArc(0, 0, radius, a1, a2, true);
30 | });
31 | };
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "browser": true,
4 | "es2021": true,
5 | "node": true
6 | },
7 | "extends": "airbnb-base",
8 | "parserOptions": {
9 | "ecmaVersion": "latest",
10 | "ecmaFeatures": { "jsx": true },
11 | "sourceType": "module"
12 | },
13 | "ignorePatterns": [
14 | "svg.js",
15 | "svg.module.js",
16 | "svg.module.comments.js"
17 | ],
18 | "rules": {
19 | "arrow-parens": 0,
20 | "camelcase": ["error", { "allow": [".*"] }],
21 | "func-names": ["error", "never"],
22 | "indent": ["error", "tab"],
23 | "no-bitwise": 0,
24 | "no-continue": 0,
25 | "no-sparse-arrays": 0,
26 | "no-tabs": 0,
27 | "import/extensions": ["error", "always", { "ignorePackages": true }],
28 | "import/no-relative-packages": 0,
29 | "object-shorthand": 0,
30 | "object-curly-newline": 0,
31 | "import/prefer-default-export": 0,
32 | "prefer-rest-params": 0,
33 | "prefer-default-export": 0,
34 | "prefer-destructuring": 0,
35 | "quotes": ["error", "double", { "allowTemplateLiterals": true }]
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/tests/urls.test.js:
--------------------------------------------------------------------------------
1 | import { expect, test } from "vitest";
2 | import xmldom from "@xmldom/xmldom";
3 | import SVG from "../src/index.js";
4 |
5 | SVG.window = xmldom;
6 |
7 | test("custom id names", () => {
8 | ["clipPath", "symbol", "mask", "marker"].forEach(nodeName => {
9 | const svg = SVG();
10 | const test1 = svg[nodeName]();
11 | const test2 = svg[nodeName]("what is");
12 | expect(test1.getAttribute("id").length).toBe(5);
13 | expect(test2.getAttribute("id")).toBe("what is");
14 | });
15 | });
16 |
17 | test("assign to types", () => {
18 | const svg = SVG();
19 | const things = ["clipPath", "symbol", "mask", "marker", "marker", "marker"]
20 | .map(nodeName => svg[nodeName]());
21 | const line = svg.line(1, 2, 3, 4);
22 | ["clipPath", "mask", "symbol", "markerEnd", "markerMid", "markerStart"]
23 | .forEach((method, i) => line[method](things[i]));
24 |
25 | // these should be the attributes
26 | ["x1", "y1", "x2", "y2", "clip-path", "mask", "symbol", "marker-end", "marker-mid", "marker-start"]
27 | .forEach((attr, i) => expect(line.attributes[i].name).toBe(attr));
28 | });
29 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "rabbit-ear-svg",
3 | "version": "0.2.3",
4 | "description": "creative coding with SVG",
5 | "main": "svg.js",
6 | "type": "module",
7 | "sourceType": "module",
8 | "types": "./types/index.d.ts",
9 | "scripts": {
10 | "test": "vitest",
11 | "clean": "node tests/clean.js",
12 | "lineno": "node tests/lineno.js"
13 | },
14 | "author": "Robby Kraft",
15 | "license": "MIT",
16 | "repository": "https://github.com/robbykraft/SVG",
17 | "keywords": [
18 | "svg",
19 | "art",
20 | "vector",
21 | "graphics",
22 | "creative",
23 | "code",
24 | "illustrator",
25 | "animation",
26 | "script",
27 | "library"
28 | ],
29 | "devDependencies": {
30 | "@rollup/plugin-terser": "^0.4.3",
31 | "@xmldom/xmldom": "^0.8.10",
32 | "eslint": "^8.47.0",
33 | "eslint-config-airbnb-base": "^15.0.0",
34 | "eslint-plugin-import": "^2.28.1",
35 | "rollup": "^3.28.1",
36 | "rollup-plugin-cleanup": "^3.2.1",
37 | "typedoc": "^0.25.13",
38 | "typescript": "^5.4.4",
39 | "vitest": "^0.34.6"
40 | },
41 | "jest": {
42 | "collectCoverage": true
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/constructor/extensions/curve/makeCurvePath.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Rabbit Ear (c) Kraft
3 | */
4 | import { svg_add2, svg_sub2, svg_scale2 } from "../../../general/algebra.js";
5 |
6 | // endpoints is an array of 4 numbers
7 | const makeCurvePath = (endpoints = [], bend = 0, pinch = 0.5) => {
8 | /** @type {[number, number]} */
9 | const tailPt = [endpoints[0] || 0, endpoints[1] || 0];
10 | /** @type {[number, number]} */
11 | const headPt = [endpoints[2] || 0, endpoints[3] || 0];
12 | const vector = svg_sub2(headPt, tailPt);
13 | const midpoint = svg_add2(tailPt, svg_scale2(vector, 0.5));
14 | /** @type {[number, number]} */
15 | const perpendicular = [vector[1], -vector[0]];
16 | const bezPoint = svg_add2(midpoint, svg_scale2(perpendicular, bend));
17 | const tailControl = svg_add2(tailPt, svg_scale2(svg_sub2(bezPoint, tailPt), pinch));
18 | const headControl = svg_add2(headPt, svg_scale2(svg_sub2(bezPoint, headPt), pinch));
19 | return `M${tailPt[0]},${tailPt[1]}C${tailControl[0]},${tailControl[1]} ${headControl[0]},${headControl[1]} ${headPt[0]},${headPt[1]}`;
20 | };
21 |
22 | export default makeCurvePath;
23 |
--------------------------------------------------------------------------------
/tests/coordinates.test.js:
--------------------------------------------------------------------------------
1 | import { expect, test } from "vitest";
2 | import xmldom from "@xmldom/xmldom";
3 | import SVG from "../src/index.js";
4 |
5 | SVG.window = xmldom;
6 |
7 | test("", () => expect(true).toBe(true));
8 |
9 | // test("coordinates, two points", () => {
10 | // SVG.makeCoordinates(1, 2, 3, 4)
11 | // .forEach((n, i) => expect(n).toBe([1, 2, 3, 4][i]));
12 | // SVG.makeCoordinates([1, 2], [3, 4])
13 | // .forEach((n, i) => expect(n).toBe([1, 2, 3, 4][i]));
14 | // SVG.makeCoordinates([1, 2, 3], [4, 5, 6])
15 | // .forEach((n, i) => expect(n).toBe([1, 2, 4, 5][i]));
16 | // SVG.makeCoordinates([1], [2])
17 | // .forEach((n, i) => expect(n).toBe([1, undefined, 2, undefined][i]));
18 | // });
19 |
20 | // test("coordinates, not two points", () => {
21 | // expect(SVG.makeCoordinates().length).toBe(0);
22 | // expect(SVG.makeCoordinates([]).length).toBe(0);
23 | // expect(SVG.makeCoordinates([[]]).length).toBe(0);
24 | // expect(SVG.makeCoordinates([], []).length).toBe(0);
25 |
26 | // SVG.makeCoordinates([1, 2], [3, 4], [5, 6])
27 | // .forEach((n, i) => expect(n).toBe([1, 2, 3, 4, 5, 6][i]));
28 | // });
29 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # SVG
2 |
3 | [](https://travis-ci.org/robbykraft/SVG)
4 |
5 | creative coding with SVG
6 |
7 | *easy drawing and styling, event handlers, browser or node.js*
8 |
9 | ## Examples
10 |
11 | [Code editor](https://robbykraft.github.io/SVG/examples/code/), a live code editor which includes examples (roll the dice).
12 |
13 | [Download](https://github.com/robbykraft/SVG/releases), and there are more examples in the `examples/` folder.
14 |
15 | ## Install
16 |
17 | The compiled library is one file, and works in the browser or in Node.
18 |
19 | ```
20 | https://robbykraft.github.io/SVG/svg.js
21 | ```
22 |
23 | ```
24 | npm i rabbit-ear-svg
25 | ```
26 |
27 | ## Usage
28 |
29 | Two sources of documentation:
30 |
31 | [SVG docs](https://robbykraft.github.io/SVG/docs/)
32 |
33 | [rabbit ear docs](https://rabbitear.org/book/svg.html)
34 |
35 | ## Credit
36 |
37 | - [vkBeautify](https://github.com/vkiryukhin/vkBeautify) pretty-print for SVG export
38 | - [XML DOM](https://github.com/xmldom/xmldom) for a "window" object in Node
39 |
40 | ## License
41 |
42 | MIT
43 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Rabbit Ear (c) Kraft
3 | */
4 | import { setWindow } from "./environment/window.js";
5 | import NS from "./spec/namespace.js";
6 | import nodes_attributes from "./spec/nodes_attributes.js";
7 | import nodes_children from "./spec/nodes_children.js";
8 | import colors from "./colors/index.js";
9 | import general from "./general/index.js";
10 | import extensions from "./constructor/extensions/index.js";
11 | import { svg, constructors } from "./constructor/elements.js";
12 |
13 | const library = {
14 | NS,
15 | nodes_attributes,
16 | nodes_children,
17 | extensions,
18 | ...colors,
19 | ...general,
20 | ...constructors,
21 | };
22 |
23 | // the top level container object is also an