diff --git a/src/components/ColorScatterPlot.svelte b/src/components/ColorScatterPlot.svelte index fff6d716..6de30189 100644 --- a/src/components/ColorScatterPlot.svelte +++ b/src/components/ColorScatterPlot.svelte @@ -10,6 +10,7 @@ makeScales, dragEventToColorZ, dragEventToColorXY, + screenSpaceAvg, } from "../lib/utils"; import configStore from "../stores/config-store"; import { scaleLinear } from "d3-scale"; @@ -100,6 +101,9 @@ $: x = scales.x; $: y = scales.y; $: z = scales.z; + // screen -> color space + $: xInv = scales.xInv; + $: yInv = scales.yInv; $: CircleProps = (color: Color, i: number) => ({ cx: x(color), @@ -137,7 +141,13 @@ dragging: interactionMode === "drag", }; - let interactionMode: "idle" | "drag" | "select" | "point-touch" = "idle"; + let interactionMode: + | "idle" + | "drag" + | "select" + | "point-touch" + | "stretch" + | "rotate" = "idle"; let dragTargetPos = { x: 0, y: 0 }; let dragDelta = { x: 0, y: 0 }; function dragStart(e: any) { @@ -258,6 +268,126 @@ "fill-opacity": "0.5", class: "pointer-events-none", }; + + $: initialRotationColors = [] as Color[]; + $: initialRotatePos = { x: 0, y: 0 }; + $: rotatePivotCenter = { x: 0, y: 0 }; + $: rotatePosition = { x: 0, y: 0 }; + + function rotateStart(e: any) { + // console.log("rotate start"); + startDragging(); + interactionMode = "rotate"; + initialRotationColors = [...colors]; + // const pos = toXY(e); + initialRotatePos = { + x: e.target.getBBox().x, + y: e.target.getBBox().y, + }; + rotatePivotCenter = screenSpaceAvg( + focusedColors + .map((x) => initialRotationColors[x]) + .map((el) => ({ + x: x(el), + y: y(el), + })) + ); + rotateUpdate(e); + } + + function rotateUpdate(e: any) { + // console.log("rotate update"); + let currentPos = toXY(e); + currentPos = { + x: currentPos.x - svgContainer.getBoundingClientRect().x, + y: currentPos.y - svgContainer.getBoundingClientRect().y, + }; + rotatePosition = currentPos; + let newAngle = Math.atan2( + currentPos.y - rotatePivotCenter.y, + currentPos.x - rotatePivotCenter.x + ); + const initialAngle = Math.atan2( + initialRotatePos.y - rotatePivotCenter.y, + initialRotatePos.x - rotatePivotCenter.x + ); + let angle = newAngle - initialAngle; + angle = angle < 0 ? angle + 2 * Math.PI : angle; + const xc = rotatePivotCenter.x; + const yc = rotatePivotCenter.y; + const newColors = focusedColors + .map((x) => initialRotationColors[x]) + .map((color) => { + const x1 = x(color); + const y1 = y(color); + const x3 = + Math.cos(angle) * (x1 - xc) - Math.sin(angle) * (y1 - yc) + xc; + const y3 = + Math.sin(angle) * (x1 - xc) + Math.cos(angle) * (y1 - yc) + yc; + const newChannels = [...color.toChannels()] as [number, number, number]; + newChannels[config.xChannelIndex] = xInv(x3, y3); + newChannels[config.yChannelIndex] = yInv(x3, y3); + return color.fromChannels(newChannels); + }); + onColorsChange(newColors); + } + function rotateEnd() { + // console.log("rotate end"); + interactionMode = "idle"; + stopDragging(); + } + + $: handles = [ + // { + // type: "stretch", + // label: "left", + // x: pos.xPos - 15, + // y: pos.yPos + pos.selectionHeight / 2, + // }, + // { + // type: "stretch", + // label: "right", + // x: pos.xPos + pos.selectionWidth + 5, + // y: pos.yPos + pos.selectionHeight / 2, + // }, + // { + // type: "stretch", + // label: "top", + // x: pos.xPos + pos.selectionWidth / 2, + // y: pos.yPos - 15, + // }, + // { + // type: "stretch", + // label: "bottom", + // x: pos.xPos + pos.selectionWidth / 2, + // y: pos.yPos + pos.selectionHeight + 5, + // }, + { + type: "rotate", + label: "", + x: pos.xPos, + y: pos.yPos, + }, + { + type: "rotate", + label: "", + x: pos.xPos, + y: pos.yPos + pos.selectionHeight, + }, + { + type: "rotate", + label: "", + x: pos.xPos + pos.selectionWidth, + y: pos.yPos, + }, + { + type: "rotate", + label: "", + x: pos.xPos + pos.selectionWidth, + y: pos.yPos + pos.selectionHeight, + }, + ]; + let svgContainer: any; @@ -269,7 +399,7 @@