diff --git a/docs/chapters/arclength/draw-slices.js b/docs/chapters/arclength/draw-slices.js index ac1c0bc9..be14500e 100644 --- a/docs/chapters/arclength/draw-slices.js +++ b/docs/chapters/arclength/draw-slices.js @@ -33,7 +33,7 @@ drawSlices(w, h, a, n) { let area = 0; let step = w/n; for(let i=0, f=TAU/w, c, y; i 50) setStroke(c); setFill(c); y = sin((i+step/2) * f) * a; diff --git a/docs/chapters/circles/arc-approximation.js b/docs/chapters/circles/arc-approximation.js index 8faeef3a..2dc4236d 100644 --- a/docs/chapters/circles/arc-approximation.js +++ b/docs/chapters/circles/arc-approximation.js @@ -25,7 +25,7 @@ draw() { noStroke(); setFill(`rgba(100,255,100,0.4)`); let a = this.angle; - arc(w/2, h/2, r, a < 0 ? a : 0, a < 0 ? 0 : a, w/2, h/2); + wedge(w/2, h/2, r, a < 0 ? a : 0, a < 0 ? 0 : a); curve.drawSkeleton(); curve.drawCurve(); diff --git a/docs/chapters/circles_cubic/arc-approximation.js b/docs/chapters/circles_cubic/arc-approximation.js index f78d49be..a6d3b363 100644 --- a/docs/chapters/circles_cubic/arc-approximation.js +++ b/docs/chapters/circles_cubic/arc-approximation.js @@ -26,7 +26,7 @@ draw() { noStroke(); setFill(`rgba(100,255,100,0.4)`); let a = this.angle; - arc(w/2, h/2, r, a < 0 ? a : 0, a < 0 ? 0 : a, w/2, h/2); + wedge(w/2, h/2, r, a < 0 ? a : 0, a < 0 ? 0 : a); guess.drawSkeleton(`lightblue`); guess.drawCurve(`lightblue`); diff --git a/docs/chapters/curvature/curvature.js b/docs/chapters/curvature/curvature.js index 8a33c1b3..1b165c43 100644 --- a/docs/chapters/curvature/curvature.js +++ b/docs/chapters/curvature/curvature.js @@ -53,7 +53,7 @@ computeCurvature(curve, t) { dd = curve.dderivative(t), num = d.x * dd.y - d.y * dd.x, qdsum = d.x * d.x + d.y * d.y, - dnm = pow(qdsum, 3 / 2); + dnm = qdsum ** 3/2; if (num === 0 || dnm === 0) return 0; diff --git a/docs/chapters/reordering/reorder.js b/docs/chapters/reordering/reorder.js index 17203f0b..82e3102a 100644 --- a/docs/chapters/reordering/reorder.js +++ b/docs/chapters/reordering/reorder.js @@ -5,8 +5,8 @@ setup() { h = this.height; for (let i=0; i<10; i++) { points.push({ - x: w/2 + random() * 20 + cos(PI*2 * i/10) * (w/2 - 40), - y: h/2 + random() * 20 + sin(PI*2 * i/10) * (h/2 - 40) + x: w/2 + random(20) + cos(PI*2 * i/10) * (w/2 - 40), + y: h/2 + random(20) + sin(PI*2 * i/10) * (h/2 - 40) }); } setMovable(points); diff --git a/docs/images/chapters/arclength/4b5d220d02b08f6c9aa19389255ef8bb.png b/docs/images/chapters/arclength/4b5d220d02b08f6c9aa19389255ef8bb.png deleted file mode 100644 index 27d044eb..00000000 Binary files a/docs/images/chapters/arclength/4b5d220d02b08f6c9aa19389255ef8bb.png and /dev/null differ diff --git a/docs/images/chapters/arclength/4bffba7dda2a3556cf5b2ae7392083c6.png b/docs/images/chapters/arclength/4bffba7dda2a3556cf5b2ae7392083c6.png deleted file mode 100644 index 7f9a3c61..00000000 Binary files a/docs/images/chapters/arclength/4bffba7dda2a3556cf5b2ae7392083c6.png and /dev/null differ diff --git a/docs/images/chapters/arclength/56533f47e73ad9fea08fa9bb3f597d49.png b/docs/images/chapters/arclength/56533f47e73ad9fea08fa9bb3f597d49.png new file mode 100644 index 00000000..325512d2 Binary files /dev/null and b/docs/images/chapters/arclength/56533f47e73ad9fea08fa9bb3f597d49.png differ diff --git a/docs/images/chapters/arclength/5ce02cbdbc47585c588f2656d5161a32.png b/docs/images/chapters/arclength/5ce02cbdbc47585c588f2656d5161a32.png new file mode 100644 index 00000000..5baa0223 Binary files /dev/null and b/docs/images/chapters/arclength/5ce02cbdbc47585c588f2656d5161a32.png differ diff --git a/docs/images/chapters/arclength/dc74a2f2da19470b8d721ece5f3ce268.png b/docs/images/chapters/arclength/dc74a2f2da19470b8d721ece5f3ce268.png deleted file mode 100644 index 78fd5a68..00000000 Binary files a/docs/images/chapters/arclength/dc74a2f2da19470b8d721ece5f3ce268.png and /dev/null differ diff --git a/docs/images/chapters/arclength/fe2663b205d14c157a5a02bfbbd55987.png b/docs/images/chapters/arclength/fe2663b205d14c157a5a02bfbbd55987.png new file mode 100644 index 00000000..fe3bac3a Binary files /dev/null and b/docs/images/chapters/arclength/fe2663b205d14c157a5a02bfbbd55987.png differ diff --git a/docs/images/chapters/circles/08ca09aacb271735e063e7e8d941a195.png b/docs/images/chapters/circles/08ca09aacb271735e063e7e8d941a195.png new file mode 100644 index 00000000..7de43a50 Binary files /dev/null and b/docs/images/chapters/circles/08ca09aacb271735e063e7e8d941a195.png differ diff --git a/docs/images/chapters/circles/8c2eb77bfd11231393a77e72f03ebe83.png b/docs/images/chapters/circles/8c2eb77bfd11231393a77e72f03ebe83.png new file mode 100644 index 00000000..dff2fb43 Binary files /dev/null and b/docs/images/chapters/circles/8c2eb77bfd11231393a77e72f03ebe83.png differ diff --git a/docs/images/chapters/circles_cubic/331f3f89b1dac9e1e60f61c5d95f9e5b.png b/docs/images/chapters/circles_cubic/331f3f89b1dac9e1e60f61c5d95f9e5b.png new file mode 100644 index 00000000..697c2f31 Binary files /dev/null and b/docs/images/chapters/circles_cubic/331f3f89b1dac9e1e60f61c5d95f9e5b.png differ diff --git a/docs/images/chapters/circles_cubic/ecacec0eb2bbe72e14b9008d854fbb06.png b/docs/images/chapters/circles_cubic/ecacec0eb2bbe72e14b9008d854fbb06.png new file mode 100644 index 00000000..53a76dc3 Binary files /dev/null and b/docs/images/chapters/circles_cubic/ecacec0eb2bbe72e14b9008d854fbb06.png differ diff --git a/docs/images/chapters/curvature/5fcfb0572cae06717506c84768aa568c.png b/docs/images/chapters/curvature/5fcfb0572cae06717506c84768aa568c.png deleted file mode 100644 index 46869d2f..00000000 Binary files a/docs/images/chapters/curvature/5fcfb0572cae06717506c84768aa568c.png and /dev/null differ diff --git a/docs/images/chapters/curvature/78581fc4b36f2e45c704a4fce7c8368a.png b/docs/images/chapters/curvature/78581fc4b36f2e45c704a4fce7c8368a.png new file mode 100644 index 00000000..23af0e10 Binary files /dev/null and b/docs/images/chapters/curvature/78581fc4b36f2e45c704a4fce7c8368a.png differ diff --git a/docs/images/chapters/curvature/7898e3a51a86afffd1a91ce0fcc99b26.png b/docs/images/chapters/curvature/7898e3a51a86afffd1a91ce0fcc99b26.png new file mode 100644 index 00000000..c34ff6df Binary files /dev/null and b/docs/images/chapters/curvature/7898e3a51a86afffd1a91ce0fcc99b26.png differ diff --git a/docs/images/chapters/curvature/876d7b2750d7c29068ac6181c3634d25.png b/docs/images/chapters/curvature/876d7b2750d7c29068ac6181c3634d25.png deleted file mode 100644 index 2f2eead7..00000000 Binary files a/docs/images/chapters/curvature/876d7b2750d7c29068ac6181c3634d25.png and /dev/null differ diff --git a/docs/images/chapters/reordering/71f47629388901b821976e034be159e4.png b/docs/images/chapters/reordering/71f47629388901b821976e034be159e4.png deleted file mode 100644 index cd0abfa7..00000000 Binary files a/docs/images/chapters/reordering/71f47629388901b821976e034be159e4.png and /dev/null differ diff --git a/docs/images/chapters/reordering/c37918c649befad06de255a6415faf29.png b/docs/images/chapters/reordering/c37918c649befad06de255a6415faf29.png new file mode 100644 index 00000000..35fbe9ba Binary files /dev/null and b/docs/images/chapters/reordering/c37918c649befad06de255a6415faf29.png differ diff --git a/docs/index.html b/docs/index.html index 12375338..13226911 100644 --- a/docs/index.html +++ b/docs/index.html @@ -31,7 +31,7 @@ - + @@ -1649,7 +1649,7 @@ function drawCurve(points[], t): Scripts are disabled. Showing fallback image. - + @@ -3142,21 +3142,21 @@ y = curve.get(t).y > Scripts are disabled. Showing fallback image. - + Scripts are disabled. Showing fallback image. - + Scripts are disabled. Showing fallback image. - + @@ -3389,7 +3389,7 @@ y = curve.get(t).y > Scripts are disabled. Showing fallback image. - + @@ -3413,7 +3413,7 @@ y = curve.get(t).y > Scripts are disabled. Showing fallback image. - + @@ -5221,7 +5221,7 @@ for p = 1 to points.length-3 (inclusive): Scripts are disabled. Showing fallback image. - + @@ -5346,7 +5346,7 @@ for p = 1 to points.length-3 (inclusive): Scripts are disabled. Showing fallback image. - + diff --git a/docs/ja-JP/index.html b/docs/ja-JP/index.html index 055ec05c..ae9c6ec7 100644 --- a/docs/ja-JP/index.html +++ b/docs/ja-JP/index.html @@ -33,7 +33,7 @@ - + @@ -1485,7 +1485,7 @@ function drawCurve(points[], t): Scripts are disabled. Showing fallback image. - + @@ -2978,21 +2978,21 @@ y = curve.get(t).y > Scripts are disabled. Showing fallback image. - + Scripts are disabled. Showing fallback image. - + Scripts are disabled. Showing fallback image. - + @@ -3225,7 +3225,7 @@ y = curve.get(t).y > Scripts are disabled. Showing fallback image. - + @@ -3249,7 +3249,7 @@ y = curve.get(t).y > Scripts are disabled. Showing fallback image. - + @@ -5057,7 +5057,7 @@ for p = 1 to points.length-3 (inclusive): Scripts are disabled. Showing fallback image. - + @@ -5182,7 +5182,7 @@ for p = 1 to points.length-3 (inclusive): Scripts are disabled. Showing fallback image. - + diff --git a/docs/js/custom-element/api/README.md b/docs/js/custom-element/api/README.md new file mode 100644 index 00000000..911457ae --- /dev/null +++ b/docs/js/custom-element/api/README.md @@ -0,0 +1,337 @@ +# The Graphics API + +Graphics code is bootstrapped and drawn uses two "master" functions: + +```js +setup() { + // initialisation code goes here +} + +draw() { + // drawing code goes here +} +``` + +All code starts at `setup()`, automatically calling `draw()` after setup has been completed. Standard JS scoping applies, so any variable declared outside of `setup`/`draw` will be a "global" variable. + +## User Events + +Graphics code can react to touch/mouse, which can be handled using: + +- `onMouseDown()` triggered by mouse/touch start events. +- `onMouseUp()` triggered by mouse/touch end events. +- `onMouseMose()` triggered by moving the mouse/your finger. + +Mouse event data can be accessed via the `this.cursor` property, which encodes: + +``` +{ + x: current event's screen x coordinate + y: current event's screen x coordinate + down: boolean signifying whether the cursor is engaged or not + mark: {x,y} coordinate object representing where mousedown occurred + last: {x,y} coordinate object representing where the cursor was "one event ago" + diff: {x,y} coordinate object representing the x/y difference between "now" and "one event ago", + with an additional `total` propert that is an {x,y} coordinate object representing the x/y + difference between "now" and the original mousedown event. +} +``` + +Graphics code can also react to keyboard events, although this is a great way to make sure your code won't work for mobile devices, so it's better to use range sliders to keep things accessible. That said, they can be handled using: + +- `onKeyDown()` triggered by pressing a key +- `onKeyUp()` triggered by releasing a key + +Keyboard event data can be accessed via the `this.keyboard` property, which encodes: + +``` +{ + currentKey: the name of the key associated with the current event +} +``` + +Additionally, the `this.keyboard` property can be consulted for named keys to see if they are currently down or not, e.g. to check whether the up arrow is down or not: + +```js +draw() { + if (this.keyboard[`ArrowUp`]) { + ... + } +} +``` + +## Controllable parameters + +Graphics code can be passed external values from HTML using data attributes: + +```html + +``` + +which can be access on the code side using + +```js +this.parameters.propname; +``` + +Additionally, graphics code has special functions for range values, either directly in code: + +```js +setup() { + // name, min, max, step, initial value + addSlider(`rangeValue`, 0, 1, 0.001, 0.5); +} + +draw() { + // values defined through sliders become accessible on `this`: + console.log(this.rangeValue); +} +``` + +Alternatively, they can be hooked into a predefined slider: + +```html + + + +``` + +using the `setSlider` function: + +```js +setup() { + // local query selector, name, initial value + setSlider(`.my-slider`, `rangeValue`, 0.5); +} + +draw() { + console.log(this.rangeValue); +} +``` + +## Movable points + +An important part of the Graphics API is showing shapes that are controlled or defined by coordinates, and as there are special functions for marking points as "movable" - that is, these points can be click/touch-dragged around a graphic. To fascilitate this, the following functions can be used: + +- `setMovable(points, ...)` takes one or more arrays of points, and marks all points as "being movable", such that if the cursor activates at an x/y coordinate near one of these, that point gets assigned to `this.currentPoint`, as well as being automatically moved around as you drag the cursor around on the sketch. +- `resetMovable()` will clear the list of movable points. +- `resetMovable(points, ...)` is the same as calling `resetMovable()` followed by `setMovable(points, ...)`. + +## The API + +The following is the list of API functions that can be used to draw... whatever you like, really. + + +### gGobal constants + +- `PI` 3.14159265358979 +- `TAU` 6.28318530717958 +- `POINTER` "default" +- `HAND` "pointer" +- `CROSS` "crosshair" +- `POLYGON` Shape.POLYGON, "Polygon" +- `CURVE` Shape.CURVE, "CatmullRom" +- `BEZIER` Shape.BEZIER, "Bezier" +- `CENTER` "center" +- `LEFT` "left" +- `RIGHT` "right" + + +### Instance propeties + +- `this.width` the width of the graphic +- `this.height` the height of the graphic +- `this.panelWidth` the width of a single panel in the graphic, only meaningful in conjunction with `setPanelWidth` (see below) +- `this.parameters` the collection of externally passed parameters (via HTML: `data-...` attributes, via JS: a key/value object) +- `this.cursor` represents the current mouse/touch cursor state +- `this.currentPoint` whatever point thec cursor is currently close enough to to interact with +- `this.keyboard` the current keyboard state +- `this.currentShape` the currently active shape. **warning:** this value gets reset any time `start()` is used, so it is recommended to cache the current shape using `saveShape()` instead of directly referencing `this.currentShape`. + + +### General functions + +- `setSize(width,height)` explicitly resizes the canvas. **warning:** this will reset all color, transform, etc. properties to their default values. +- `setPanelCount(int)` use this in `setup()` to let the API know that this graphic is technically a number of "separate" panels of content, setting `this.panelWidth` to `width`/`panelcount`. +- `toDataURL()` returns the graphic as PNG image, encoded as a data URL. +- `find(qs)` find an HTML elements in the `` DOM tree, using a query selector +- `findAll(qs)` find all HTML elements that match the provided querySelector. **note:** unlike the DOM API, this function returns a plain array. + + +### Maths functions + +- `abs(v)` get the absolute value +- `approx(v1, v2, epsilon = 0.001)` check whether v1 differs from v2 by no more than `epsilon` +- `atan2(dy, dx)` [atan2](https://en.wikipedia.org/wiki/Atan2) +- `binomial(n, k)` get the binomial coefficient, i.e. "n choose k" +- `ceil(v)` round any fractional number up to the next highest interger +- `constrain(v, lowest, highest)` restrict a value in its lowest and highest value. +- `cos(v)` cosine +- `dist(x1, y1, x2, y2)` the euclidean distance between (x1,y1) and (x2,y2) +- `floor(v)` round any fractional number to an integer by discarding its fractional part +- `map(v, fromStart, fromEnd, toStrart, toEnd, constrain = false)` compute a value on an interval [fromStart,fromEnd] to its corresponding value on the interval [toStart,toEnd], with optional constraining to that new interval. +- `max(...v)` find the highest number in two or more numbers +- `min(...v)` find the lowest number in two or more numbers +- `random()` generate a random value between 0 (inclusive) and 1 (exclusive) +- `random(v)` generate a random value between 0 (inclusive) and `v` (exclusive) +- `random(a,b)` generate a random value between `a` (inclusive) and `b` (exclusive) +- `round(v)` round any fractional number by applying `ceil` for any number with fractional part >= 0.5, and `floor` for any number with fractional part < 0.5. +- `sin(v)` sine +- `sqrt(v)` square root +- `tan(v)` tangent + +### Property functions + +- `setBorder(width = 1, color = "black")` set the canvas border width and color +- `setColor(color)` set the color for both shape stroke and fill +- `noColor()` set both stroke and fill color to "transparent" +- `setCursor(type)` set the CSS cursor type. `POINTER`, `HAND`, and `CROSS` constants are provided, other values must be supplied as string. +- `setFill(color)` set the fill color +- `noFill()` set the fill color to "transparent" +- `setFont(font)` set the text font, using [CSS font syntax](https://developer.mozilla.org/en-US/docs/Web/CSS/font) +- `setFontFamily(name)` set the font to be used, by name +- `setFontSize(px)` set the font size in pixels +- `setFontWeight(val)` set the font weight in CSS weight units +- `setGrid(size, color)` set the background grid's spacing and line coloring +- `noGrid()` do not draw a background grid +- `setLineDash(...values)` set the interval values for [dashed lines](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setLineDash) +- `noLineDash()` do not use line dashing for strokes +- `setShadow(color, px)` set the color and blur distance for drawing shape shadows +- `noShadow()` do not use shape shadows +- `setStroke(color)` set the stroke color +- `noStroke()` set the stroke color to "transparent" +- `setTextStroke(color, weight)` set the text outline stroke color and width (in pixels) +- `noTextStroke()` disable text outline +- `setWidth()` reset the stroke width to be 1 pixel wide +- `setWidth(width)` set the stroke width to a custom value, in pixels + +For coloring purposes, there is also the `randomColor` function: + +- `randomColor()` returns a random, full opaque CSS color +- `randomColor(opacity)` returns a random CSS color with the indicated opacity (in interval [0,1]) +- `randomColor(opacity, cycle=false)` if the second parameter is explicitly set to `false`, the random seed used to generate the random color will not be updated, and the resulting random color will be the same as when the function was previously called. + +For temporary work, where you might want to change some properties and then revert to the previous state, there are two functions available: + +- `cacheStyle()` cache the current collection of properties. This uses a stack, with each call adding a new "snapshot" on the stack. +- `restoreStyle()` restore the most recently cached state from the stack. + +### Coordinate transform function + +- `rotate(angle)` rotate the coordinate system by `angle` (clockwise, in radians) +- `scale(x, y)` scale the coordinate system by a factor of `x` horizontally, and `y` vertically +- `translate(x, y)` move the coordinate system by `x` units horizontally, and `y` units vertically +- `resetTransform()` reset the coordinate system to its default values +- `transform(a,b,c,d,e,f)` transform the coordinate system by applying a transformation matrix to it. This matrix has the form: + +``` + | a b c | +m = | d e f | + | 0 0 1 | +``` + +**note:** all transforms are with respect to (0,0) irrespective of where (0,0) has been moved to through successive transforms. + +In addition to transformations, there are also two functions to allow you to map screen coordinates (e.g. cursor position) to their corresponding transformed coordinate, and vice versa, to allow for drawing/computing points in either coordinate system + +- `screenToWorld(x, y)` converts a screen coordinate (x,y) to its corresponding transformed coordinate system coordinate (x',y') +- `worldToScreen(x, y)` converts a transformed coordinate system coordinate (x,y) to its corresponding screen coordinate (x', y') + +### Drawing functions + +- `arc(x, y, r, s, e)` draw a section of outline for a circle with radius `r` centered on (x,y), starting at angle `s` and ending at angle `e`. +- `circle(x, y, r)` draw a circle at (x,y) with radius `r` +- `clear(color="white", preserveTransforms=false)` clears the graphics to a specific CSS background color, resetting the transform by default. +- `drawAxes(hlabel, hs, he, vlabel, vs, ve, w, h)` draw a set of labelled axes, using `{hlabel, hs, he}` as horizontal label, with start marker `hs` and end marker `he`, and using `{vlabel, vs, ve}` as vertical label, with start marker `vs` and end marker `ve` +- `drawGrid(division = 20)` draw a grid with the specified spacing, colored using the current stroke color +- `drawShape(...shapes)` draw one or more saved full shapes (see below) +- `image(img, x = 0, y = 0, w = auto, h = auto)` draw an image at some (x,y) coordinate, defaulting to (0,0), scaled to some width and height, defaulting to the native image dimensions +- `line(x1, y1, x2, y2)` draw a line from (x1,y1) to (x2,y2) +- `plot(fn, start = 0, end = 1, steps = 24, xscale = 1, yscale = 1)` plot a function defined by `fn` (which must take a single numerical input, and output an object of the form `{x:..., y:...}`) for inputs in the interval [start,end], at a resolution of `steps` steps, with the resulting values scaled by `xscale` horizontally, and `yscale` vertically. +- `point(x, y)` draw a single point at (x,y) +- `rect(x, y, w, h)` draw a rectangle from (x,y) with width `w` and height `h` +- `redraw()` triggers a new draw loop. Use this instead of calling `draw()` directly when you wish to draw a new frame as part of event handling. +- `text(str, x, y, alignment = LEFT)` place text, colored by the fill color, anchored to (x,y), with the type of anchoring determined by `alignemtn`. The alignment constants `LEFT`, `RIGHT`, and `CENTER` are available. +- `wedge(x, y, r, s, e)` similar to arc, but draw a full wedge + +#### Shape drawing functions + +- `start(type = POLYGON, factor)` set up a new `Shape` as `this.currentShape` in preparation for receiving data. Types can be `POLYGON` (default), `CURVE` (Catmull-Rom), or `BEZIER`. If `CURVE` is specified, the `factor` indicates how loose or tight the resulting Catmull-Rom curve will be. +- `segment(type, factor)` set up a new section of the shape. If left unspecified the `type` and `factor` are inherited from the current `Shape`. +- `vertex(x, y)` add a point to the current `Shape`'s current segment. +- `end(close = false)` draw the current `Shape` +- `saveShape()` returns the current shape for later reuse + + +## Built in object types + +### Shape + +The `Shape` class reprents a drawable shape consisting of one or more polygonal, Catmull-Rom curve, or Bezier curve segments. It has a minimal API: + +- **`new Shape(type, factor, points = [])`** construct a new `Shape` of the specified `type` (options are `Shape.POLYGON`, `Shape.CURVE` for Catmull-Rom curves, and `Shape.BEZIER` for Bezier curves), with the specified `factor` (used for Catmull-Rom curve tightness), and optional list of points to prepopulate the first shape segment. +- `merge(other)` merge another `Shape`'s segments into this `Shape` +- `copy()` returns a copy of this `Shape` +- `newSegment(type, factor)` start a new segment in this `Shape` of the indicated type, with the indicated tightness factor. +- `vertex(p)` add an on-shape coordinate (x,y) to this `Shape`. How this vertex contributes to the overall shape drawing depends on the current segment type. + + +### Vector + +The `Vector` class represents a 2d/3d coordinate, with a minimal standard API: + +- **`new Vector(x,y,z?) / new Vector({x:,y:,z:}`** construct a new `Vector` +- `vector.dist(other, y, z = 0)` calculate the distance to some other vector-as-coordinate +- `vector.normalize(f)` return a new `Vector` representing a scaled copy of this vector, with length 1.0 +- `vector.getAngle()` get the angle between this vector and the x-axis. +- `vector.reflect(other)` reflect this vector-as-coordinate over the line that some other vector lies on +- `vector.add(other)` return a new `vector` representing the addition of some other vector to this vector +- `vector.subtract(other)` return a new `vector` representing the subtraction of some other vector to this vector +- `vector.scale(f = 1)` return a new `vector` representing the scaled version of this vector + + +### Matrix + +The `Matrix` class represents an `N`x`M` matrix, with minimal standard API: + +- **`new Matrix(n,m,data?)`** construct a new `Matrix`. If `data` is provided, the matrix will be filled with that. **warning:** `data` is assumed to be `n` x `m`, but is **not** validated. +- `setData(data)` **warning:** `data` is assumed to be `n` x `m`, but is **not** validated. +- `get(i, j)` get the value at row `i` and column `j` +- `set(i, j, value)` set the value at row `i` and column `j` +- `row(i)` get the entire `i`th row as an array of values +- `col(j)` get the entire `j`th column as a (flat) array of values +- `multiply(other)` return a new `Matrix` representing the right-multiplication of this matrix with some other matrix +- `invert()` return a new `Matrix` representing the inverse of this matrix, or `undefined` if no inverse exists +- `transpose()` return a new `Matrix` representing the transpose of this matrix +... + +### Bezier + +The `Bezier` class is an instance of [bezier.js](https://pomax.github.io/bezierjs/) with all its API functions, extended for use on the canvas: + +- static `defaultQuadratic(apiInstance)` returns a new quadratic `Bezier` with preset coordinate values tailored to the Primer on Bezier Curves. The `apiInstance` must be a reference to a valid Graphics-API instance (typically thiat will simply be `this` in your code). +- static `defaultCubic(apiInstance)` returns a new cubic `Bezier` with preset coordinate values tailored to the Primer on Bezier Curves. The `apiInstance` must be a reference to a valid Graphics-API instance (typically thiat will simply be `this` in your code). +- static `fitCurveToPoints(apiInstance, points, tvalues)` returns a new `n`-dimensional `Bezier` that has been fit to the provided list of points, constrained to the provided `tvalues`, using MSE polygonal curve fitting. The `apiInstance` must be a reference to a valid Graphics-API instance (typically thiat will simply be `this` in your code). + + +The extended API in addition to these static functions are: + +- **`new Bezier(apiInstance, ...coords)`** construct a new `Bezier` curve controlled by three or more points, either supplied as numerical arguments, or as point objects `{x:..., y:..., z?:...}` where `z` is optional. The `apiInstance` must be a reference to a valid Graphics-API instance (typically thiat will simply be `this` in your code). +- `project(x, y)` returns an `{x:..., y:...}` object representing the projection of some point (x,y) onto this curve. +- `getPointNear(x, y, d = 5)` returns either `undefined`, or one of the `Bezier` curve's control points if the specified (x,y) coordinate is `d` or fewer pixels from a control point. +- `drawCurve(color = #333)` draws this curve on the canvas, with optional custom color +- `drawPoints(labels = true)` draws the curve's control points, with optional coordinate labels (defaulting to true) +- `drawSkeleton(color = #555)` draws this curve's coordinate polygon, with optional custom color +- `getStrutPoints(t)` get the list of points obtained through "de Casteljau" interpolation, for a given `t` value +- `drawStruts(t, color = "black", showpoints = true)` draws this curve's "de Casteljau" points and lines, for a given `t` value, with optional custom color, and optional omission of the points if only the lines are required. +- `drawBoundingBox(color = "black")` draw the axis-aligned bounding box for this `Bezier` curve with optional custom color + +### BSpline + +The `BSpline` class represents a generaic [B-spline](https://en.wikipedia.org/wiki/B-spline) curve, with a minimal API: + +- **`new BSpline(apiInstance, points)`** constructs a B-spline controlled by a points array in which each element may be either of the form `{x: ..., y: ...}` or a numerical tuple `[x,y]`. The `apiInstance` must be a reference to a valid Graphics-API instance (typically thiat will simply be `this` in your code). +- `getLUT(count)` returns an array of `count` coordinates of the form `{x:...,y:...}`, representing a polygonal approximation of this `BSpline`. +- `formKnots(open = false)` set-and-return the list of B-spline knots using either the standard uniform interval spacing, or if `open` is set to `true`, special spacing to effect a B-spline that start and ends at the actual start and end coordinates. The knot array returned in this fashion is a live array, and updating its values **will** change the B-spline's shape. +- `formUniformKnots()` set-and-return uniformaly spaced knot values. The knot array returned in this fashion is a live array, and updating its values **will** change the B-spline's shape. +- `formWeights()` set-and-return the array of weights. These will all be uniformly initialized to 1, with the weight array returned being a live array, so that updating its values will change the B-spline's shape. diff --git a/docs/js/custom-element/api/graphics-api.js b/docs/js/custom-element/api/graphics-api.js index 6342d35b..00dff793 100644 --- a/docs/js/custom-element/api/graphics-api.js +++ b/docs/js/custom-element/api/graphics-api.js @@ -38,6 +38,9 @@ class GraphicsAPI extends BaseAPI { get HAND() { return `pointer`; } + get CROSS() { + return `crosshair`; + } get POLYGON() { return Shape.POLYGON; } @@ -56,25 +59,6 @@ class GraphicsAPI extends BaseAPI { get RIGHT() { return `right`; } - // hatching patterns - get HATCH1() { - return this.HATCHING[0]; - } - get HATCH2() { - return this.HATCHING[1]; - } - get HATCH3() { - return this.HATCHING[2]; - } - get HATCH4() { - return this.HATCHING[3]; - } - get HATCH5() { - return this.HATCHING[4]; - } - get HATCH6() { - return this.HATCHING[5]; - } onMouseDown(evt) { super.onMouseDown(evt); @@ -205,6 +189,17 @@ class GraphicsAPI extends BaseAPI { this.ctx.scale(x, y); } + /** + * transforms: universal free transform based on applying + * + * | a b c | + * m = | d e f | + * | 0 0 1 | + */ + transform(a, b, c, d, e, f) { + this.ctx.transform(a, b, c, d, e, f); + } + /** * transforms: screen to world */ @@ -371,7 +366,7 @@ class GraphicsAPI extends BaseAPI { /** * Set the context lineWidth */ - setWidth(width) { + setWidth(width = 1) { this.ctx.lineWidth = width; } @@ -497,15 +492,22 @@ class GraphicsAPI extends BaseAPI { /** * Draw a circular arc */ - arc(x, y, r, s, e, cx = false, cy = false) { + arc(x, y, r, s, e, wedge = false) { this.ctx.beginPath(); - if (cx !== false && cy != false) this.ctx.moveTo(cx, cy); + if (wedge) this.ctx.moveTo(x, y); this.ctx.arc(x, y, r, s, e); - if (cx !== false && cy != false) this.ctx.moveTo(cx, cy); + if (wedge) this.ctx.moveTo(x, y); this.ctx.fill(); this.ctx.stroke(); } + /** + * Draw a circular wedge + */ + wedge(x, y, r, s, e) { + this.arc(x, y, r, s, e, true); + } + /** * Draw text on the canvas */ @@ -540,11 +542,11 @@ class GraphicsAPI extends BaseAPI { * Draw a function plot from [start] to [end] in [steps] steps. * Returns the plot shape so that it can be cached for redrawing. */ - plot(fn, start = 0, end = 1, steps = 24, xscale = 1, yscale = 1) { + plot(generator, start = 0, end = 1, steps = 24, xscale = 1, yscale = 1) { const interval = end - start; this.start(); for (let i = 0, e = steps - 1, v; i < steps; i++) { - v = fn(start + (interval * i) / e); + v = generator(start + (interval * i) / e); this.vertex(v.x * xscale, v.y * yscale); } this.end(); @@ -585,7 +587,6 @@ class GraphicsAPI extends BaseAPI { this.currentShape.merge(shapes[i]); } this.end(); - this.STARTREPORTING = false; } /** @@ -601,6 +602,13 @@ class GraphicsAPI extends BaseAPI { this.ctx.stroke(); } + /** + * Yield a snapshot of the current shape. + */ + save() { + return this.currentShape; + } + /** * Polygon draw function */ @@ -645,12 +653,6 @@ class GraphicsAPI extends BaseAPI { this.ctx.bezierCurveTo(p1.x, p1.y, p2.x, p2.y, p3.x, p3.y); } } - /** - * Yield a snapshot of the current shape. - */ - save() { - return this.currentShape; - } /** * convenient grid drawing function @@ -704,8 +706,15 @@ class GraphicsAPI extends BaseAPI { return Math.round(v); } - random(v = 1) { - return Math.random() * v; + random(a, b) { + if (a === undefined) { + a = 0; + b = 1; + } else if (b === undefined) { + b = a; + a = 0; + } + return a + Math.random() * (b - a); } abs(v) { @@ -744,10 +753,6 @@ class GraphicsAPI extends BaseAPI { return Math.atan2(dy, dx); } - pow(v, p) { - return Math.pow(v, p); - } - binomial(n, k) { return binomial(n, k); } diff --git a/docs/js/custom-element/api/types/bezier.js b/docs/js/custom-element/api/types/bezier.js index 319a6d66..c1654dd5 100644 --- a/docs/js/custom-element/api/types/bezier.js +++ b/docs/js/custom-element/api/types/bezier.js @@ -160,7 +160,7 @@ class Bezier extends Original { return p; } - drawBoundingBox(color) { + drawBoundingBox(color = `black`) { let bbox = this.bbox(), mx = bbox.x.min, my = bbox.y.min, @@ -169,7 +169,7 @@ class Bezier extends Original { api = this.api; api.cacheStyle(); api.noFill(); - api.setStroke(color ? color : `black`); + api.setStroke(color); api.rect(mx, my, MX - mx, MY - my); api.restoreStyle(); } diff --git a/docs/js/custom-element/api/types/bspline.js b/docs/js/custom-element/api/types/bspline.js index b49173dc..6c47c654 100644 --- a/docs/js/custom-element/api/types/bspline.js +++ b/docs/js/custom-element/api/types/bspline.js @@ -50,34 +50,9 @@ class BSpline { return (this.knots = [...new Array(this.points.length + DEGREE + 1)].map((_, i) => i)); } - formNodes() { - const knots = this.knots; - const domain = [DEGREE, knots.length - 1 - DEGREE], - nodes = []; - - for (let k = 0; k < this.points.length; k++) { - let node = 0; - for (let offset = 1; offset <= DEGREE; offset++) { - node += knots[k + offset]; - } - node /= DEGREE; - if (node < knots[domain[0]]) continue; - if (node > knots[domain[1]]) continue; - nodes.push(node); - } - - return (this.nodes = nodes); - } - formWeights() { return (this.weights = this.points.map((p) => 1)); } - - setDegree(d) { - DEGREE += d; - this.knots = this.formKnots(); - this.nodes = this.formNodes(); - } } export { BSpline }; diff --git a/docs/js/custom-element/api/types/matrix.js b/docs/js/custom-element/api/types/matrix.js index e9ef38b1..e8f5082e 100644 --- a/docs/js/custom-element/api/types/matrix.js +++ b/docs/js/custom-element/api/types/matrix.js @@ -143,11 +143,11 @@ class Matrix { row(i) { return this.data[i]; } - col(i) { + col(j) { var d = this.data, col = []; for (let r = 0, l = d.length; r < l; r++) { - col.push(d[r][i]); + col.push(d[r][j]); } return col; } diff --git a/docs/js/custom-element/api/util/shape.js b/docs/js/custom-element/api/util/shape.js index 57a0626d..8415eb62 100644 --- a/docs/js/custom-element/api/util/shape.js +++ b/docs/js/custom-element/api/util/shape.js @@ -7,7 +7,7 @@ class Shape { constructor(type, factor, points = []) { this.first = false; this.segments = []; - this.addSegment(type, factor); + this.newSegment(type, factor); points.forEach((p) => this.vertex(p)); } merge(other) { @@ -22,7 +22,7 @@ class Shape { copy.segments = this.segments.map((s) => s.copy()); return copy; } - addSegment(type, factor) { + newSegment(type, factor) { this.currentSegment = new Segment(type, factor); this.segments.push(this.currentSegment); } diff --git a/docs/news/2020-09-20.html b/docs/news/2020-09-20.html index 24403606..160c0fd9 100644 --- a/docs/news/2020-09-20.html +++ b/docs/news/2020-09-20.html @@ -27,7 +27,7 @@ - + diff --git a/docs/zh-CN/index.html b/docs/zh-CN/index.html index f5dba2fd..07653d75 100644 --- a/docs/zh-CN/index.html +++ b/docs/zh-CN/index.html @@ -33,7 +33,7 @@ - + @@ -1457,7 +1457,7 @@ function drawCurve(points[], t): Scripts are disabled. Showing fallback image. - + @@ -2950,21 +2950,21 @@ y = curve.get(t).y > Scripts are disabled. Showing fallback image. - + Scripts are disabled. Showing fallback image. - + Scripts are disabled. Showing fallback image. - + @@ -3197,7 +3197,7 @@ y = curve.get(t).y > Scripts are disabled. Showing fallback image. - + @@ -3221,7 +3221,7 @@ y = curve.get(t).y > Scripts are disabled. Showing fallback image. - + @@ -5029,7 +5029,7 @@ for p = 1 to points.length-3 (inclusive): Scripts are disabled. Showing fallback image. - + @@ -5154,7 +5154,7 @@ for p = 1 to points.length-3 (inclusive): Scripts are disabled. Showing fallback image. - +