Skip to content
Snippets Groups Projects
Commit b94c7f36 authored by Moritz Langenstein's avatar Moritz Langenstein
Browse files

(ml5717) (ztw17) Fixed path smoothing (unless weight changes extremely rapidly)

parent 4b7ee9e1
No related branches found
No related tags found
1 merge request!78(ml5717) (ztw17) Small frontend improvements
Pipeline #109516 passed
......@@ -4,7 +4,7 @@ import "array-flat-polyfill"
// Emit input events and receive draw calls seperately - these must be piped
// together externally if desired.
import { line, curveCatmullRom } from "d3-shape"
import { line, curveCatmullRom, curveLinear } from "d3-shape"
import { canvas } from "./elements.js"
......@@ -12,46 +12,25 @@ const SVG_URL = "http://www.w3.org/2000/svg"
import * as HTML from "./elements.js"
// TODO: look at paper.js which has path.smooth() and curve.getPart()
// TODO: look at snap.svg which has path.getTotalLength(), path.subpath() and Snap.closestPoint()
// TODO: look at curve interpolation that respects mouse points based on velocity
const curve = curveCatmullRom.alpha(1.0)
const smoothLine = line()
.x((d) => d[0])
.y((d) => d[1])
.curve(curve)
const straightLine = line()
.x((d) => d[0])
.y((d) => d[1])
.curve(curveLinear)
const pathGroupElems = new Map()
const MAX_POINT_DISTANCE = 5
const MAX_RADIUS_DELTA = 0.05
// Interpolate a path so that:
// - The distance between two adjacent points is capped at MAX_POINT_DISTANCE.
// - The radius delta between two adjacent points is capped at
// MAX_RADIUS_DELTA
// MAX_RADIUS_DELTA.
// If paths are too choppy, try decreasing these constants.
const smoothPath = ([...path]) => {
// Apply MAX_POINT_DISTANCE.
for (let i = 1; i < path.length; i++) {
const dx = path[i][0] - path[i - 1][0]
const dy = path[i][1] - path[i - 1][1]
const dw = path[i][2] - path[i - 1][2]
const distance = Math.hypot(dx, dy)
const segmentsToSplit = Math.ceil(distance / MAX_POINT_DISTANCE)
const newPoints = []
for (let j = 1; j < segmentsToSplit; j++) {
newPoints.push([
path[i - 1][0] + (dx / segmentsToSplit) * j,
path[i - 1][1] + (dy / segmentsToSplit) * j,
path[i - 1][2] + (dw / segmentsToSplit) * j,
path[i - 1][3],
])
}
path.splice(i, 0, ...newPoints)
i += newPoints.length
}
// Apply MAX_RADIUS_DELTA.
for (let i = 1; i < path.length; i++) {
const dx = path[i][0] - path[i - 1][0]
......@@ -103,26 +82,14 @@ const ensurePathGroupElem = (id) => {
return groupElem
}
const renderPoint = (point) => {
const circleElem = createSvgElem("circle")
circleElem.setAttribute("stroke", "none")
circleElem.setAttribute("fill", point[3])
circleElem.setAttribute("cx", point[0])
circleElem.setAttribute("cy", point[1])
circleElem.setAttribute("r", point[2])
return circleElem
}
const renderSubpath = (subpath) => {
if (subpath.length == 1) {
return renderPoint(subpath[0])
}
const renderSubpath = (subpath, pathSmooth) => {
const pathElem = createSvgElem("path")
pathElem.setAttribute("stroke", subpath[0][3])
pathElem.setAttribute("stroke-width", subpath[0][2] * 2)
pathElem.setAttribute("d", smoothLine(subpath))
pathElem.setAttribute(
"d",
pathSmooth ? smoothLine(subpath) : straightLine(subpath),
)
return pathElem
}
......@@ -225,10 +192,37 @@ export const splitOnPressures = ([...path]) => {
}
export const renderPath = (id, points, pathEraseIntervals) => {
let index = -1
if (Object.keys(pathEraseIntervals).length == 0 && points.length == 5) {
index = 0
} else if (Object.keys(pathEraseIntervals).length > 0 && points.length > 5) {
for (let i = 0; i < points.length - 5; i++) {
if (
!pathEraseIntervals[i] ||
pathEraseIntervals[i].length != 1 ||
pathEraseIntervals[i][0][0] != 0 ||
pathEraseIntervals[i][0][1] != 1
) {
break
}
}
index = points.length - 5
}
const pathSmooth = !(
index >= 0 &&
points[index][0] == points[index + 4][0] &&
points[index][1] == points[index + 4][1] &&
points[index][2] == points[index + 4][2] &&
points[index][3] == points[index + 4][3]
)
let subpaths = applyErasureIntervals(points, pathEraseIntervals)
subpaths = subpaths.map(smoothPath)
subpaths = subpaths.flatMap(splitOnPressures)
const subpathElems = subpaths.map((subpath) => renderSubpath(subpath))
const subpathElems = subpaths.map((subpath) =>
renderSubpath(subpath, pathSmooth),
)
const pathGroupElem = ensurePathGroupElem(id)
subpathElems.forEach((subpathElem) => pathGroupElem.appendChild(subpathElem))
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment