Skip to content

Commit d8d5cb1

Browse files
committed
color picker v1 + layout touches + material node
1 parent 0177883 commit d8d5cb1

File tree

16 files changed

+614
-46
lines changed

16 files changed

+614
-46
lines changed

src/Editor.ts

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,27 @@ export class Editor {
7575
this.ctx.scale(1, this.aspectCorrection) ;
7676

7777
//#region MOUSE WHEEL
78+
const mouseWheelCaptured = (obj:LayoutElement, globalMouse:Vector2Like, delta:number) => obj.traverse( elem=>{
79+
80+
if( isMouseHandler(elem) && elem.intersects(globalMouse) )
81+
{
82+
elem.onMouseWheel( delta );
83+
return true;
84+
}
85+
86+
});
87+
7888
canvas.addEventListener("wheel", ev=>{
7989

8090
const cursor = this.getMousePos( ev );
8191
const pos = this.getCanvasMousePosition(cursor );
8292

93+
if( this.overlay )
94+
{
95+
if( mouseWheelCaptured(this.overlay.overlayBody, cursor, ev.deltaY) )
96+
return;
97+
}
98+
8399
if( this.eventsHandler )
84100
{
85101
this.eventsHandler.onMouseWheel( ev.deltaY )
@@ -90,16 +106,17 @@ export class Editor {
90106
// check if some prop wants to handle the wheel...
91107
//
92108
for (const obj of this.objs) {
93-
if( obj.traverse( elem=>{
94-
95-
if( isMouseHandler(elem) && elem.intersects(cursor) )
96-
{
97-
elem.onMouseWheel( ev.deltaY );
98-
return true;
99-
}
100-
101-
}))
102-
return;
109+
if( mouseWheelCaptured(obj, cursor, ev.deltaY) )
110+
return;
111+
// if( obj.traverse( elem=>{
112+
// if( isMouseHandler(elem) && elem.intersects(cursor) )
113+
// {
114+
// elem.onMouseWheel( ev.deltaY );
115+
// return true;
116+
// }
117+
118+
// }))
119+
// return;
103120
}
104121

105122
//
@@ -122,8 +139,8 @@ export class Editor {
122139
const mousePos = this.getMousePos( event);
123140
const canvasPos = this.getCanvasMousePosition(mousePos);
124141

125-
const sx = ( mousePos.x - this.mouse.x ) / scale;
126-
const sy = ( mousePos.y - this.mouse.y ) / scale / this.aspectCorrection;
142+
let sx = ( mousePos.x - this.mouse.x ) ;
143+
let sy = ( mousePos.y - this.mouse.y ) ;
127144

128145
this.mouse = mousePos;
129146

@@ -132,6 +149,9 @@ export class Editor {
132149
this.eventsHandler.onMouseMove( sx, sy );
133150
return;
134151
}
152+
153+
sx /= scale;
154+
sy = sy / scale / this.aspectCorrection;
135155

136156
if( this.mouseDrag )
137157
{

src/colors/Theme.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,15 @@ export class Theme {
2323
readonly vec3 : FillStyle = "#c7c729";
2424
readonly vec1 : FillStyle = "#cccccc";
2525
readonly vec2 : FillStyle = "#6363c7";
26+
readonly materialOutputSocketColor:FillStyle = "#bf4a06";
2627

2728
readonly groupTexture : FillStyle = "#79461d";
2829
readonly groupVector : FillStyle = "#3c3c83";
2930
readonly groupMath : FillStyle = "#246283";
3031
readonly groupInput : FillStyle = "#83314a";
3132
readonly groupOutput : FillStyle = "#3c1d26";
3233
readonly groupAttribute:FillStyle = "#83314a";
34+
readonly groupShader:FillStyle = "#2b652b";
3335

3436
readonly barBgColor : FillStyle = "#545454";
3537
readonly barFillColor : FillStyle = "#4772b3";

src/components/Button.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ export class Button extends InteractiveLayoutElement
55
{
66
private xpadding = 5;
77

8-
constructor( protected label:string , protected onClick?:VoidFunction )
8+
constructor( protected _label:string , protected onClick?:VoidFunction )
99
{
1010
super();
1111
}
@@ -32,4 +32,12 @@ export class Button extends InteractiveLayoutElement
3232
override onMouseDown(cursorX: number, cursorY: number): void {
3333
this.onClick?.();
3434
}
35+
36+
get label() {
37+
return this._label;
38+
}
39+
40+
set label( newLabel:string ) {
41+
this._label = newLabel;
42+
}
3543
}

src/components/ColorPicker.ts

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
import { Color, HSL } from "three";
2+
import { InteractiveLayoutElement } from "../layout/InteractiveLayoutElement";
3+
import { IOverlayRenderer } from "../layout/IOverlayRenderer";
4+
import { Column, Layout, Row } from "../layout/Layout";
5+
import { LayoutElement } from "../layout/LayoutElement";
6+
import { Node } from "../nodes/Node";
7+
import { ColorWheel } from "./ColorWheel";
8+
import { HeaderElement } from "./Header";
9+
import { ColorSwatch } from "./ColorSwatch";
10+
import { DraggableValue } from "./DraggableValue";
11+
import { Theme } from "../colors/Theme";
12+
import { TextLabel } from "./TextLabel";
13+
import { Button } from "./Button";
14+
15+
export class ColorPicker extends InteractiveLayoutElement implements IOverlayRenderer {
16+
17+
private myTransform!:DOMMatrix;
18+
protected overlay:Layout;
19+
protected _color = new Color() ;
20+
21+
protected _opacity = 1;
22+
protected _saturation = 1;
23+
24+
protected colorWheel:ColorWheel;
25+
protected colorSwatch:ColorSwatch;
26+
27+
protected alphaSlider:DraggableValue;
28+
protected RGBSliders:DraggableValue[];
29+
protected HSLSliders:DraggableValue[];
30+
protected hexBtn:Button;
31+
private _emitInterval:number=0;
32+
33+
private hsl:HSL = {h:0, s:0, l:0};
34+
35+
constructor( private onColorChange?:(color:Color, opacity:number)=>void ) {
36+
super();
37+
this.backgroundColor = "black";
38+
39+
this.colorWheel = new ColorWheel(this.onColorWheel.bind(this));
40+
this.colorSwatch = new ColorSwatch();
41+
this.alphaSlider = new DraggableValue("Alpha", true, 0, 1, 0.01, this.onAlphaSlider.bind(this));
42+
43+
this.RGBSliders = ["R","G","B"].map((name,i)=>new DraggableValue(name, true, 0.001, 1, 0.01, this.onRGBSlider.bind(this, i)))
44+
this.HSLSliders = ["H","S","L"].map((name,i)=>new DraggableValue(name, true, 0.001, 1, 0.01, this.onHSLSlider.bind(this, i)))
45+
46+
this.hexBtn = new Button("...", this.manuallySetHex.bind(this));
47+
48+
this.overlay = new Column([
49+
50+
new Row([
51+
52+
new Column([
53+
this.colorSwatch,
54+
this.colorWheel
55+
],{
56+
align:"stretch"
57+
}),
58+
59+
new Column([
60+
this.alphaSlider,
61+
...this.RGBSliders,
62+
...this.HSLSliders
63+
],{
64+
justify:"space-around",
65+
align:"stretch",
66+
width: 100,
67+
gap:1,
68+
lineHeight: Theme.config.nodeRowHeight * 0.7,
69+
})
70+
71+
72+
], {
73+
align:"stretch"
74+
}),
75+
76+
this.hexBtn
77+
]);
78+
79+
this.overlay.boxShadowLevel = 8
80+
this.overlay.backgroundColor = "#111"
81+
this.overlay.parent = this;
82+
this.overlay.xPadding = 2
83+
//
84+
// initial values...
85+
//
86+
this.opacity = 1;
87+
this.saturation = 1;
88+
this.color = new Color("black");
89+
}
90+
91+
get opacity() { return this._opacity; }
92+
set opacity( newOpacity:number )
93+
{
94+
this._opacity = newOpacity;
95+
this.colorSwatch.opacity = newOpacity;
96+
this.alphaSlider.value = newOpacity;
97+
this.emit();
98+
}
99+
100+
get saturation() { return this._saturation; }
101+
set saturation( newSaturation:number )
102+
{
103+
this._saturation = newSaturation;
104+
this.colorWheel.saturation = newSaturation;
105+
this.HSLSliders[1].value = newSaturation;
106+
this.emit();
107+
}
108+
109+
get color() { return this._color }
110+
protected set color( newColor:Color )
111+
{
112+
const c = this._color;
113+
114+
c.copy( newColor );
115+
c.getHSL( this.hsl );
116+
117+
this.hsl.s = this._saturation;
118+
c.setHSL(this.hsl.h, this.hsl.s, this.hsl.l);
119+
120+
this._color = c;
121+
122+
this.colorWheel.color = this._color;
123+
this.colorSwatch.color = this._color;
124+
125+
const rgb = [c.r, c.g, c.b];
126+
this.RGBSliders.forEach( (slider,i)=>slider.value = rgb[i]);
127+
128+
const hsl:number[] = Object.values( c.getHSL( this.hsl ) );
129+
130+
this.HSLSliders.forEach( (slider, i)=>slider.value=hsl[i])
131+
132+
this.hexBtn.label = this._color.getHexString();
133+
this.emit();
134+
}
135+
136+
protected override renderContents(ctx: CanvasRenderingContext2D, maxWidth: number, maxHeight: number): void {
137+
this.myTransform = ctx.getTransform();
138+
this.colorSwatch.render(ctx, maxWidth, maxHeight);
139+
}
140+
141+
renderOverlay(ctx: CanvasRenderingContext2D): void {
142+
ctx.setTransform( this.myTransform );
143+
this.overlay.render( ctx, this.overlay.width(ctx), this.overlay.height(ctx) );
144+
}
145+
146+
get overlayBody(): LayoutElement {
147+
return this.overlay;
148+
}
149+
150+
override width(ctx: CanvasRenderingContext2D): number {
151+
return 50
152+
}
153+
154+
override onMouseDown(cursorX: number, cursorY: number): void {
155+
156+
(this.root as Node).editor.overlay = this;
157+
}
158+
159+
protected onColorWheel( color:Color ) {
160+
161+
this.color = color;
162+
}
163+
164+
protected onAlphaSlider( newAlpha:number ) {
165+
this.opacity = newAlpha;
166+
}
167+
168+
protected onRGBSlider( i:number, newValue:number ) {
169+
170+
const c = this._color;
171+
const rgb = [c.r, c.g, c.b];
172+
173+
rgb[i] = newValue;
174+
175+
this.color = c.clone().setRGB(rgb[0], rgb[1], rgb[2]);
176+
}
177+
178+
protected onHSLSlider( i:number, newValue:number ) {
179+
180+
const c = this._color;
181+
const hsl:number[] = Object.values( c.getHSL( this.hsl ) );
182+
183+
hsl[i] = newValue;
184+
185+
if( i==1 ) {
186+
this.saturation = newValue;
187+
}
188+
189+
this.color = c.clone().setHSL(hsl[0], this._saturation, hsl[2]);
190+
}
191+
192+
protected manuallySetHex() {
193+
const val = prompt("Type the HEX value...", this._color.getHexString() );
194+
if( val )
195+
{
196+
this.color = this._color.clone().setStyle( "#"+val );
197+
}
198+
}
199+
200+
private emit() {
201+
clearInterval(this._emitInterval);
202+
this._emitInterval = setTimeout(()=>{
203+
this.onColorChange?.( this.color, this.opacity );
204+
}, 0);
205+
}
206+
}

src/components/ColorSwatch.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import { Color } from "three";
2+
import { LayoutElement } from "../layout/LayoutElement";
3+
import { createCheckerPattern } from "../util/createCheckerPattern";
4+
5+
export class ColorSwatch extends LayoutElement {
6+
7+
private _opacity = 1;
8+
private _color:Color = new Color();
9+
private checkerPattern = createCheckerPattern(5,5, "grey", "lightgrey");
10+
11+
set opacity( newOpacity:number ) {
12+
this._opacity = newOpacity;
13+
}
14+
15+
set color( newColor:Color ) {
16+
this._color.copy(newColor);
17+
}
18+
19+
protected override renderContents(ctx: CanvasRenderingContext2D, maxWidth: number, maxHeight: number): void {
20+
ctx.fillStyle = this._color.getStyle()
21+
ctx.fillRect(0,0,maxWidth/2, maxHeight);
22+
23+
ctx.fillStyle = this.checkerPattern;
24+
ctx.fillRect(maxWidth/2,0,maxWidth/2, maxHeight);
25+
26+
ctx.fillStyle = this._color.getStyle()
27+
ctx.globalAlpha = this._opacity;
28+
ctx.fillRect(maxWidth/2,0,maxWidth/2, maxHeight);
29+
ctx.globalAlpha = 1;
30+
}
31+
}

0 commit comments

Comments
 (0)