import { peek } from "@thi.ng/arrays/peek" ; import { polyline as gPolyline } from "@thi.ng/geom/polyline" ; import { resample } from "@thi.ng/geom/resample" ; import { vertices } from "@thi.ng/geom/vertices" ; import { circle } from "@thi.ng/hiccup-svg/circle" ; import { group } from "@thi.ng/hiccup-svg/group" ; import { polyline } from "@thi.ng/hiccup-svg/polyline" ; import { svg } from "@thi.ng/hiccup-svg/svg" ; import { type , GestureEvent , gestureStream } from "@thi.ng/rstream-gestures" ; import { fromIterable } from "@thi.ng/rstream/iterable" ; import { merge } from "@thi.ng/rstream/merge" ; import { sync } from "@thi.ng/rstream/sync" ; import { updateDOM } from "@thi.ng/transducers-hdom" ; import { comp } from "@thi.ng/transducers/comp" ; import { filter } from "@thi.ng/transducers/filter" ; import { map } from "@thi.ng/transducers/map" ; import { multiplexObj } from "@thi.ng/transducers/multiplex-obj" ; import { noop } from "@thi.ng/transducers/noop" ; import { partition } from "@thi.ng/transducers/partition" ; import { push } from "@thi.ng/transducers/push" ; import { transduce } from "@thi.ng/transducers/transduce" ; import type { Vec } from "@thi.ng/vectors" ; import { angleBetween2 } from "@thi.ng/vectors/angle-between" ; import { mixN2 } from "@thi.ng/vectors/mixn" ; import { sub2 } from "@thi.ng/vectors/sub" ; import { CTA } from "./config" ; param raw param processed const app = ( { raw , processed , } ) => [ "div" , svg ( { width : window . innerWidth , height : window . innerHeight , stroke : "none" , fill : "none" , } , path ( raw || [ ] , processed . path , processed . corners || [ ] ) ) , [ "div.fixed.top-0.left-0.ma3" , [ "div" , `raw: ${ ( raw && raw . length ) || 0 }` ] , [ "div" , `resampled: ${ ( processed && processed . path . length ) || 0 }` ] , [ "div" , `corners: ${ ( processed && processed . corners . length ) || 0 }` ] , ] , ] ; param raw param sampled param corners const path = ( raw , sampled , corners ) => group ( { __diff : false } , polyline ( raw , { stroke : "#444" } ) , map ( ( p ) => circle ( p , 2 , { fill : "#444" } ) , raw ) , polyline ( sampled , { stroke : "#fff" } ) , map ( ( p ) => circle ( p , 2 , { fill : "#fff" } ) , sampled ) , map ( ( p ) => circle ( p , 6 , { fill : "#cf0" } ) , corners ) , circle ( sampled [ 0 ] , 6 , { fill : "#f0c" } ) , circle ( peek ( sampled ) , 6 , { fill : "#0cf" } ) ) ; param step param pts const sampleUniform = ( step , pts ) => vertices ( resample ( gPolyline ( pts ) , { dist : step } ) ) ; param path const smoothPath = ( smooth , path ) => { const res = [ path [ 0 ] ] ; for ( let i = 1 , n = path . length ; i < n ; i ++ ) { res . push ( mixN2 ( [ ] , path [ i ] , res [ i - 1 ] , smooth ) ) ; } return res ; } ; param thresh const isCorner = ( thresh ) => ( [ a , b , c ] ) => angleBetween2 ( sub2 ( [ ] , b , a ) , sub2 ( [ ] , b , c ) , true ) < thresh ; /**
 * Gesture event processor. Collects gesture event positions into an
 * array of Vec2.
 */ const collectPath = ( ) => { let pts = [ ] ; return ( g ) => { console . log ( g ) ; switch ( g . type ) { case "start" : pts = [ g . pos ] ; break ; case "drag" : pts . push ( g . pos ) ; break ; } return pts ; } ; } ; const gesture = merge < any , any > ( { src : [ fromIterable ( [ CTA ] ) , gestureStream ( document . body ) . transform ( filter ( ( g ) => ! ( g . type === "move" || g . type === "zoom" ) ) , map ( collectPath ( ) ) ) , ] , } ) ; sync ( { src : { raw : gesture , processed : gesture . transform ( comp ( map ( ( pts ) => smoothPath ( 3 / 4 , pts ) ) , map ( ( pts ) => sampleUniform ( 20 , pts ) ) , multiplexObj ( { path : noop ( ) , corners : map ( ( pts ) => transduce ( comp ( partition ( 3 , 1 ) , filter ( isCorner ( ( Math . PI * 2 ) / 3 ) ) , map ( ( x ) => x [ 1 ] ) ) , push ( ) , pts ) ) , } ) ) ) , } , } ) . transform ( map ( app ) , updateDOM ( ) ) ;