From 933ef684ad4b3becb60e0349480d6fbe83a1dc2a Mon Sep 17 00:00:00 2001 From: Neil Graham Date: Thu, 7 Sep 2023 17:15:55 +1200 Subject: [PATCH] Brush mirroring --- web/page/bspaint.js | 141 ++++++++++++++++++++++++++++++++++++++++++-- web/page/style.css | 70 +++++++++++++++++++++- 2 files changed, 206 insertions(+), 5 deletions(-) diff --git a/web/page/bspaint.js b/web/page/bspaint.js index 8570e87..a1ec70b 100644 --- a/web/page/bspaint.js +++ b/web/page/bspaint.js @@ -26,8 +26,8 @@ function cells(x=1,y=1) { } return result; } - } + function mirrorX(strokes) { return [...strokes, ...strokes.map( a=>a.map(({x,y})=>({x:1-x,y})))]; } @@ -35,6 +35,39 @@ function mirrorY(strokes) { return [...strokes, ...strokes.map( a=>a.map(({x,y})=>({x,y:1-y})))]; } +function mirrorDiagonalXY(strokes) { + return [...strokes, ...strokes.map( a => a.map(({x, y}) => ({x: y, y: x})))]; +} + +function mirrorDiagonalY1MinusX(strokes) { + return [...strokes, ...strokes.map( a => a.map(({x, y}) => ({x: 1 - y, y: 1 - x})))]; +} + +function rotationalSymmetry(ways) { + const angleIncrement = (Math.PI * 2) / ways; + const originX = 0.5, originY = 0.5; + + return function(strokes) { + let newStrokes = [...strokes]; + + for(let i = 1; i < ways; i++) { + const angle = i * angleIncrement; + const cosAngle = Math.cos(angle); + const sinAngle = Math.sin(angle); + + const rotatedStrokes = strokes.map( a => a.map(({x, y}) => ({ + x: cosAngle * (x - originX) - sinAngle * (y - originY) + originX, + y: sinAngle * (x - originX) + cosAngle * (y - originY) + originY + }))); + + newStrokes = [...newStrokes, ...rotatedStrokes]; + } + + return newStrokes; + }; +} + + function composeFunction(fnA,fnB) { return (...args)=>(fnB(fnA(...args))) } @@ -163,7 +196,7 @@ function blankCanvas(width=512, height=width, filled=true) { } function circleImage(diameter) { - console.log({diameter}) + const imageSize=(diameter+4)|0; let result = blankCanvas(imageSize,imageSize,false); result.ctx.arc(imageSize/2,imageSize/2,diameter/2,0,Math.PI*2); @@ -176,6 +209,78 @@ function circleImage(diameter) { return result; } +function gridImage(rows,columns,size=24) { + const result = blankCanvas(size,size,false); + const ctx = result.ctx; + const rowHeight = size/rows; + const columnWidth = size/columns; + for (let tx=0; txa; + +function updateMirrors() { + let result = a=>a; + if($("#mirror_grid.mirror.button").hasClass("down")) { + const columns = $(".grid_mirrors #repeat_x").val(); + const rows = $(".grid_mirrors #repeat_y").val(); + const gridFunction = cells(columns,rows); + result = composeFunction(result,gridFunction); + } + if($("#mirror_x.mirror.button").hasClass("down")) result = composeFunction(result,mirrorX); + if($("#mirror_y.mirror.button").hasClass("down")) result = composeFunction(result,mirrorY); + if($("#mirror_tlbr.mirror.button").hasClass("down")) result = composeFunction(result,mirrorDiagonalXY); + if($("#mirror_trbl.mirror.button").hasClass("down")) result = composeFunction(result,mirrorDiagonalY1MinusX); + if($("#mirror_rotational.mirror.button").hasClass("down")) { + const ways = $(".rotational_mirrors input").val(); + const rotationFunction = rotationalSymmetry(ways); + result = composeFunction(result,rotationFunction); + } + + mirrorFunction=result; +} + function createDrawArea(canvas = blankCanvas()) { const undoDepth = 10; const element = document.createElement("div") @@ -305,6 +410,7 @@ function createDrawArea(canvas = blankCanvas()) { startDraw(x,y) { if (!this.activeLayer.visible) return; //don't draw on hidden layers. + this.strokeModifier=mirrorFunction; //let data = this.activeLayer.ctx.getAllImageData() //activeOperationCanvas.ctx.putImageData(data,0,0); this.isDrawing=true; @@ -437,11 +543,38 @@ function initPaint(){ updateBrushCursor(p); } - }); - $(".panel").append(brushSizeControl) + $(".panel").append(brushSizeControl).append(` +
+
+
+
+
+
+
+ + + + +
+
+
+
+ +
+ `); + + $('.grid_mirrors input').on("change",_=>{updateMirrorGridButtonImage(); updateMirrors()}) + $('.rotational_mirrors input').on("change",_=>{updateMirrorRotationButtonImage(); updateMirrors()}) + + $('.mirror.button').on("mousedown", e=>{ + e.currentTarget.classList.toggle('down'); + updateMirrors(); + }) + updateMirrorGridButtonImage(); + updateMirrorRotationButtonImage(); window.test1=createDrawArea(); window.test2=createDrawArea(); diff --git a/web/page/style.css b/web/page/style.css index 27a3092..898d805 100644 --- a/web/page/style.css +++ b/web/page/style.css @@ -43,7 +43,7 @@ canvas { height:32px; margin:2px; background-color : #ddd; - + box-sizing: border-box; box-shadow: -1px -1px 1px 1px black inset, 1px 1px 1px 1px white inset; } .button:hover { @@ -62,6 +62,74 @@ canvas { margin-bottom: 9px; } +.subpanel { + display: inline-block; + border-left: 1px solid black; + padding-left: 6px; + height: var(--top-panel-height); + vertical-align: top; + box-sizing:border-box; +} + +.subpanel input { + display:block; +} + +.subpanel.simple_mirrors { + width:70px; +} + +.subpanel.grid_mirrors>span { + height: var(--top-panel-height); + vertical-align: top; + padding-top: 10px; +} + + +.mirror.button { + width:24px; + height:24px; +} + +.mirror.button#mirror_y { + background-image: linear-gradient(to bottom, #0000 45%, #000f 50%, #0000 55%); +} + +.mirror.button#mirror_x { + background-image: linear-gradient(to right, #0000 45%, #000f 50%, #0000 55%); +} + +.mirror.button#mirror_tlbr { + background-image: linear-gradient(to right top, #0000 45%, #000f 50%, #0000 55%); +} + +.mirror.button#mirror_trbl { + background-image: linear-gradient(to right bottom, #0000 45%, #000f 50%, #0000 55%); +} + +.mirror.button#mirror_grid { + width: 48px; + height: 48px; + margin-top: 6px; +} + +.subpanel.rotational_mirrors { + width: 55px; + text-align:center; +} + +.mirror.button#mirror_rotational { + width: 36px; + height: 36px; + margin-bottom:0; +} + +.subpanel.rotational_mirrors input { + width: 42px; +} + + + #palette { position:absolute; left:0px;