Selection tool basics WIP

This commit is contained in:
JannisX11 2023-10-22 02:45:47 +02:00
parent db83bdfc8e
commit 635ee169cd
8 changed files with 480 additions and 124 deletions

View File

@ -2019,6 +2019,38 @@ span.controller_state_section_info {
font-size: 16px;
padding-top: 2px;
}
#uv_selection_outline {
--color-selection-outline-a: black;
--color-selection-outline-b: white;
position: absolute;
pointer-events: none;
width: calc(100% + 2px);
height: calc(100% + 2px);
top: -1px;
left: -1px;
object-fit: cover;
object-position: 0 0;
}
#uv_selection_outline path {
fill: none;
stroke-width: 2px;
stroke: var(--color-selection-outline-a);
}
#uv_selection_outline path.dash_overlay {
stroke: var(--color-selection-outline-b);
stroke-dasharray: 6px 4px;
stroke-dashoffset: 0;
animation: selection-outline-shift 700ms linear infinite;
animation-timing-function: steps(3, end);
}
@keyframes selection-outline-shift {
0% {
stroke-dashoffset: 0;
}
100% {
stroke-dashoffset: -10px;
}
}
.face_properties_toggle {
width: 32px;

View File

@ -374,13 +374,13 @@ class Action extends BarItem {
return clone;
}
setIcon(icon) {
var scope = this;
this.icon = icon
this.icon_node = Blockbench.getIconNode(this.icon)
$(this.menu_node).find('.icon').replaceWith(this.icon_node)
this.nodes.forEach(function(n) {
$(n).find('.icon').replaceWith($(scope.icon_node).clone())
this.nodes.forEach(n => {
let old_icon = n.querySelector('.icon:not(.action_more_options)');
old_icon.replaceWith(this.icon_node.cloneNode(true));
})
}
setName(name) {
@ -2282,7 +2282,7 @@ const BARS = {
'color_picker',
'draw_shape_tool',
'gradient_tool',
'copy_paste_tool'
'selection_tool',
],
vertical: Blockbench.isMobile == true,
default_place: true
@ -2385,6 +2385,7 @@ const BARS = {
'copy_brush_mode',
'draw_shape_type',
'copy_paste_tool_mode',
'selection_tool_operation_mode',
'_',
'slider_brush_size',
'slider_brush_opacity',

View File

@ -623,9 +623,14 @@ window.addEventListener('blur', event => {
delete Toolbox.original;
}
}
let changed = Pressing.shift || Pressing.alt || Pressing.ctrl;
let before = changed && {shift: Pressing.shift, alt: Pressing.alt, ctrl: Pressing.ctrl};
Pressing.shift = false;
Pressing.alt = false;
Pressing.ctrl = false;
if (changed) {
Blockbench.dispatchEvent('update_pressed_modifier_keys', {before, now: Pressing});
}
})
window.addEventListener('focus', event => {
@ -660,9 +665,17 @@ function getFocusedTextInput() {
addEventListeners(document, 'keydown mousedown', function(e) {
if (Keybinds.recording || e.which < 4) return;
//Shift
let modifiers_changed = Pressing.shift != e.shiftKey || Pressing.alt != e.altKey || Pressing.ctrl != e.ctrlKey;
let before = modifiers_changed && {shift: Pressing.shift, alt: Pressing.alt, ctrl: Pressing.ctrl};
Pressing.shift = e.shiftKey;
Pressing.alt = e.altKey;
Pressing.ctrl = e.ctrlKey;
if (modifiers_changed) {
Blockbench.dispatchEvent('update_pressed_modifier_keys', {before, now: Pressing});
}
if (e.which === 16) {
showShiftTooltip()
}
@ -863,7 +876,12 @@ $(document).keyup(function(e) {
Toolbox.original.select()
delete Toolbox.original;
}
let changed = Pressing.shift || Pressing.alt || Pressing.ctrl;
let before = changed && {shift: Pressing.shift, alt: Pressing.alt, ctrl: Pressing.ctrl};
Pressing.shift = false;
Pressing.alt = false;
Pressing.ctrl = false;
if (changed) {
Blockbench.dispatchEvent('update_pressed_modifier_keys', {before, now: Pressing});
}
})

View File

@ -215,8 +215,11 @@ function updateSelection(options = {}) {
function selectAll() {
if (Modes.animate) {
selectAllKeyframes()
} else if (Prop.active_panel == 'uv') {
} else if (Prop.active_panel == 'uv' && Modes.edit) {
UVEditor.selectAll()
} else if (Prop.active_panel == 'uv' && Modes.paint && Texture.selected) {
Texture.selected.texture_selection.setOverride(Texture.selected.texture_selection.override == true ? false : true);
UVEditor.updateSelectionOutline();
} else if (Modes.edit && Mesh.selected.length && Mesh.selected.length === Outliner.selected.length && BarItems.selection_mode.value !== 'object') {
let selection_mode = BarItems.selection_mode.value;

View File

@ -1299,9 +1299,13 @@ BARS.defineActions(function() {
click() {
if (Modes.animate) {
unselectAllKeyframes()
} else if (Prop.active_panel == 'uv') {
} else if (Prop.active_panel == 'uv' && Modes.edit) {
this.vue.selected_faces.empty();
UVEditor.displayTools();
} else if (Prop.active_panel == 'uv' && Modes.paint) {
Texture.selected.texture_selection.setOverride(false);
UVEditor.updateSelectionOutline();
} else if (Modes.edit && Mesh.selected.length && Mesh.selected.length === Outliner.selected.length && BarItems.selection_mode.value !== 'object') {
Mesh.selected.forEach(mesh => {

View File

@ -1636,6 +1636,91 @@ const Painter = {
]
}
class IntMatrix {
constructor(width = 16, height = 16) {
this.width = width;
this.height = height;
this.array = null;
this.override = false;
}
/**
* The array does not exist by default to save memory, this activates it.
*/
activate() {
this.array = new Int8Array(this.width * this.height);
}
/**
* Get the value at the specified pixel
* @param {*} x
* @param {*} y
* @returns
*/
get(x, y, simple_access) {
if (this.override !== null) {
return this.override
} else {
if (x < 0 || x >= this.width || y < 0 || y >= this.height) return 0;
return this.array[y * this.width + x] || 0;
}
}
/**
* Set the value at a specified pixel
* @param {number} x
* @param {number} y
* @param {number} value
*/
set(x, y, value) {
if (this.override !== null) {
if (!this.array) this.activate();
if (this.override == true) {
this.array.fill(1);
}
this.override = null;
}
this.array[y * this.width + x] = value;
}
/**
* If there was a selection, whether override or not, clear it
*/
clear() {
if (this.override == true) this.override = false;
if (this.array) this.array.fill(0);
}
/**
* Change override mode
* @param {true|false|null} value
* @returns
*/
setOverride(value) {
if (value === this.override) return;
this.override = value;
if (value === null) {
if (!this.array) {
this.activate();
} else {
this.array.fill(0);
}
} else {
delete this.array;
}
}
/**
* Change the size of the matrix. Unless using overrides, the selection gets lost.
* @param {number} width
* @param {number} height
* @returns {boolean} Whether the size had to be changed
*/
changeSize(width, height) {
if (width == this.width && height == this.height) return false;
this.width = width;
this.height = height;
if (this.array) {
this.array = new Int8Array(this.width * this.height);
}
return true;
}
}
BARS.defineActions(function() {
new Tool('pan_tool', {
@ -1993,7 +2078,7 @@ BARS.defineActions(function() {
Interface.removeSuggestedModifierKey('shift', 'modifier_actions.snap_direction');
}
})
new Tool('copy_paste_tool', {
/*new Tool('copy_paste_tool', {
icon: 'fa-vector-square',
category: 'tools',
toolbar: 'brush',
@ -2016,7 +2101,52 @@ BARS.defineActions(function() {
open_interface.confirm()
}
}
})*/
let selection_tool = new Tool('selection_tool', {
icon: 'select',
category: 'tools',
toolbar: 'brush',
cursor: 'crosshair',
selectFace: true,
transformerMode: 'hidden',
paintTool: true,
allowed_view_modes: ['textured'],
modes: ['paint'],
condition: {modes: ['paint']},
side_menu: new Menu('selection_tool', () => {
let modes = {
rectangle: {icon: 'select'},
ellipse: {icon: 'lasso_select'},
lasso: {icon: 'fa-draw-polygon'},
wand: {icon: 'fa-magic'},
same_color: {icon: 'format_paint'},
};
let entries = [];
for (let id in modes) {
let entry = {
id,
name: id,
icon: modes[id].icon,
click() {
selection_tool.setIcon(modes[id].icon);
selection_tool.mode = id;
selection_tool.select();
}
}
entries.push(entry);
}
return entries;
}),
onCanvasClick(data) {
if (data && data.element) {
Blockbench.showQuickMessage('message.copy_paste_tool_viewport')
}
},
onSelect() {
}
})
selection_tool.mode = 'rectangle';
new BarSelect('brush_shape', {
category: 'paint',
@ -2094,6 +2224,28 @@ BARS.defineActions(function() {
move: true,
}
})
new BarSelect('selection_tool_operation_mode', {
category: 'paint',
condition: {tools: ['selection_tool']},
icon_mode: true,
options: {
create: {name: true, icon: 'shadow'},
add: {name: true, icon: 'shadow_add'},
subtract: {name: true, icon: 'shadow_minus'},
intersect: {name: true, icon: 'join_inner'},
}
})
Blockbench.on('update_pressed_modifier_keys', ({before, now}) => {
let tool = BarItems.selection_tool_operation_mode;
if (!Condition(tool.condition)) return;
if (now.shift) {
tool.set('add');
} else if (now.ctrl) {
tool.set('subtract');
} else if (before.ctrl || before.shift) {
tool.set('create');
}
});
StateMemory.init('mirror_painting_options', 'object');
Painter.mirror_painting_options = StateMemory.mirror_painting_options;

View File

@ -26,6 +26,7 @@ class Texture {
this.layers = [];
this.layers_enabled = false;
this.selected_layer = null;
this.texture_selection = new IntMatrix(0, 0);
this.internal = !isApp;
this.uuid = uuid || guid()
@ -198,6 +199,7 @@ class Texture {
let dimensions_changed = scope.width !== img.naturalWidth || scope.height !== img.naturalHeight;
scope.width = img.naturalWidth;
scope.height = img.naturalHeight;
scope.texture_selection.changeSize(scope.width, scope.height);
if (img.naturalWidth > 16384 || img.naturalHeight > 16384) {
scope.error = 2;
}

View File

@ -36,7 +36,7 @@ const UVEditor = {
];
}
if (Toolbox.selected.id === 'copy_paste_tool') {
if (Toolbox.selected.id === 'copy_paste_tool' || Toolbox.selected.id === 'selection_tool') {
if (settings.nearest_rectangle_select.value) {
result.x = Math.round(mouse_coords[0]/pixel_size*1);
result.y = Math.round(mouse_coords[1]/pixel_size*1);
@ -65,13 +65,13 @@ const UVEditor = {
if (texture) {
var coords = this.getBrushCoordinates(event, texture)
if (Toolbox.selected.id !== 'copy_paste_tool') {
Painter.startPaintTool(texture, coords.x, coords.y, undefined, event)
if (Toolbox.selected.id == 'selection_tool') {
this.vue.startTextureSelection(coords.x, coords.y, event);
} else {
this.startSelection(coords.x, coords.y, event)
Painter.startPaintTool(texture, coords.x, coords.y, undefined, event);
}
}
if (Toolbox.selected.id !== 'color_picker' && Toolbox.selected.id !== 'copy_paste_tool' && texture) {
if (Toolbox.selected.id !== 'color_picker' && Toolbox.selected.id !== 'copy_paste_tool' && Toolbox.selected.id !== 'selection_tool' && texture) {
addEventListeners(this.vue.$refs.frame, 'mousemove touchmove', UVEditor.movePaintTool, false );
addEventListeners(document, 'mouseup touchend', UVEditor.stopBrush, false );
}
@ -111,121 +111,68 @@ const UVEditor = {
UVEditor.stopSelection()
}
},
// Copy Paste Tool
startSelection(x, y, event) {
if (Painter.selection.overlay && event.target && event.target.id === 'uv_frame' && !event.altKey) {
if (open_interface) {
open_interface.confirm()
} else {
this.removePastingOverlay()
selection_outline_lines: [],
updateSelectionOutline(recalculate_lines = true) {
let {texture} = this.vue;
if (!texture) {
this.vue.selection_outline = '';
return;
}
if (texture.texture_selection.override == false) {
this.vue.selection_outline = '';
return;
}
let size = UVEditor.getPixelSize();
let width = this.vue.texture.width;
let height = this.vue.texture.display_height;
if (texture.texture_selection.override == true) {
this.vue.selection_outline = `M1 1 L${width * size + 1} 1 L${width * size + 1} ${height * size + 1} L1 ${height * size + 1} L1 1`;
return;
}
let lines = UVEditor.selection_outline_lines;
if (recalculate_lines) {
lines.empty();
let matrix = texture.texture_selection;
// =
for (let y = 0; y <= height; y++) {
let pre = null;
for (let x = 0; x <= width; x++) {
let a = matrix.get(x, y-1);
let b = matrix.get(x, y);
let line = a != b;
if (pre !== line) {
if (pre !== null || line) {
lines.push([!!line, x, y]);
}
pre = line;
}
}
}
// ||
for (let x = 0; x <= width; x++) {
let pre = null;
for (let y = 0; y <= height; y++) {
let a = matrix.get(x-1, y);
let b = matrix.get(x, y);
let line = a != b;
if (pre !== line) {
if (pre !== null || line) {
lines.push([!!line, x, y]);
}
pre = line;
}
}
}
}
delete Painter.selection.calcrect;
let viewport = UVEditor.vue.$refs.viewport;
if (!Painter.selection.overlay) {
$(this.vue.$refs.frame).find('#texture_selection_rect').detach();
let rect = document.createElement('div');
rect.style.visibility = 'hidden';
rect.id = 'texture_selection_rect';
this.vue.$refs.frame.append(rect)
Painter.selection.rect = rect;
Painter.selection.start_x = Math.clamp(x, 0, UVEditor.texture ? UVEditor.texture.width : Project.texture_width);
Painter.selection.start_y = Math.clamp(y, 0, UVEditor.texture ? UVEditor.texture.height : Project.texture_height);
UVEditor.vue.copy_overlay.width = 0;
UVEditor.vue.copy_overlay.height = 0;
} else {
Painter.selection.start_x = Painter.selection.x;
Painter.selection.start_y = Painter.selection.y;
Painter.selection.start_scroll_x = viewport.scrollLeft;
Painter.selection.start_scroll_y = viewport.scrollTop;
Painter.selection.start_event = event;
let outline = '';
for (let line of lines) {
outline += `${outline ? '' : ' '}${line[0] ? 'M' : 'L'}${line[1] * size + 1} ${line[2] * size + 1}`;
}
function drag(e1) {
let texture = UVEditor.texture;
var {x, y} = UVEditor.getBrushCoordinates(e1, texture);
if (texture.img.naturalWidth + texture.img.naturalHeight == 0) return;
UVEditor.dragSelection(x, y, e1);
}
function stop() {
removeEventListeners(document, 'pointermove', drag);
removeEventListeners(document, 'pointerup', stop);
UVEditor.stopSelection();
}
addEventListeners(document, 'pointermove', drag);
addEventListeners(document, 'pointerup', stop);
},
dragSelection(x, y, event) {
let m = UVEditor.inner_width / UVEditor.texture.width;
if (!Painter.selection.overlay) {
let {start_x, start_y} = Painter.selection;
if (!settings.nearest_rectangle_select.value) {
if (x >= Painter.selection.start_x) x++;
if (y >= Painter.selection.start_y) y++;
if (x < Painter.selection.start_x) start_x++;
if (y < Painter.selection.start_y) start_y++;
}
if (x === Painter.current.x && y === Painter.current.y) return;
Painter.current.x = x = Math.clamp(x, 0, UVEditor.texture.img.naturalWidth);
Painter.current.y = y = Math.clamp(y, 0, UVEditor.texture.img.naturalHeight);
let calcrect = getRectangle(start_x, start_y, x, y)
if (!calcrect.x && !calcrect.y) return;
UVEditor.vue.copy_overlay.state = 'select';
Painter.selection.calcrect = calcrect;
Painter.selection.x = calcrect.ax;
Painter.selection.y = calcrect.ay;
UVEditor.vue.copy_overlay.width = calcrect.x;
UVEditor.vue.copy_overlay.height = calcrect.y;
$(Painter.selection.rect)
.css('left', calcrect.ax*m + 'px')
.css('top', (calcrect.ay%UVEditor.texture.display_height)*m + 'px')
.css('width', calcrect.x *m + 'px')
.css('height', calcrect.y *m + 'px')
.css('visibility', 'visible')
} else if (UVEditor.texture && Painter.selection.canvas) {
let viewport = UVEditor.vue.$refs.viewport;
let move_offset_x = event.clientX - Painter.selection.start_event.clientX - Painter.selection.start_scroll_x + viewport.scrollLeft;
let move_offset_y = event.clientY - Painter.selection.start_event.clientY - Painter.selection.start_scroll_y + viewport.scrollTop;
Painter.selection.x = Painter.selection.start_x + Math.round((move_offset_x) / m);
Painter.selection.y = Painter.selection.start_y + Math.round((move_offset_y) / m);
Painter.selection.x = Math.clamp(Painter.selection.x, 1-Painter.selection.canvas.width, UVEditor.texture.width -1)
Painter.selection.y = Math.clamp(Painter.selection.y, 1-Painter.selection.canvas.height, UVEditor.texture.height-1)
UVEditor.updatePastingOverlay()
}
},
stopSelection() {
if (Painter.selection.rect) {
Painter.selection.rect.remove()
}
if (Painter.selection.overlay || !Painter.selection.calcrect) return;
UVEditor.vue.copy_overlay.state = 'off';
if (Painter.selection.calcrect.x == 0 || Painter.selection.calcrect.y == 0) return;
let calcrect = Painter.selection.calcrect;
var canvas = document.createElement('canvas')
var ctx = canvas.getContext('2d');
canvas.width = calcrect.x;
canvas.height = calcrect.y;
ctx.drawImage(UVEditor.vue.texture.img, -calcrect.ax, -calcrect.ay)
if (isApp) {
let image = nativeImage.createFromDataURL(canvas.toDataURL())
clipboard.writeImage(image)
}
Painter.selection.canvas = canvas;
Painter.selection.move_mode = BarItems.copy_paste_tool_mode.value == 'move';
if (Painter.selection.move_mode) {
UVEditor.texture.edit((canvas) => {
var ctx = canvas.getContext('2d');
ctx.clearRect(Painter.selection.x, Painter.selection.y, Painter.selection.canvas.width, Painter.selection.canvas.height);
}, {no_undo_finish: true});
}
UVEditor.addPastingOverlay();
this.vue.selection_outline = outline;
},
addPastingOverlay() {
if (Painter.selection.overlay) return;
@ -424,6 +371,7 @@ const UVEditor = {
this.vue.zoom = zoom;
Project.uv_viewport.zoom = this.zoom;
Vue.nextTick(() => {
UVEditor.updateSelectionOutline(false);
if (Painter.selection.overlay) UVEditor.updatePastingOverlay()
})
return this;
@ -2037,6 +1985,13 @@ Interface.definePanels(function() {
height: 0,
active: false
},
texture_selection_rect: {
pos_x: 0,
pos_y: 0,
width: 0,
height: 0,
active: false
},
copy_overlay,
uv_resolution: [16, 16],
@ -2044,6 +1999,7 @@ Interface.definePanels(function() {
all_elements: [],
selected_faces: [],
display_uv: 'selected_elements',
selection_outline: '',
face_names: {
north: tl('face.north'),
@ -2213,6 +2169,7 @@ Interface.definePanels(function() {
this.texture = 0;
}
// Display canvas while painting
UVEditor.updateSelectionOutline();
this.updateTextureCanvas();
},
updateTextureCanvas() {
@ -3261,6 +3218,176 @@ Interface.definePanels(function() {
return style ? `${margin[1]}px ${margin[0]}px` : margin;
},
startTextureSelection(x, y, event) {
let texture = UVEditor.texture;
if (!texture) return;
if (texture.img.naturalWidth + texture.img.naturalHeight == 0) return;
let clicked_val = texture.texture_selection.get(Math.floor(x), Math.floor(y));
let selection_mode = BarItems.selection_tool.mode;
let op_mode = BarItems.selection_tool_operation_mode.value;
let selection_rect = this.texture_selection_rect;
let start_x, start_y, calcrect;
let create_selection = !(op_mode == 'create' && clicked_val);
/*if (op_mode == 'create' && clicked_val) {
if (open_interface) {
open_interface.confirm()
} else {
UVEditor.removePastingOverlay()
}
}*/
let viewport = this.$refs.viewport;
if (create_selection) {
//$(this.$refs.frame).find('#texture_selection_rect').detach();
//let rect = document.createElement('div');
//rect.style.visibility = 'hidden';
//rect.id = 'texture_selection_rect';
//this.$refs.frame.append(rect)
start_x = Math.clamp(x, 0, UVEditor.texture ? UVEditor.texture.width : Project.texture_width);
start_y = Math.clamp(y, 0, UVEditor.texture ? UVEditor.texture.height : Project.texture_height);
} else {
//Painter.selection.start_x = Painter.selection.x;
//Painter.selection.start_y = Painter.selection.y;
//Painter.selection.start_scroll_x = viewport.scrollLeft;
//Painter.selection.start_scroll_y = viewport.scrollTop;
//Painter.selection.start_event = event;
}
let last_x, last_y;
function drag(e1) {
var {x, y} = UVEditor.getBrushCoordinates(e1, texture);
if (last_x == x && last_y == y) return;
last_x = x, last_y = y;
/*let rect = getRectangle(
event.offsetX / scope.inner_width * scope.uv_resolution[0],
event.offsetY / scope.inner_height * scope.uv_resolution[1],
(event.offsetX - event.clientX + e1.clientX) / scope.inner_width * scope.uv_resolution[0],
(event.offsetY - event.clientY + e1.clientY) / scope.inner_height * scope.uv_resolution[1],
)
selection_rect.pos_x = rect.ax;
selection_rect.pos_y = rect.ay;
selection_rect.width = rect.x;
selection_rect.height = rect.y;*/
if (create_selection) {
let start_x_here = start_x;
let start_y_here = start_y;
if (!settings.nearest_rectangle_select.value) {
if (x >= start_x) x++;
if (y >= start_y) y++;
if (x < start_x) start_x_here++;
if (y < start_y) start_y_here++;
}
if (x === Painter.current.x && y === Painter.current.y) return;
Painter.current.x = x = Math.clamp(x, 0, UVEditor.texture.img.naturalWidth);
Painter.current.y = y = Math.clamp(y, 0, UVEditor.texture.img.naturalHeight);
calcrect = getRectangle(start_x_here, start_y_here, x, y);
if (!calcrect.x && !calcrect.y) return;
//UVEditor.vue.copy_overlay.state = 'select';
//Painter.selection.calcrect = calcrect;
//Painter.selection.x = calcrect.ax;
//Painter.selection.y = calcrect.ay;
//UVEditor.vue.copy_overlay.width = calcrect.x;
//UVEditor.vue.copy_overlay.height = calcrect.y;
selection_rect.active = true;
selection_rect.pos_x = calcrect.ax;
selection_rect.pos_y = calcrect.ay;
selection_rect.width = calcrect.x;
selection_rect.height = calcrect.y;
} else {
//let viewport = UVEditor.vue.$refs.viewport;
//let move_offset_x = e1.clientX - Painter.selection.start_event.clientX - Painter.selection.start_scroll_x + viewport.scrollLeft;
//let move_offset_y = e1.clientY - Painter.selection.start_event.clientY - Painter.selection.start_scroll_y + viewport.scrollTop;
//Painter.selection.x = Painter.selection.start_x + Math.round((move_offset_x) / m);
//Painter.selection.y = Painter.selection.start_y + Math.round((move_offset_y) / m);
//Painter.selection.x = Math.clamp(Painter.selection.x, 1-Painter.selection.canvas.width, UVEditor.texture.width -1)
//Painter.selection.y = Math.clamp(Painter.selection.y, 1-Painter.selection.canvas.height, UVEditor.texture.height-1)
//UVEditor.updatePastingOverlay()
}
}
function stop() {
removeEventListeners(document, 'pointermove', drag);
removeEventListeners(document, 'pointerup', stop);
selection_rect.active = false;
if (create_selection) {
if (selection_rect.width == 0 || selection_rect.height == 0) return;
if (op_mode == 'create') {
texture.texture_selection.clear();
}
if (selection_mode == 'rectangle') {
for (let x = calcrect.ax; x < calcrect.bx; x++) {
for (let y = calcrect.ay; y < calcrect.by; y++) {
switch (op_mode) {
case 'create': case 'add': {
texture.texture_selection.set(x, y, 1);
break;
}
case 'subtract': {
texture.texture_selection.set(x, y, 0);
break;
}
}
}
}
}
if (selection_mode == 'ellipse') {
let center_x = calcrect.ax + calcrect.x/2;
let center_y = calcrect.ay + calcrect.y/2;
for (let x = calcrect.ax; x < calcrect.bx; x++) {
for (let y = calcrect.ay; y < calcrect.by; y++) {
let distance = Math.sqrt(Math.pow((center_x - x - 0.5) / calcrect.x, 2) + Math.pow((center_y - y - 0.5) / calcrect.y, 2));
if (distance > 0.5) continue;
switch (op_mode) {
case 'create': case 'add': {
texture.texture_selection.set(x, y, 1);
break;
}
case 'subtract': {
texture.texture_selection.set(x, y, 0);
break;
}
}
}
}
}
UVEditor.updateSelectionOutline();
}
/*let calcrect = Painter.selection.calcrect;
var canvas = document.createElement('canvas')
var ctx = canvas.getContext('2d');
canvas.width = calcrect.x;
canvas.height = calcrect.y;
ctx.drawImage(UVEditor.vue.texture.img, -calcrect.ax, -calcrect.ay)
/*if (isApp) {
let image = nativeImage.createFromDataURL(canvas.toDataURL())
clipboard.writeImage(image)
}*/
/*Painter.selection.canvas = canvas;
Painter.selection.move_mode = BarItems.copy_paste_tool_mode.value == 'move';
if (Painter.selection.move_mode) {
UVEditor.texture.edit((canvas) => {
var ctx = canvas.getContext('2d');
ctx.clearRect(Painter.selection.x, Painter.selection.y, Painter.selection.canvas.width, Painter.selection.canvas.height);
}, {no_undo_finish: true});
}
//UVEditor.addPastingOverlay();*/
}
addEventListeners(document, 'pointermove', drag);
addEventListeners(document, 'pointerup', stop);
},
checkFormat(values) {
for (let key in values) {
if (Format[key] != values[key]) return false;
@ -3559,6 +3686,7 @@ Interface.definePanels(function() {
}">
</div>
<div v-if="helper_lines.x >= 0" class="uv_helper_line_x" :style="{left: toPixels(helper_lines.x)}"></div>
<div v-if="helper_lines.y >= 0" class="uv_helper_line_y" :style="{top: toPixels(helper_lines.y)}"></div>
@ -3573,10 +3701,26 @@ Interface.definePanels(function() {
>
<div ref="texture_canvas_wrapper" id="texture_canvas_wrapper" v-if="texture && texture.error != 1 && texture.display_canvas"></div>
<img style="object-fit: fill; opacity: 0.02; mix-blend-mode: screen;" v-if="texture == 0 && !box_uv" src="./assets/missing_blend.png">
<svg id="uv_texture_grid" v-if="pixel_grid && mode == 'paint' && texture && texture.width">
<path :d="textureGrid" />
<path :d="textureGridBold" class="bold_grid" />
</svg>
<div id="texture_selection_rect"
v-if="texture_selection_rect.active"
:style="{
left: toPixels(texture_selection_rect.pos_x),
top: toPixels(texture_selection_rect.pos_y),
width: toPixels(texture_selection_rect.width),
height: toPixels(texture_selection_rect.height),
}">
</div>
<svg id="uv_selection_outline">
<path :d="selection_outline" />
<path :d="selection_outline" class="dash_overlay" />
</svg>
</div>
<div class="uv_transparent_face" v-else-if="selected_faces.length">${tl('uv_editor.transparent_face')}</div>