import { sin } from "@thi.ng/dsp/osc-sin" ; import { asCubic } from "@thi.ng/geom/as-cubic" ; import { svgDoc } from "@thi.ng/geom/as-svg" ; import { circle } from "@thi.ng/geom/circle" ; import { group } from "@thi.ng/geom/group" ; import { line } from "@thi.ng/geom/line" ; import { pathFromCubics } from "@thi.ng/geom/path" ; import { star } from "@thi.ng/geom/polygon" ; import { withAttribs } from "@thi.ng/geom/with-attribs" ; import { fromRAF } from "@thi.ng/rstream/raf" ; import { reactive , Stream } from "@thi.ng/rstream/stream" ; import { sync } from "@thi.ng/rstream/sync" ; import { updateDOM } from "@thi.ng/transducers-hdom" ; import { comp } from "@thi.ng/transducers/comp" ; import { iterator } from "@thi.ng/transducers/iterator" ; import { map } from "@thi.ng/transducers/map" ; import { mapcat } from "@thi.ng/transducers/mapcat" ; import { partition } from "@thi.ng/transducers/partition" ; import { reducer } from "@thi.ng/transducers/reduce" ; import { scan } from "@thi.ng/transducers/scan" ; const BUTTONS = { blue : "bg-blue white hover-bg-light-blue hover-navy" , green : "bg-green white hover-bg-light-green hover-dark-green" , } ; const emitOnStream = ( stream , val ) => ( ) => stream . next ( val ) ; const button = ( _ , clazz , onclick , label ) => [ "a" , { href : "#" , onclick , class : "dib w4 mr2 pa2 link " + clazz , } , label , ] ; const slider = ( _ , attribs , stream , label ) => [ "div.mv3" , [ "span.dib.w4.mr2" , label ] , [ "input.mr3" , { type : "range" , value : stream . deref ( ) , oninput : ( e ) => stream . next ( parseFloat ( e . target . value ) ) , ... attribs , } , ] , stream . deref ( ) ! . toFixed ( 1 ) , ] ; const app = ( _mode , _uniform , _scale , _uniScale ) => ( { poly , mode , uniform , scale , uniScale } ) => { const cubics = asCubic ( poly , { breakPoints : mode , scale : scale * ( uniform ? uniScale : 1 ) , uniform , } ) ; const controlPoints = iterator ( comp ( mapcat ( ( x ) => x . points ) , map ( ( p ) => circle ( p , 0.75 ) ) ) , cubics ) ; const handles = iterator ( comp ( mapcat ( ( x ) => x . points ) , partition ( 2 ) , map ( line ) ) , cubics ) ; return [ "div.sans-serif.ma3" , [ "div" , [ button , BUTTONS . blue , emitOnStream ( _mode , true ) , mode ? "break points" : "control points" , ] , [ button , BUTTONS . green , emitOnStream ( _uniform , true ) , uniform ? "uniform" : "non-uniform" , ] , [ slider , { min : - 1.3 , max : 1.3 , step : 0.1 } , _scale , "tangent scale" , ] , [ slider , { min : 0 , max : 100 , step : 1 , disabled : ! uniform } , _uniScale , "uniform scale" , ] , ] , [ "div" , svgDoc ( { convert : true , width : 480 , height : 480 , viewBox : "-150 -150 300 300" , fill : "none" , stroke : "#ccc" , "stroke-width" : 0.25 , } , poly , withAttribs ( pathFromCubics ( cubics ) , { stroke : mode ? "blue" : "red" , "stroke-width" : 1 , } ) , group ( { stroke : "#333" } , [ ... controlPoints , ... handles ] ) ) , ] , ] ; } ; const poly = fromRAF ( ) . transform ( map ( ( t ) => star ( 100 , 6 , [ sin ( t , 0.01 , 0.5 , 0.8 ) , 1 ] ) ) ) ; const mode = reactive ( false ) ; const uniform = reactive ( false ) ; const scale = reactive ( 0.5 ) ; const uniScale = reactive ( 25 ) ; const toggle = scan ( reducer ( ( ) => true , ( acc ) => ! acc ) ) ; const main = sync ( { src : { poly , mode : mode . transform ( toggle ) , uniform : uniform . transform ( toggle ) , scale , uniScale , } , } ) ; main . transform ( map ( app ( mode , uniform , scale , uniScale ) ) , updateDOM ( ) ) ;