Add loop edge selection

Merge "Add Mesh" and "Add Primitive
Close #1010 Remember "Add Primitive" dialog settings
Close #1038 Write popup-setting description below input instead on a button
Close #1035 by adding tool to filter menu
This commit is contained in:
JannisX11 2021-09-09 23:46:03 +02:00
parent c7a9cc943d
commit bcad73d4e4
6 changed files with 323 additions and 273 deletions

View File

@ -576,7 +576,6 @@ const MenuBar = {
'_',
'add_cube',
'add_mesh',
'add_primitive',
'add_group',
'add_locator',
'add_null_object',
@ -636,6 +635,7 @@ const MenuBar = {
})
new BarMenu('filter', [
'convert_to_mesh',
'remove_blank_faces',
])

View File

@ -136,7 +136,11 @@ class Setting {
label: this.name,
description: this.description,
type: this.type
}
},
description: this.description ? {
type: 'info',
text: this.description
} : undefined
},
onConfirm({input}) {
setting.set(input);

View File

@ -231,6 +231,7 @@ class ModelProject {
unselect() {
if (isApp) updateRecentProjectThumbnail();
this.thumbnail = Preview.selected.canvas.toDataURL();
Interface.tab_bar.last_opened_project = this.uuid;
this.selected = false;
Painter.current = {};
@ -416,6 +417,7 @@ onVueSetup(() => {
drag_position_index: null,
close_tab_label: tl('projects.close_tab'),
search_tabs_label: tl('generic.search'),
last_opened_project: '',
new_tab: {
name: tl('projects.new_tab'),
saved: true,
@ -424,7 +426,14 @@ onVueSetup(() => {
visible: true,
is_new_tab: true,
close: () => {
if (ModelProject.all.length) {
Interface.tab_bar.new_tab.visible = false;
let project = Project.all.find(project => project.uuid == Interface.tab_bar.last_opened_project) ||
Project.all.last();
if (project) project.select();
} else {
window.close();
}
},
select() {
if (Project) {
@ -448,6 +457,7 @@ onVueSetup(() => {
},
methods: {
openNewTab() {
this.last_opened_project = Project.uuid;
this.new_tab.visible = true;
this.new_tab.select();
setStartScreen(true);

View File

@ -649,48 +649,278 @@ new NodePreviewController(Mesh, {
})
BARS.defineActions(function() {
new Action('add_mesh', {
icon: 'fa-gem',
category: 'edit',
keybind: new Keybind({key: 'n', ctrl: true}),
condition: () => (Modes.edit && Format.meshes),
click: function () {
Undo.initEdit({outliner: true, elements: [], selection: true});
var base_mesh = new Mesh({
autouv: (settings.autouv.value ? 1 : 0)
}).init()
let add_mesh_dialog = new Dialog({
id: 'add_primitive',
title: 'action.add_mesh',
form: {
shape: {label: 'dialog.add_primitive.shape', type: 'select', options: {
cube: 'dialog.add_primitive.shape.cube',
pyramid: 'dialog.add_primitive.shape.pyramid',
circle: 'dialog.add_primitive.shape.circle',
cylinder: 'dialog.add_primitive.shape.cylinder',
tube: 'dialog.add_primitive.shape.tube',
cone: 'dialog.add_primitive.shape.cone',
sphere: 'dialog.add_primitive.shape.sphere',
torus: 'dialog.add_primitive.shape.torus',
}},
diameter: {label: 'dialog.add_primitive.diameter', type: 'number', value: 16},
height: {label: 'dialog.add_primitive.height', type: 'number', value: 8, condition: ({shape}) => ['cylinder', 'cone', 'cube', 'pyramid', 'tube'].includes(shape)},
sides: {label: 'dialog.add_primitive.sides', type: 'number', value: 16, condition: ({shape}) => ['cylinder', 'cone', 'circle', 'torus', 'sphere', 'tube'].includes(shape)},
minor_diameter: {label: 'dialog.add_primitive.minor_diameter', type: 'number', value: 4, condition: ({shape}) => ['torus', 'tube'].includes(shape)},
minor_sides: {label: 'dialog.add_primitive.minor_sides', type: 'number', value: 8, condition: ({shape}) => ['torus'].includes(shape)},
},
onConfirm(result) {
let elements = [];
Undo.initEdit({elements});
let mesh = new Mesh({
name: result.shape,
vertices: {}
});
var group = getCurrentGroup();
base_mesh.addTo(group)
mesh.addTo(group)
if (result.shape == 'circle') {
let vertex_keys = mesh.addVertices([0, 0, 0]);
let [m] = vertex_keys;
for (let i = 0; i < result.sides; i++) {
let x = Math.sin((i / result.sides) * Math.PI * 2) * result.diameter/2;
let z = Math.cos((i / result.sides) * Math.PI * 2) * result.diameter/2;
vertex_keys.push(...mesh.addVertices([x, 0, z]));
}
for (let i = 0; i < result.sides; i++) {
let [a, b] = vertex_keys.slice(i+2, i+2 + 2);
if (!a) {
b = vertex_keys[2];
a = vertex_keys[1];
} else if (!b) {
b = vertex_keys[1];
}
mesh.addFaces(new MeshFace( mesh, {vertices: [a, b, m]} ));
}
}
if (result.shape == 'cone') {
let vertex_keys = mesh.addVertices([0, 0, 0], [0, result.height, 0]);
let [m0, m1] = vertex_keys;
for (let i = 0; i < result.sides; i++) {
let x = Math.sin((i / result.sides) * Math.PI * 2) * result.diameter/2;
let z = Math.cos((i / result.sides) * Math.PI * 2) * result.diameter/2;
vertex_keys.push(...mesh.addVertices([x, 0, z]));
}
for (let i = 0; i < result.sides; i++) {
let [a, b] = vertex_keys.slice(i+2, i+2 + 2);
if (!b) {
b = vertex_keys[2];
}
mesh.addFaces(
new MeshFace( mesh, {vertices: [b, a, m0]} ),
new MeshFace( mesh, {vertices: [a, b, m1]} )
);
}
}
if (result.shape == 'cylinder') {
let vertex_keys = mesh.addVertices([0, 0, 0], [0, result.height, 0]);
let [m0, m1] = vertex_keys;
for (let i = 0; i < result.sides; i++) {
let x = Math.sin((i / result.sides) * Math.PI * 2) * result.diameter/2;
let z = Math.cos((i / result.sides) * Math.PI * 2) * result.diameter/2;
vertex_keys.push(...mesh.addVertices([x, 0, z], [x, result.height, z]));
}
for (let i = 0; i < result.sides; i++) {
let [a, b, c, d] = vertex_keys.slice(2*i+2, 2*i+2 + 4);
if (!c) {
c = vertex_keys[2];
d = vertex_keys[3];
}
mesh.addFaces(
new MeshFace( mesh, {vertices: [c, a, m0]}),
new MeshFace( mesh, {vertices: [a, c, d, b]} ),
new MeshFace( mesh, {vertices: [b, d, m1]} )
);
}
}
if (result.shape == 'tube') {
let vertex_keys = [];
let outer_r = result.diameter/2;
let inner_r = outer_r - result.minor_diameter;
for (let i = 0; i < result.sides; i++) {
let x = Math.sin((i / result.sides) * Math.PI * 2);
let z = Math.cos((i / result.sides) * Math.PI * 2);
vertex_keys.push(...mesh.addVertices(
[x * outer_r, 0, z * outer_r],
[x * outer_r, result.height, z * outer_r],
[x * inner_r, 0, z * inner_r],
[x * inner_r, result.height, z * inner_r],
));
}
for (let i = 0; i < result.sides; i++) {
let [a1, b1, c1, d1, a2, b2, c2, d2] = vertex_keys.slice(4*i, 4*i + 8);
if (!a2) {
a2 = vertex_keys[0];
b2 = vertex_keys[1];
c2 = vertex_keys[2];
d2 = vertex_keys[3];
}
if (a1 && b1 && c1 && d1 && a2 && b2 && c2 && d2) {
mesh.addFaces(
new MeshFace( mesh, {vertices: [a1, a2, b2, b1]} ),
new MeshFace( mesh, {vertices: [d1, d2, c2, c1]} ),
new MeshFace( mesh, {vertices: [c1, c2, a2, a1]} ),
new MeshFace( mesh, {vertices: [b1, b2, d2, d1]} ),
);
}
}
}
if (result.shape == 'torus') {
let rings = [];
for (let i = 0; i < result.sides; i++) {
let circle_x = Math.sin((i / result.sides) * Math.PI * 2);
let circle_z = Math.cos((i / result.sides) * Math.PI * 2);
let vertices = [];
for (let j = 0; j < result.minor_sides; j++) {
let slice_x = Math.sin((j / result.minor_sides) * Math.PI * 2) * result.minor_diameter/2;
let x = circle_x * (result.diameter/2 + slice_x)
let y = Math.cos((j / result.minor_sides) * Math.PI * 2) * result.minor_diameter/2;
let z = circle_z * (result.diameter/2 + slice_x)
vertices.push(...mesh.addVertices([x, y, z]));
}
rings.push(vertices);
}
for (let i = 0; i < result.sides; i++) {
let this_ring = rings[i];
let next_ring = rings[i+1] || rings[0];
for (let j = 0; j < result.minor_sides; j++) {
mesh.addFaces(new MeshFace( mesh, {vertices: [
this_ring[j+1] || this_ring[0],
next_ring[j+1] || next_ring[0],
this_ring[j],
next_ring[j],
]} ));
}
}
}
if (result.shape == 'sphere') {
let rings = [];
let sides = Math.round(result.sides/2)*2;
let [bottom] = mesh.addVertices([0, -result.diameter/2, 0]);
let [top] = mesh.addVertices([0, result.diameter/2, 0]);
for (let i = 0; i < result.sides; i++) {
let circle_x = Math.sin((i / result.sides) * Math.PI * 2);
let circle_z = Math.cos((i / result.sides) * Math.PI * 2);
let vertices = [];
for (let j = 1; j < (sides/2); j++) {
let slice_x = Math.sin((j / sides) * Math.PI * 2) * result.diameter/2;
let x = circle_x * slice_x
let y = Math.cos((j / sides) * Math.PI * 2) * result.diameter/2;
let z = circle_z * slice_x
vertices.push(...mesh.addVertices([x, y, z]));
}
rings.push(vertices);
}
for (let i = 0; i < result.sides; i++) {
let this_ring = rings[i];
let next_ring = rings[i+1] || rings[0];
for (let j = 0; j < (sides/2); j++) {
if (j == 0) {
mesh.addFaces(new MeshFace( mesh, {vertices: [
this_ring[j],
next_ring[j],
top
]} ));
} else if (!this_ring[j]) {
mesh.addFaces(new MeshFace( mesh, {vertices: [
next_ring[j-1],
this_ring[j-1],
bottom
]} ));
} else {
mesh.addFaces(new MeshFace( mesh, {vertices: [
this_ring[j],
next_ring[j],
this_ring[j-1],
next_ring[j-1],
]} ));
}
}
}
}
if (result.shape == 'cube') {
let r = result.diameter/2;
let h = result.height;
mesh.addVertices([r, h, r], [r, h, -r], [r, 0, r], [r, 0, -r], [-r, h, r], [-r, h, -r], [-r, 0, r], [-r, 0, -r]);
let vertex_keys = Object.keys(mesh.vertices);
mesh.addFaces(
new MeshFace( mesh, {vertices: [vertex_keys[0], vertex_keys[2], vertex_keys[1], vertex_keys[3]]} ), // East
new MeshFace( mesh, {vertices: [vertex_keys[4], vertex_keys[5], vertex_keys[6], vertex_keys[7]]} ), // West
new MeshFace( mesh, {vertices: [vertex_keys[0], vertex_keys[1], vertex_keys[4], vertex_keys[5]]} ), // Up
new MeshFace( mesh, {vertices: [vertex_keys[2], vertex_keys[6], vertex_keys[3], vertex_keys[7]]} ), // Down
new MeshFace( mesh, {vertices: [vertex_keys[0], vertex_keys[4], vertex_keys[2], vertex_keys[6]]} ), // South
new MeshFace( mesh, {vertices: [vertex_keys[1], vertex_keys[3], vertex_keys[5], vertex_keys[7]]} ), // North
);
}
if (result.shape == 'pyramid') {
let r = result.diameter/2;
let h = result.height;
mesh.addVertices([0, h, 0], [r, 0, r], [r, 0, -r], [-r, 0, r], [-r, 0, -r]);
let vertex_keys = Object.keys(mesh.vertices);
mesh.addFaces(
new MeshFace( mesh, {vertices: [vertex_keys[1], vertex_keys[3], vertex_keys[2], vertex_keys[4]]} ), // Down
new MeshFace( mesh, {vertices: [vertex_keys[1], vertex_keys[2], vertex_keys[0]]} ), // east
new MeshFace( mesh, {vertices: [vertex_keys[3], vertex_keys[1], vertex_keys[0]]} ), // south
new MeshFace( mesh, {vertices: [vertex_keys[2], vertex_keys[4], vertex_keys[0]]} ), // north
new MeshFace( mesh, {vertices: [vertex_keys[4], vertex_keys[3], vertex_keys[0]]} ), // west
);
}
if (Texture.all.length && Format.single_texture) {
for (var face in base_mesh.faces) {
base_mesh.faces[face].texture = Texture.getDefault().uuid
for (var face in mesh.faces) {
mesh.faces[face].texture = Texture.getDefault().uuid
}
UVEditor.loadData()
}
if (Format.bone_rig) {
if (group) {
var pos1 = group.origin.slice()
base_mesh.extend({
from:[ pos1[0]-0, pos1[1]-0, pos1[2]-0 ],
to:[ pos1[0]+1, pos1[1]+1, pos1[2]+1 ],
mesh.extend({
origin: pos1.slice()
})
}
}
elements.push(mesh);
mesh.init()
if (Group.selected) Group.selected.unselect()
base_mesh.select()
Undo.finishEdit('Add mesh', {outliner: true, elements: selected, selection: true});
Blockbench.dispatchEvent( 'add_mesh', {object: base_mesh} )
mesh.select()
Undo.finishEdit('Add primitive');
Blockbench.dispatchEvent( 'add_mesh', {object: mesh} )
Vue.nextTick(function() {
if (settings.create_rename.value) {
base_mesh.rename()
mesh.rename()
}
})
return base_mesh
}
})
new Action('add_mesh', {
icon: 'fa-gem',
category: 'edit',
keybind: new Keybind({key: 'n', ctrl: true}),
condition: () => (Modes.edit && Format.meshes),
click: function () {
add_mesh_dialog.show();
}
})
new BarSelect('selection_mode', {
@ -1226,250 +1456,4 @@ BARS.defineActions(function() {
})
}
})
new Action('add_primitive', {
icon: 'fa-shapes',
category: 'edit',
condition: () => (Modes.edit && Format.meshes),
click() {
let dialog = new Dialog({
id: 'add_primitive',
title: 'action.add_primitive',
form: {
shape: {label: 'dialog.add_primitive.shape', type: 'select', options: {
circle: 'dialog.add_primitive.shape.circle',
cylinder: 'dialog.add_primitive.shape.cylinder',
tube: 'dialog.add_primitive.shape.tube',
cone: 'dialog.add_primitive.shape.cone',
torus: 'dialog.add_primitive.shape.torus',
sphere: 'dialog.add_primitive.shape.sphere',
cube: 'dialog.add_primitive.shape.cube',
pyramid: 'dialog.add_primitive.shape.pyramid',
}},
diameter: {label: 'dialog.add_primitive.diameter', type: 'number', value: 16},
height: {label: 'dialog.add_primitive.height', type: 'number', value: 8, condition: ({shape}) => ['cylinder', 'cone', 'cube', 'pyramid', 'tube'].includes(shape)},
sides: {label: 'dialog.add_primitive.sides', type: 'number', value: 16, condition: ({shape}) => ['cylinder', 'cone', 'circle', 'torus', 'sphere', 'tube'].includes(shape)},
minor_diameter: {label: 'dialog.add_primitive.minor_diameter', type: 'number', value: 4, condition: ({shape}) => ['torus', 'tube'].includes(shape)},
minor_sides: {label: 'dialog.add_primitive.minor_sides', type: 'number', value: 8, condition: ({shape}) => ['torus'].includes(shape)},
},
onConfirm(result) {
let elements = [];
Undo.initEdit({elements});
let mesh = new Mesh({
name: result.shape,
vertices: {}
});
if (result.shape == 'circle') {
let vertex_keys = mesh.addVertices([0, 0, 0]);
let [m] = vertex_keys;
for (let i = 0; i < result.sides; i++) {
let x = Math.sin((i / result.sides) * Math.PI * 2) * result.diameter/2;
let z = Math.cos((i / result.sides) * Math.PI * 2) * result.diameter/2;
vertex_keys.push(...mesh.addVertices([x, 0, z]));
}
for (let i = 0; i < result.sides; i++) {
let [a, b] = vertex_keys.slice(i+2, i+2 + 2);
if (!a) {
b = vertex_keys[2];
a = vertex_keys[1];
} else if (!b) {
b = vertex_keys[1];
}
mesh.addFaces(new MeshFace( mesh, {vertices: [a, b, m]} ));
}
}
if (result.shape == 'cone') {
let vertex_keys = mesh.addVertices([0, 0, 0], [0, result.height, 0]);
let [m0, m1] = vertex_keys;
for (let i = 0; i < result.sides; i++) {
let x = Math.sin((i / result.sides) * Math.PI * 2) * result.diameter/2;
let z = Math.cos((i / result.sides) * Math.PI * 2) * result.diameter/2;
vertex_keys.push(...mesh.addVertices([x, 0, z]));
}
for (let i = 0; i < result.sides; i++) {
let [a, b] = vertex_keys.slice(i+2, i+2 + 2);
if (!b) {
b = vertex_keys[2];
}
mesh.addFaces(
new MeshFace( mesh, {vertices: [b, a, m0]} ),
new MeshFace( mesh, {vertices: [a, b, m1]} )
);
}
}
if (result.shape == 'cylinder') {
let vertex_keys = mesh.addVertices([0, 0, 0], [0, result.height, 0]);
let [m0, m1] = vertex_keys;
for (let i = 0; i < result.sides; i++) {
let x = Math.sin((i / result.sides) * Math.PI * 2) * result.diameter/2;
let z = Math.cos((i / result.sides) * Math.PI * 2) * result.diameter/2;
vertex_keys.push(...mesh.addVertices([x, 0, z], [x, result.height, z]));
}
for (let i = 0; i < result.sides; i++) {
let [a, b, c, d] = vertex_keys.slice(2*i+2, 2*i+2 + 4);
if (!c) {
c = vertex_keys[2];
d = vertex_keys[3];
}
mesh.addFaces(
new MeshFace( mesh, {vertices: [c, a, m0]}),
new MeshFace( mesh, {vertices: [a, c, d, b]} ),
new MeshFace( mesh, {vertices: [b, d, m1]} )
);
}
}
if (result.shape == 'tube') {
let vertex_keys = [];
let outer_r = result.diameter/2;
let inner_r = outer_r - result.minor_diameter;
for (let i = 0; i < result.sides; i++) {
let x = Math.sin((i / result.sides) * Math.PI * 2);
let z = Math.cos((i / result.sides) * Math.PI * 2);
vertex_keys.push(...mesh.addVertices(
[x * outer_r, 0, z * outer_r],
[x * outer_r, result.height, z * outer_r],
[x * inner_r, 0, z * inner_r],
[x * inner_r, result.height, z * inner_r],
));
}
for (let i = 0; i < result.sides; i++) {
let [a1, b1, c1, d1, a2, b2, c2, d2] = vertex_keys.slice(4*i, 4*i + 8);
if (!a2) {
a2 = vertex_keys[0];
b2 = vertex_keys[1];
c2 = vertex_keys[2];
d2 = vertex_keys[3];
}
if (a1 && b1 && c1 && d1 && a2 && b2 && c2 && d2) {
mesh.addFaces(
new MeshFace( mesh, {vertices: [a1, a2, b2, b1]} ),
new MeshFace( mesh, {vertices: [d1, d2, c2, c1]} ),
new MeshFace( mesh, {vertices: [c1, c2, a2, a1]} ),
new MeshFace( mesh, {vertices: [b1, b2, d2, d1]} ),
);
}
}
}
if (result.shape == 'torus') {
let rings = [];
for (let i = 0; i < result.sides; i++) {
let circle_x = Math.sin((i / result.sides) * Math.PI * 2);
let circle_z = Math.cos((i / result.sides) * Math.PI * 2);
let vertices = [];
for (let j = 0; j < result.minor_sides; j++) {
let slice_x = Math.sin((j / result.minor_sides) * Math.PI * 2) * result.minor_diameter/2;
let x = circle_x * (result.diameter/2 + slice_x)
let y = Math.cos((j / result.minor_sides) * Math.PI * 2) * result.minor_diameter/2;
let z = circle_z * (result.diameter/2 + slice_x)
vertices.push(...mesh.addVertices([x, y, z]));
}
rings.push(vertices);
}
for (let i = 0; i < result.sides; i++) {
let this_ring = rings[i];
let next_ring = rings[i+1] || rings[0];
for (let j = 0; j < result.minor_sides; j++) {
mesh.addFaces(new MeshFace( mesh, {vertices: [
this_ring[j+1] || this_ring[0],
next_ring[j+1] || next_ring[0],
this_ring[j],
next_ring[j],
]} ));
}
}
}
if (result.shape == 'sphere') {
let rings = [];
let sides = Math.round(result.sides/2)*2;
let [bottom] = mesh.addVertices([0, -result.diameter/2, 0]);
let [top] = mesh.addVertices([0, result.diameter/2, 0]);
for (let i = 0; i < result.sides; i++) {
let circle_x = Math.sin((i / result.sides) * Math.PI * 2);
let circle_z = Math.cos((i / result.sides) * Math.PI * 2);
let vertices = [];
for (let j = 1; j < (sides/2); j++) {
let slice_x = Math.sin((j / sides) * Math.PI * 2) * result.diameter/2;
let x = circle_x * slice_x
let y = Math.cos((j / sides) * Math.PI * 2) * result.diameter/2;
let z = circle_z * slice_x
vertices.push(...mesh.addVertices([x, y, z]));
}
rings.push(vertices);
}
for (let i = 0; i < result.sides; i++) {
let this_ring = rings[i];
let next_ring = rings[i+1] || rings[0];
for (let j = 0; j < (sides/2); j++) {
if (j == 0) {
mesh.addFaces(new MeshFace( mesh, {vertices: [
this_ring[j],
next_ring[j],
top
]} ));
} else if (!this_ring[j]) {
mesh.addFaces(new MeshFace( mesh, {vertices: [
next_ring[j-1],
this_ring[j-1],
bottom
]} ));
} else {
mesh.addFaces(new MeshFace( mesh, {vertices: [
this_ring[j],
next_ring[j],
this_ring[j-1],
next_ring[j-1],
]} ));
}
}
}
}
if (result.shape == 'cube') {
let r = result.diameter/2;
let h = result.height;
mesh.addVertices([r, h, r], [r, h, -r], [r, 0, r], [r, 0, -r], [-r, h, r], [-r, h, -r], [-r, 0, r], [-r, 0, -r]);
let vertex_keys = Object.keys(mesh.vertices);
mesh.addFaces(
new MeshFace( mesh, {vertices: [vertex_keys[0], vertex_keys[2], vertex_keys[1], vertex_keys[3]]} ), // East
new MeshFace( mesh, {vertices: [vertex_keys[4], vertex_keys[5], vertex_keys[6], vertex_keys[7]]} ), // West
new MeshFace( mesh, {vertices: [vertex_keys[0], vertex_keys[1], vertex_keys[4], vertex_keys[5]]} ), // Up
new MeshFace( mesh, {vertices: [vertex_keys[2], vertex_keys[6], vertex_keys[3], vertex_keys[7]]} ), // Down
new MeshFace( mesh, {vertices: [vertex_keys[0], vertex_keys[4], vertex_keys[2], vertex_keys[6]]} ), // South
new MeshFace( mesh, {vertices: [vertex_keys[1], vertex_keys[3], vertex_keys[5], vertex_keys[7]]} ), // North
);
}
if (result.shape == 'pyramid') {
let r = result.diameter/2;
let h = result.height;
mesh.addVertices([0, h, 0], [r, 0, r], [r, 0, -r], [-r, 0, r], [-r, 0, -r]);
let vertex_keys = Object.keys(mesh.vertices);
mesh.addFaces(
new MeshFace( mesh, {vertices: [vertex_keys[1], vertex_keys[3], vertex_keys[2], vertex_keys[4]]} ), // Down
new MeshFace( mesh, {vertices: [vertex_keys[1], vertex_keys[2], vertex_keys[0]]} ), // east
new MeshFace( mesh, {vertices: [vertex_keys[3], vertex_keys[1], vertex_keys[0]]} ), // south
new MeshFace( mesh, {vertices: [vertex_keys[2], vertex_keys[4], vertex_keys[0]]} ), // north
new MeshFace( mesh, {vertices: [vertex_keys[4], vertex_keys[3], vertex_keys[0]]} ), // west
);
}
elements.push(mesh);
mesh.init()
Undo.finishEdit('Add primitive');
}
}).show()
}
})
})

View File

@ -782,6 +782,60 @@ class Preview {
} else {
list.replace(data.vertices);
}
if (event.altKey || Pressing.overrides.alt) {
let mesh = data.element;
let start_face;
for (let fkey in mesh.faces) {
let face = mesh.faces[fkey];
if (face.vertices.length < 3) continue;
let vertices = face.vertices.filter(vkey => data.vertices.includes(vkey))
if (vertices.length >= 2) {
start_face = face;
break;
}
}
if (!start_face) return;
let processed_faces = [start_face];
function splitFace(face, side_vertices) {
processed_faces.push(face);
let sorted_vertices = face.getSortedVertices();
let side_index_diff = sorted_vertices.indexOf(side_vertices[0]) - sorted_vertices.indexOf(side_vertices[1]);
if (side_index_diff == -1 || side_index_diff > 2) side_vertices.reverse();
let opposite_vertices = sorted_vertices.filter(vkey => !side_vertices.includes(vkey));
let opposite_index_diff = sorted_vertices.indexOf(opposite_vertices[0]) - sorted_vertices.indexOf(opposite_vertices[1]);
if (opposite_index_diff == 1 || opposite_index_diff < -2) opposite_vertices.reverse();
list.safePush(...side_vertices);
// Find next (and previous) face
function doNextFace(index) {
for (let fkey in mesh.faces) {
let ref_face = mesh.faces[fkey];
if (ref_face.vertices.length < 3 || processed_faces.includes(ref_face)) continue;
let sorted_vertices = ref_face.getSortedVertices();
let vertices = ref_face.vertices.filter(vkey => vkey == side_vertices[index] || vkey == opposite_vertices[index]);
if (vertices.length >= 2) {
let second_vertex = sorted_vertices.find((vkey, i) => {
return vkey !== side_vertices[index]
&& vkey !== opposite_vertices[index]
&& (sorted_vertices.length == 3 || Math.abs(sorted_vertices.indexOf(side_vertices[index]) - i) !== 2);
})
splitFace(ref_face, [side_vertices[index], second_vertex]);
break;
}
}
}
doNextFace(0)
doNextFace(1);
}
splitFace(start_face, data.vertices);
}
updateSelection();
}
if (typeof Toolbox.selected.onCanvasClick === 'function') {

View File

@ -913,8 +913,6 @@
"action.add_cube.desc": "Adds a new cube",
"action.add_mesh": "Add Mesh",
"action.add_mesh.desc": "Adds a new mesh",
"action.add_primitive": "Add Primitive",
"action.add_primitive.desc": "Adds a new primitive shape",
"action.add_locator": "Add Locator",
"action.add_locator.desc": "Adds a new locator to control positions of particles, leashes etc",
"action.add_null_object": "Add Null Object",