Skip to content

Commit 4f075f2

Browse files
committed
multi color gradient by Z color mode
1 parent b534874 commit 4f075f2

5 files changed

Lines changed: 128 additions & 38 deletions

File tree

index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ <h1 class="welcome-title">Welcome to Speleo Studio</h1>
5757
</button>
5858
</div>
5959
</div>
60+
6061
<div id="fixed-size-editor" class="popup"> </div>
6162
<div id="resizable-editor" class="popup"></div>
6263
<div id="control-panel"></div>

src/config.js

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,13 @@ export const DEFAULT_OPTIONS = {
6464
},
6565
caveLines : {
6666
color : {
67-
start : '#00ff2a',
68-
end : '#0000ff',
69-
mode : 'gradientByZ'
67+
mode : 'gradientByZ',
68+
gradientColors : [
69+
{ depth: 0, color: '#e1ff00' }, // 0 = highest point
70+
{ depth: 30, color: '#c20534' },
71+
{ depth: 40, color: '#2de51f' },
72+
{ depth: 70, color: '#1c38a6' } // 100 = deepest point
73+
]
7074
}
7175
},
7276
sectionAttributes : {
@@ -439,7 +443,7 @@ export class ConfigManager {
439443

440444
for (const [key, value] of Object.entries(target)) {
441445
if (value && typeof value === 'object' && !Array.isArray(value)) {
442-
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
446+
if (!source[key] || (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key]))) {
443447
this.deepMerge(source[key], value);
444448
} else {
445449
source[key] = value;
@@ -457,11 +461,10 @@ export class ConfigManager {
457461
* Centralizes all the onChange logic from GUI controls
458462
*/
459463
export class ConfigChanges {
460-
constructor(watchedConfig, scene, materials, options) {
464+
constructor(watchedConfig, scene, materials) {
461465
this.watchedConfig = watchedConfig;
462466
this.scene = scene;
463467
this.materials = materials;
464-
this.options = options;
465468
}
466469

467470
/**
@@ -625,6 +628,12 @@ export class ConfigChanges {
625628
case 'scene.caveLines.color.mode':
626629
this.scene.changeCenterLineColorMode(newValue);
627630
break;
631+
case 'scene.caveLines.color.gradientColors':
632+
if (this.watchedConfig.scene.caveLines.color.mode === 'gradientByZ') {
633+
// Update the scene with new gradient colors
634+
this.scene.changeCenterLineColorMode('gradientByZ');
635+
}
636+
break;
628637
}
629638
}
630639

src/io/export.js

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -159,12 +159,16 @@ class Exporter {
159159
lines.push('Survey data');
160160
lines.push('From To Length Azimuth Vertical Label Left Right Up Down Note');
161161

162-
survey.shots.forEach((shot) => {
163-
lines.push(
164-
[shot.from, shot.to, shot.length, shot.azimuth, shot.clino, '', '0', '0', '0', '0', shot.comment].join('\t')
165-
);
166-
167-
});
162+
survey.shots
163+
.filter((sh) => sh.isCenter()) // do not save splays and auxiliary shots
164+
.forEach((shot) => {
165+
lines.push(
166+
[shot.from, shot.to, shot.length, shot.azimuth, shot.clino, '', '0', '0', '0', '0', shot.comment].join(
167+
'\t'
168+
)
169+
);
170+
171+
});
168172
lines.push('');
169173
});
170174
});

src/survey.js

Lines changed: 48 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -313,32 +313,35 @@ class SurveyHelper {
313313
c.surveys.forEach((s) => {
314314
sm.set(
315315
s.name,
316-
SurveyHelper.getColorGradientsByDepth(s, c.stations, diffZ, maxZ, clOptions.color.start, clOptions.color.end)
316+
SurveyHelper.getColorGradientsByDepthMultiColor(s, c.stations, diffZ, maxZ, clOptions.color.gradientColors)
317317
);
318+
318319
});
319320

320321
});
321322
return colorGradients;
322323
}
323324

324-
static getColorGradientsByDepth(survey, stations, diffZ, maxZ, startColorHex, endColorHex) {
325+
static getColorGradientsByDepthMultiColor(survey, stations, diffZ, maxZ, gradientColors) {
325326
const centerColors = [];
326327
const splayColors = [];
327328
const auxiliaryColors = [];
328329

329-
const startColor = new Color(startColorHex);
330-
const endColor = new Color(endColorHex);
330+
// Sort gradient colors by depth
331+
const sortedColors = [...gradientColors].sort((a, b) => a.depth - b.depth);
331332

332-
const colorDiff = endColor.sub(startColor);
333333
survey.validShots.forEach((sh) => {
334334
const fromStation = stations.get(survey.getFromStationName(sh));
335335
const toStation = stations.get(survey.getToStationName(sh));
336336

337337
if (fromStation !== undefined && toStation !== undefined) {
338-
const fromD = maxZ - fromStation.position.z;
339-
const toD = maxZ - toStation.position.z;
340-
const fc = startColor.add(colorDiff.mul(fromD / diffZ));
341-
const tc = startColor.add(colorDiff.mul(toD / diffZ));
338+
// Convert absolute Z coordinates to relative depth (0-100)
339+
const fromRelativeDepth = diffZ === 0 ? 0 : ((maxZ - fromStation.position.z) / diffZ) * 100;
340+
const toRelativeDepth = diffZ === 0 ? 0 : ((maxZ - toStation.position.z) / diffZ) * 100;
341+
342+
const fc = SurveyHelper.interpolateColorByDepth(fromRelativeDepth, sortedColors);
343+
const tc = SurveyHelper.interpolateColorByDepth(toRelativeDepth, sortedColors);
344+
342345
if (sh.type === ShotType.CENTER) {
343346
centerColors.push(fc.r, fc.g, fc.b, tc.r, tc.g, tc.b);
344347
} else if (sh.type === ShotType.SPLAY) {
@@ -350,6 +353,42 @@ class SurveyHelper {
350353
});
351354
return { center: centerColors, splays: splayColors, auxiliary: auxiliaryColors };
352355
}
356+
357+
// gradient colors must be sorted by depth
358+
static interpolateColorByDepth(relativeDepth, sortedColors) {
359+
if (sortedColors.length < 2) {
360+
throw new Error('At least 2 gradient colors are required');
361+
}
362+
// Find the two colors to interpolate between
363+
let lowerColor = sortedColors[0];
364+
let upperColor = sortedColors[sortedColors.length - 1];
365+
366+
for (let i = 0; i < sortedColors.length - 1; i++) {
367+
if (relativeDepth >= sortedColors[i].depth && relativeDepth <= sortedColors[i + 1].depth) {
368+
lowerColor = sortedColors[i];
369+
upperColor = sortedColors[i + 1];
370+
break;
371+
}
372+
}
373+
374+
// If depth is outside the range, clamp to the nearest color
375+
if (relativeDepth < lowerColor.depth) {
376+
return new Color(lowerColor.color);
377+
}
378+
if (relativeDepth > upperColor.depth) {
379+
return new Color(upperColor.color);
380+
}
381+
382+
// Interpolate between the two colors
383+
const range = upperColor.depth - lowerColor.depth;
384+
const factor = range === 0 ? 0 : (relativeDepth - lowerColor.depth) / range;
385+
386+
const startColor = new Color(lowerColor.color);
387+
const endColor = new Color(upperColor.color);
388+
const colorDiff = endColor.sub(startColor);
389+
390+
return startColor.add(colorDiff.mul(factor));
391+
}
353392
}
354393

355394
export { SurveyHelper };

src/ui/controls.js

Lines changed: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,13 @@ export class Controls {
2121
const gui = new GUI({ title: 'Control panel', container: this.element });
2222

2323
const centerLineParam = {
24-
'show center lines' : s.centerLines.segments.show,
25-
'line color' : s.centerLines.segments.color,
26-
'gradient start color' : s.caveLines.color.start,
27-
'gradient end color' : s.caveLines.color.end,
28-
width : s.centerLines.segments.width,
29-
opacity : s.centerLines.segments.opacity,
30-
'show station' : s.centerLines.spheres.show,
31-
'station color' : s.centerLines.spheres.color,
32-
'station size' : s.centerLines.spheres.radius
24+
'show center lines' : s.centerLines.segments.show,
25+
'line color' : s.centerLines.segments.color,
26+
width : s.centerLines.segments.width,
27+
opacity : s.centerLines.segments.opacity,
28+
'show station' : s.centerLines.spheres.show,
29+
'station color' : s.centerLines.spheres.color,
30+
'station size' : s.centerLines.spheres.radius
3331
};
3432

3533
const splayParam = {
@@ -66,6 +64,53 @@ export class Controls {
6664
DPI : this.options.screen.DPI
6765
};
6866

67+
// Multi-color gradient controls
68+
const gradientFolder = gui.addFolder('Color gradient');
69+
70+
// Add gradient color controls
71+
const addGradientColor = () => {
72+
const maxDepth = Math.max(...s.caveLines.color.gradientColors.map((gc) => gc.depth));
73+
const newColor = { depth: Math.min(maxDepth + 25, 100), color: '#ffffff' };
74+
s.caveLines.color.gradientColors = [...s.caveLines.color.gradientColors, newColor]; // trigger a change event
75+
this.reload();
76+
};
77+
78+
const removeGradientColor = (index) => {
79+
if (s.caveLines.color.gradientColors.length > 2) {
80+
s.caveLines.color.gradientColors.splice(index, 1);
81+
s.caveLines.color.gradientColors = [...s.caveLines.color.gradientColors]; // trigger a change event
82+
this.reload();
83+
}
84+
};
85+
86+
gradientFolder.add({ 'Add Color Stop': addGradientColor }, 'Add Color Stop');
87+
88+
// Create controls for each gradient color
89+
s.caveLines.color.gradientColors.forEach((gradientColor, index) => {
90+
const colorFolder = gradientFolder.addFolder(`Color Stop ${index + 1}`);
91+
92+
colorFolder
93+
.add(gradientColor, 'depth', 0, 100)
94+
.step(1)
95+
.name('Relative Depth')
96+
.onFinishChange(() => {
97+
// Sort gradient colors by depth
98+
s.caveLines.color.gradientColors.sort((a, b) => a.depth - b.depth);
99+
// Trigger scene update by changing the gradientColors array
100+
s.caveLines.color.gradientColors = [...s.caveLines.color.gradientColors];
101+
this.reload();
102+
});
103+
104+
colorFolder.addColor(gradientColor, 'color').onFinishChange(() => {
105+
// Trigger scene update by changing the gradientColors array
106+
s.caveLines.color.gradientColors = [...s.caveLines.color.gradientColors];
107+
});
108+
109+
if (s.caveLines.color.gradientColors.length > 2) {
110+
colorFolder.add({ Remove: () => removeGradientColor(index) }, 'Remove');
111+
}
112+
});
113+
69114
const centerLineFolder = gui.addFolder('Center lines');
70115

71116
centerLineFolder.add(centerLineParam, 'show center lines').onFinishChange(function (val) {
@@ -76,14 +121,6 @@ export class Controls {
76121
s.centerLines.segments.color = val;
77122
});
78123

79-
centerLineFolder.addColor(centerLineParam, 'gradient start color').onChange(function (val) {
80-
s.caveLines.color.start = val;
81-
});
82-
83-
centerLineFolder.addColor(centerLineParam, 'gradient end color').onChange(function (val) {
84-
s.caveLines.color.end = val;
85-
});
86-
87124
centerLineFolder
88125
.add(centerLineParam, 'width', 0.5, 8)
89126
.step(0.1)

0 commit comments

Comments
 (0)