mirror of
https://github.com/beestat/app.git
synced 2026-02-26 13:10:23 -05:00
Cleanup
This commit is contained in:
parent
ef2ba14202
commit
e630b61c01
@ -433,8 +433,9 @@ input[type=range]::-moz-range-thumb {
|
||||
.icon.battery_10:before { content: "\F007A"; }
|
||||
.icon.bell:before { content: "\F009A"; }
|
||||
.icon.bell_off:before { content: "\F009B"; }
|
||||
.icon.border_none_variant:before { content: "\F08A4"; }
|
||||
.icon.bullhorn:before { content: "\F00E6"; }
|
||||
.icon.border_none_variant:before { content: "\F08A4"; }
|
||||
.icon.texture_box:before { content: "\F0FE6"; }
|
||||
.icon.bullhorn:before { content: "\F00E6"; }
|
||||
.icon.calendar:before { content: "\F00ED"; }
|
||||
.icon.calendar_alert:before { content: "\F0A31"; }
|
||||
.icon.calendar_edit:before { content: "\F08A7"; }
|
||||
|
||||
@ -176,6 +176,7 @@ beestat.component.card.floor_plan_editor.prototype.decorate_contents_ = function
|
||||
*/
|
||||
beestat.component.card.floor_plan_editor.prototype.decorate_drawing_pane_ = function(parent) {
|
||||
const self = this;
|
||||
const has_early_access = beestat.user.has_early_access() === true;
|
||||
|
||||
// Dispose existing SVG to remove any global listeners.
|
||||
if (this.floor_plan_ !== undefined) {
|
||||
@ -195,6 +196,11 @@ beestat.component.card.floor_plan_editor.prototype.decorate_drawing_pane_ = func
|
||||
|
||||
this.floor_plan_.render(parent);
|
||||
|
||||
if (has_early_access !== true) {
|
||||
delete this.state_.active_surface_entity;
|
||||
delete this.state_.active_tree_entity;
|
||||
}
|
||||
|
||||
// Create and render the compass for setting orientation (early access only)
|
||||
if (beestat.user.has_early_access() === true) {
|
||||
this.compass_ = new beestat.component.compass(
|
||||
@ -343,6 +349,7 @@ beestat.component.card.floor_plan_editor.prototype.decorate_drawing_pane_ = func
|
||||
let active_surface_entity;
|
||||
this.state_.active_group.surfaces.forEach(function(surface) {
|
||||
const surface_entity = new beestat.component.floor_plan_entity.surface(self.floor_plan_, self.state_)
|
||||
.set_enabled(has_early_access)
|
||||
.set_surface(surface)
|
||||
.set_group(self.state_.active_group);
|
||||
|
||||
@ -396,6 +403,7 @@ beestat.component.card.floor_plan_editor.prototype.decorate_drawing_pane_ = func
|
||||
let active_tree_entity;
|
||||
tree_group.trees.forEach(function(tree) {
|
||||
const tree_entity = new beestat.component.floor_plan_entity.tree(self.floor_plan_, self.state_)
|
||||
.set_enabled(has_early_access)
|
||||
.set_tree(tree)
|
||||
.set_group(tree_group);
|
||||
|
||||
@ -602,8 +610,9 @@ beestat.component.card.floor_plan_editor.prototype.decorate_info_pane_tree_ = fu
|
||||
const grid = $.createElement('div')
|
||||
.style({
|
||||
'display': 'grid',
|
||||
'grid-template-columns': 'repeat(auto-fit, minmax(150px, 1fr))',
|
||||
'column-gap': beestat.style.size.gutter
|
||||
'grid-template-columns': 'repeat(4, minmax(150px, 1fr))',
|
||||
'column-gap': beestat.style.size.gutter,
|
||||
'width': '100%'
|
||||
});
|
||||
parent.appendChild(grid);
|
||||
|
||||
@ -694,8 +703,9 @@ beestat.component.card.floor_plan_editor.prototype.decorate_info_pane_surface_ =
|
||||
const grid = $.createElement('div')
|
||||
.style({
|
||||
'display': 'grid',
|
||||
'grid-template-columns': 'repeat(auto-fit, minmax(150px, 1fr))',
|
||||
'column-gap': beestat.style.size.gutter
|
||||
'grid-template-columns': 'repeat(4, minmax(150px, 1fr))',
|
||||
'column-gap': beestat.style.size.gutter,
|
||||
'width': '100%'
|
||||
});
|
||||
parent.appendChild(grid);
|
||||
|
||||
@ -708,29 +718,116 @@ beestat.component.card.floor_plan_editor.prototype.decorate_info_pane_surface_ =
|
||||
.set_label('Color')
|
||||
.set_width('100%');
|
||||
|
||||
const surface_colors = [
|
||||
{'label': 'Concrete', 'value': '#9e9e9e'},
|
||||
{'label': 'Asphalt', 'value': '#2f2f2f'},
|
||||
{'label': 'Mulch - Brown', 'value': '#6f4e37'},
|
||||
{'label': 'Mulch - Black', 'value': '#1f1b1a'},
|
||||
{'label': 'Gravel', 'value': '#b3aea3'},
|
||||
{'label': 'Pavers', 'value': '#8c6d5a'},
|
||||
{'label': 'Deck - Wood', 'value': '#8b5a2b'},
|
||||
{'label': 'Grass', 'value': '#4a7c3f'}
|
||||
];
|
||||
const normalize_hex_color = function(value) {
|
||||
if (value === undefined || value === null) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let normalized = String(value).trim();
|
||||
if (normalized === '') {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (normalized.charAt(0) !== '#') {
|
||||
normalized = '#' + normalized;
|
||||
}
|
||||
|
||||
if (/^#[0-9a-fA-F]{6}$/.test(normalized) !== true) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return normalized.toLowerCase();
|
||||
};
|
||||
|
||||
const apply_surface_color = function(color) {
|
||||
surface.color = color;
|
||||
self.floor_plan_.update_infobox();
|
||||
self.update_floor_plan_();
|
||||
self.rerender();
|
||||
};
|
||||
|
||||
const surface_colors = [
|
||||
{'label': 'Pavement - Concrete', 'value': '#9a9a96'},
|
||||
{'label': 'Pavement - Asphalt', 'value': '#1f2328'},
|
||||
{'label': 'Pavers - Brick', 'value': '#7a2f2a'},
|
||||
{'label': 'Pavers - Stone', 'value': '#8f877e'},
|
||||
{'label': 'Wood - Light', 'value': '#c79a6b'},
|
||||
{'label': 'Wood - Dark', 'value': '#4b2f1f'},
|
||||
{'label': 'Mulch - Brown', 'value': '#6b4a2f'},
|
||||
{'label': 'Mulch - Red', 'value': '#7a3f32'},
|
||||
{'label': 'Mulch - Black', 'value': '#2e3136'},
|
||||
{'label': 'Water - Pool', 'value': '#3e89b8'},
|
||||
{'label': 'Water - Natural', 'value': '#3f6f5b'}
|
||||
];
|
||||
surface_colors.sort(function(a, b) {
|
||||
return a.label.localeCompare(b.label, 'en', {'sensitivity': 'base'});
|
||||
});
|
||||
surface_colors.push({'label': 'Custom', 'value': '__custom__'});
|
||||
|
||||
const preset_color_map = {};
|
||||
surface_colors.forEach(function(surface_color) {
|
||||
if (surface_color.value !== '__custom__') {
|
||||
preset_color_map[surface_color.value] = true;
|
||||
}
|
||||
color_input.add_option(surface_color);
|
||||
});
|
||||
|
||||
color_input.render(div);
|
||||
color_input.set_value(surface.color || '#9e9e9e');
|
||||
|
||||
const custom_color_container = $.createElement('div');
|
||||
custom_color_container.style('display', 'none');
|
||||
grid.appendChild(custom_color_container);
|
||||
const custom_color_input = new beestat.component.input.text()
|
||||
.set_label('Custom Hex')
|
||||
.set_placeholder('#RRGGBB')
|
||||
.set_width('100%')
|
||||
.set_maxlength(7)
|
||||
.render(custom_color_container);
|
||||
|
||||
const current_surface_color = normalize_hex_color(surface.color) || '#9a9a96';
|
||||
const is_preset_color = preset_color_map[current_surface_color] === true;
|
||||
|
||||
if (is_preset_color === true) {
|
||||
color_input.set_value(current_surface_color);
|
||||
custom_color_input.set_value('', false);
|
||||
custom_color_container.style('display', 'none');
|
||||
} else {
|
||||
color_input.set_value('__custom__');
|
||||
custom_color_input.set_value(current_surface_color, false);
|
||||
custom_color_container.style('display', 'block');
|
||||
}
|
||||
|
||||
color_input.addEventListener('change', function() {
|
||||
surface.color = color_input.get_value();
|
||||
self.floor_plan_.update_infobox();
|
||||
self.update_floor_plan_();
|
||||
self.rerender();
|
||||
const selected_value = color_input.get_value();
|
||||
|
||||
if (selected_value === '__custom__') {
|
||||
const custom_color = normalize_hex_color(custom_color_input.get_value()) ||
|
||||
normalize_hex_color(surface.color) ||
|
||||
'#9a9a96';
|
||||
custom_color_input.set_value(custom_color, false);
|
||||
custom_color_container.style('display', 'block');
|
||||
custom_color_input.input_.focus();
|
||||
return;
|
||||
}
|
||||
|
||||
custom_color_input.set_value('', false);
|
||||
custom_color_container.style('display', 'none');
|
||||
apply_surface_color(selected_value);
|
||||
});
|
||||
|
||||
custom_color_input.addEventListener('change', function() {
|
||||
if (color_input.get_value() !== '__custom__') {
|
||||
return;
|
||||
}
|
||||
|
||||
const custom_color = normalize_hex_color(custom_color_input.get_value());
|
||||
if (custom_color === undefined) {
|
||||
custom_color_input.set_value(surface.color || '#9a9a96', false);
|
||||
return;
|
||||
}
|
||||
|
||||
custom_color_input.set_value(custom_color, false);
|
||||
apply_surface_color(custom_color);
|
||||
});
|
||||
|
||||
// Elevation
|
||||
|
||||
@ -66,14 +66,27 @@ beestat.component.floor_plan.prototype.render = function(parent) {
|
||||
this.update_view_box_();
|
||||
|
||||
this.toolbar_container_ = $.createElement('div');
|
||||
const toolbar_left = beestat.style.size.gutter + (beestat.style.size.gutter / 2);
|
||||
const toolbar_column_width = 40;
|
||||
const toolbar_column_gap = beestat.style.size.gutter / 2;
|
||||
const toolbar_row_offset = toolbar_column_width;
|
||||
this.toolbar_container_.style({
|
||||
'position': 'absolute',
|
||||
'top': beestat.style.size.gutter,
|
||||
'left': beestat.style.size.gutter + (beestat.style.size.gutter / 2),
|
||||
'left': toolbar_left,
|
||||
'width': '40px'
|
||||
});
|
||||
parent.appendChild(this.toolbar_container_);
|
||||
|
||||
this.toolbar_container_secondary_ = $.createElement('div');
|
||||
this.toolbar_container_secondary_.style({
|
||||
'position': 'absolute',
|
||||
'top': beestat.style.size.gutter + toolbar_row_offset,
|
||||
'left': toolbar_left + toolbar_column_width + toolbar_column_gap,
|
||||
'width': '40px'
|
||||
});
|
||||
parent.appendChild(this.toolbar_container_secondary_);
|
||||
|
||||
this.floors_container_ = $.createElement('div');
|
||||
this.floors_container_.style({
|
||||
'position': 'absolute',
|
||||
@ -112,22 +125,27 @@ beestat.component.floor_plan.prototype.render = function(parent) {
|
||||
} else if (e.key === 'Delete') {
|
||||
if (self.state_.active_point_entity !== undefined) {
|
||||
self.remove_point_();
|
||||
} else if (self.state_.active_surface_entity !== undefined) {
|
||||
self.remove_surface_();
|
||||
} else if (self.state_.active_room_entity !== undefined) {
|
||||
self.remove_room_();
|
||||
} else if (self.state_.active_tree_entity !== undefined) {
|
||||
self.remove_tree_();
|
||||
} else {
|
||||
self.remove_active_entity_();
|
||||
}
|
||||
} else if (e.key.toLowerCase() === 'r') {
|
||||
if (e.ctrlKey === false) {
|
||||
self.add_room_();
|
||||
}
|
||||
} else if (e.key.toLowerCase() === 'f') {
|
||||
if (e.ctrlKey === false && beestat.user.has_early_access() === true) {
|
||||
self.add_surface_();
|
||||
}
|
||||
} else if (e.key.toLowerCase() === 't') {
|
||||
if (e.ctrlKey === false && beestat.user.has_early_access() === true) {
|
||||
self.add_tree_();
|
||||
}
|
||||
} else if (e.key.toLowerCase() === 's') {
|
||||
self.toggle_snapping_();
|
||||
} else if (
|
||||
e.key.toLowerCase() === 'c' &&
|
||||
e.ctrlKey === true &&
|
||||
beestat.user.has_early_access() === true &&
|
||||
self.state_.active_surface_entity !== undefined
|
||||
) {
|
||||
self.state_.copied_object = {
|
||||
@ -146,6 +164,7 @@ beestat.component.floor_plan.prototype.render = function(parent) {
|
||||
} else if (
|
||||
e.key.toLowerCase() === 'c' &&
|
||||
e.ctrlKey === true &&
|
||||
beestat.user.has_early_access() === true &&
|
||||
self.state_.active_tree_entity !== undefined
|
||||
) {
|
||||
self.state_.copied_object = {
|
||||
@ -155,6 +174,7 @@ beestat.component.floor_plan.prototype.render = function(parent) {
|
||||
} else if (
|
||||
e.key.toLowerCase() === 'v' &&
|
||||
e.ctrlKey === true &&
|
||||
beestat.user.has_early_access() === true &&
|
||||
self.state_.copied_object !== undefined &&
|
||||
self.state_.copied_object.type === 'surface'
|
||||
) {
|
||||
@ -162,6 +182,7 @@ beestat.component.floor_plan.prototype.render = function(parent) {
|
||||
} else if (
|
||||
e.key.toLowerCase() === 'v' &&
|
||||
e.ctrlKey === true &&
|
||||
beestat.user.has_early_access() === true &&
|
||||
self.state_.copied_object !== undefined &&
|
||||
self.state_.copied_object.type === 'tree'
|
||||
) {
|
||||
@ -450,6 +471,7 @@ beestat.component.floor_plan.prototype.dispose = function() {
|
||||
beestat.component.floor_plan.prototype.update_toolbar = function() {
|
||||
const self = this;
|
||||
const tree_group = this.get_tree_group_();
|
||||
const has_early_access = beestat.user.has_early_access() === true;
|
||||
|
||||
if (this.tile_group_ !== undefined) {
|
||||
this.tile_group_.dispose();
|
||||
@ -458,8 +480,12 @@ beestat.component.floor_plan.prototype.update_toolbar = function() {
|
||||
if (this.tile_group_floors_ !== undefined) {
|
||||
this.tile_group_floors_.dispose();
|
||||
}
|
||||
if (this.tile_group_secondary_ !== undefined) {
|
||||
this.tile_group_secondary_.dispose();
|
||||
}
|
||||
|
||||
this.tile_group_ = new beestat.component.tile_group();
|
||||
this.tile_group_secondary_ = new beestat.component.tile_group();
|
||||
|
||||
// Add floor
|
||||
this.tile_group_.add_tile(new beestat.component.tile()
|
||||
@ -480,49 +506,55 @@ beestat.component.floor_plan.prototype.update_toolbar = function() {
|
||||
})
|
||||
);
|
||||
|
||||
// Add surface
|
||||
this.tile_group_.add_tile(new beestat.component.tile()
|
||||
.set_icon('border_none_variant')
|
||||
.set_title('Add Surface')
|
||||
.set_text_color(beestat.style.color.gray.light)
|
||||
.set_background_color(beestat.style.color.bluegray.base)
|
||||
.set_background_hover_color(beestat.style.color.bluegray.light)
|
||||
.addEventListener('click', function() {
|
||||
self.add_surface_();
|
||||
})
|
||||
);
|
||||
|
||||
// Remove room
|
||||
const remove_room_button = new beestat.component.tile()
|
||||
.set_icon('card_remove_outline')
|
||||
.set_title('Remove Room [Delete]')
|
||||
.set_background_color(beestat.style.color.bluegray.base);
|
||||
this.tile_group_.add_tile(remove_room_button);
|
||||
|
||||
if (this.state_.active_room_entity !== undefined) {
|
||||
remove_room_button
|
||||
if (has_early_access === true) {
|
||||
// Add surface
|
||||
this.tile_group_.add_tile(new beestat.component.tile()
|
||||
.set_icon('texture_box')
|
||||
.set_title('Add Surface [F]')
|
||||
.set_text_color(beestat.style.color.gray.light)
|
||||
.set_background_color(beestat.style.color.bluegray.base)
|
||||
.set_background_hover_color(beestat.style.color.bluegray.light)
|
||||
.set_text_color(beestat.style.color.red.base)
|
||||
.addEventListener('click', this.remove_room_.bind(this));
|
||||
} else {
|
||||
remove_room_button
|
||||
.set_text_color(beestat.style.color.bluegray.dark);
|
||||
.addEventListener('click', function() {
|
||||
self.add_surface_();
|
||||
})
|
||||
);
|
||||
|
||||
// Add tree (first floor only)
|
||||
const add_tree_button = new beestat.component.tile()
|
||||
.set_icon('tree')
|
||||
.set_title('Add Tree [T]')
|
||||
.set_background_color(beestat.style.color.bluegray.base);
|
||||
this.tile_group_.add_tile(add_tree_button);
|
||||
|
||||
if (this.state_.active_group === tree_group) {
|
||||
add_tree_button
|
||||
.set_background_hover_color(beestat.style.color.bluegray.light)
|
||||
.set_text_color(beestat.style.color.gray.light)
|
||||
.addEventListener('click', this.add_tree_.bind(this));
|
||||
} else {
|
||||
add_tree_button
|
||||
.set_text_color(beestat.style.color.bluegray.dark);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove surface
|
||||
const remove_surface_button = new beestat.component.tile()
|
||||
// Remove selected room, surface, or tree
|
||||
const remove_button = new beestat.component.tile()
|
||||
.set_icon('card_remove_outline')
|
||||
.set_title('Remove Surface [Delete]')
|
||||
.set_title('Remove [Delete]')
|
||||
.set_background_color(beestat.style.color.bluegray.base);
|
||||
this.tile_group_.add_tile(remove_surface_button);
|
||||
this.tile_group_.add_tile(remove_button);
|
||||
|
||||
if (this.state_.active_surface_entity !== undefined) {
|
||||
remove_surface_button
|
||||
if (
|
||||
this.state_.active_room_entity !== undefined ||
|
||||
this.state_.active_surface_entity !== undefined ||
|
||||
this.state_.active_tree_entity !== undefined
|
||||
) {
|
||||
remove_button
|
||||
.set_background_hover_color(beestat.style.color.bluegray.light)
|
||||
.set_text_color(beestat.style.color.red.base)
|
||||
.addEventListener('click', this.remove_surface_.bind(this));
|
||||
.addEventListener('click', this.remove_active_entity_.bind(this));
|
||||
} else {
|
||||
remove_surface_button
|
||||
remove_button
|
||||
.set_text_color(beestat.style.color.bluegray.dark);
|
||||
}
|
||||
|
||||
@ -589,7 +621,7 @@ beestat.component.floor_plan.prototype.update_toolbar = function() {
|
||||
.set_icon('undo')
|
||||
.set_title('Undo [Ctrl+Z]')
|
||||
.set_background_color(beestat.style.color.bluegray.base);
|
||||
this.tile_group_.add_tile(undo_button);
|
||||
this.tile_group_secondary_.add_tile(undo_button);
|
||||
|
||||
if (
|
||||
this.can_undo_() === true
|
||||
@ -610,7 +642,7 @@ beestat.component.floor_plan.prototype.update_toolbar = function() {
|
||||
.set_icon('redo')
|
||||
.set_title('Redo [Ctrl+Y]')
|
||||
.set_background_color(beestat.style.color.bluegray.base);
|
||||
this.tile_group_.add_tile(redo_button);
|
||||
this.tile_group_secondary_.add_tile(redo_button);
|
||||
|
||||
if (
|
||||
this.can_redo_() === true
|
||||
@ -631,7 +663,7 @@ beestat.component.floor_plan.prototype.update_toolbar = function() {
|
||||
.set_icon('magnify_plus_outline')
|
||||
.set_title('Zoom In')
|
||||
.set_background_color(beestat.style.color.bluegray.base);
|
||||
this.tile_group_.add_tile(zoom_in_button);
|
||||
this.tile_group_secondary_.add_tile(zoom_in_button);
|
||||
|
||||
if (
|
||||
this.can_zoom_in_() === true
|
||||
@ -652,7 +684,7 @@ beestat.component.floor_plan.prototype.update_toolbar = function() {
|
||||
.set_icon('magnify_minus_outline')
|
||||
.set_title('Zoom out')
|
||||
.set_background_color(beestat.style.color.bluegray.base);
|
||||
this.tile_group_.add_tile(zoom_out_button);
|
||||
this.tile_group_secondary_.add_tile(zoom_out_button);
|
||||
|
||||
if (
|
||||
this.can_zoom_out_() === true
|
||||
@ -668,25 +700,9 @@ beestat.component.floor_plan.prototype.update_toolbar = function() {
|
||||
.set_text_color(beestat.style.color.bluegray.dark);
|
||||
}
|
||||
|
||||
// Add tree (first floor only)
|
||||
const add_tree_button = new beestat.component.tile()
|
||||
.set_icon('tree')
|
||||
.set_title('Add Tree')
|
||||
.set_background_color(beestat.style.color.bluegray.base);
|
||||
this.tile_group_.add_tile(add_tree_button);
|
||||
|
||||
if (this.state_.active_group === tree_group) {
|
||||
add_tree_button
|
||||
.set_background_hover_color(beestat.style.color.bluegray.light)
|
||||
.set_text_color(beestat.style.color.gray.light)
|
||||
.addEventListener('click', this.add_tree_.bind(this));
|
||||
} else {
|
||||
add_tree_button
|
||||
.set_text_color(beestat.style.color.bluegray.dark);
|
||||
}
|
||||
|
||||
// Render
|
||||
this.tile_group_.render(this.toolbar_container_);
|
||||
this.tile_group_secondary_.render(this.toolbar_container_secondary_);
|
||||
|
||||
// FLOORS
|
||||
this.tile_group_floors_ = new beestat.component.tile_group();
|
||||
@ -810,6 +826,10 @@ beestat.component.floor_plan.prototype.toggle_snapping_ = function() {
|
||||
* @param {object} surface Optional surface to copy from.
|
||||
*/
|
||||
beestat.component.floor_plan.prototype.add_surface_ = function(surface) {
|
||||
if (beestat.user.has_early_access() !== true) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.save_buffer();
|
||||
|
||||
if (this.state_.active_group.surfaces === undefined) {
|
||||
@ -825,7 +845,7 @@ beestat.component.floor_plan.prototype.add_surface_ = function(surface) {
|
||||
'surface_id': window.crypto.randomUUID(),
|
||||
'x': svg_view_box.x + (svg_view_box.width / 2) - (new_surface_size / 2),
|
||||
'y': svg_view_box.y + (svg_view_box.height / 2) - (new_surface_size / 2),
|
||||
'color': '#9e9e9e',
|
||||
'color': '#9a9a96',
|
||||
'height': 0,
|
||||
'points': [
|
||||
{
|
||||
@ -863,7 +883,7 @@ beestat.component.floor_plan.prototype.add_surface_ = function(surface) {
|
||||
'surface_id': window.crypto.randomUUID(),
|
||||
'x': svg_view_box.x + (svg_view_box.width / 2) - ((max_x - min_x) / 2),
|
||||
'y': svg_view_box.y + (svg_view_box.height / 2) - ((max_y - min_y) / 2),
|
||||
'color': surface.color || '#9e9e9e',
|
||||
'color': surface.color || '#9a9a96',
|
||||
'height': surface.height || 0,
|
||||
'points': beestat.clone(surface.points)
|
||||
};
|
||||
@ -984,6 +1004,25 @@ beestat.component.floor_plan.prototype.remove_room_ = function() {
|
||||
this.dispatchEvent('remove_room');
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove the currently active selectable entity (surface, room, or tree).
|
||||
*/
|
||||
beestat.component.floor_plan.prototype.remove_active_entity_ = function() {
|
||||
if (this.state_.active_surface_entity !== undefined) {
|
||||
this.remove_surface_();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.state_.active_room_entity !== undefined) {
|
||||
this.remove_room_();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.state_.active_tree_entity !== undefined) {
|
||||
this.remove_tree_();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove the currently active surface.
|
||||
*/
|
||||
@ -1026,6 +1065,10 @@ beestat.component.floor_plan.prototype.remove_surface_ = function() {
|
||||
* @param {object} tree Optional tree to copy from.
|
||||
*/
|
||||
beestat.component.floor_plan.prototype.add_tree_ = function(tree) {
|
||||
if (beestat.user.has_early_access() !== true) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tree_group = this.get_tree_group_();
|
||||
if (tree_group === undefined || this.state_.active_group !== tree_group) {
|
||||
return;
|
||||
|
||||
@ -30,7 +30,7 @@ beestat.component.floor_plan_entity.surface.prototype.decorate_polygon_ = functi
|
||||
} else if (this.enabled_ === true) {
|
||||
this.polygon_.style.cursor = 'pointer';
|
||||
this.polygon_.style.fillOpacity = '0.5';
|
||||
this.polygon_.style.fill = this.surface_.color || '#9e9e9e';
|
||||
this.polygon_.style.fill = this.surface_.color || '#9a9a96';
|
||||
this.polygon_.style.stroke = beestat.style.color.gray.base;
|
||||
} else {
|
||||
this.polygon_.style.cursor = 'default';
|
||||
@ -82,7 +82,7 @@ beestat.component.floor_plan_entity.surface.prototype.set_surface = function(sur
|
||||
}
|
||||
|
||||
if (this.surface_.color === undefined) {
|
||||
this.surface_.color = '#9e9e9e';
|
||||
this.surface_.color = '#9a9a96';
|
||||
}
|
||||
if (this.surface_.height === undefined) {
|
||||
this.surface_.height = 0;
|
||||
@ -108,6 +108,10 @@ beestat.component.floor_plan_entity.surface.prototype.get_surface = function() {
|
||||
* @return {beestat.component.floor_plan_entity.surface} This.
|
||||
*/
|
||||
beestat.component.floor_plan_entity.surface.prototype.set_active = function(active) {
|
||||
if (active === true && this.enabled_ !== true) {
|
||||
return this;
|
||||
}
|
||||
|
||||
if (this.state_.active_point_entity !== undefined) {
|
||||
this.state_.active_point_entity.set_active(false);
|
||||
this.floor_plan_.update_toolbar();
|
||||
|
||||
@ -161,6 +161,19 @@ beestat.component.floor_plan_entity.tree.prototype.set_group = function(group) {
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set enabled (different than active).
|
||||
*
|
||||
* @param {boolean} enabled
|
||||
*
|
||||
* @return {beestat.component.floor_plan_entity.tree} This.
|
||||
*/
|
||||
beestat.component.floor_plan_entity.tree.prototype.set_enabled = function(enabled) {
|
||||
this.enabled_ = enabled;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the tree.
|
||||
*
|
||||
@ -178,6 +191,10 @@ beestat.component.floor_plan_entity.tree.prototype.get_tree = function() {
|
||||
* @return {beestat.component.floor_plan_entity.tree} This.
|
||||
*/
|
||||
beestat.component.floor_plan_entity.tree.prototype.set_active = function(active) {
|
||||
if (active === true && this.enabled_ !== true) {
|
||||
return this;
|
||||
}
|
||||
|
||||
if (active !== this.active_) {
|
||||
this.active_ = active;
|
||||
|
||||
|
||||
@ -377,7 +377,7 @@ beestat.component.scene.prototype.get_snow_cover_blend_ = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Blend roof and ground surface materials toward snow white.
|
||||
* Blend roof, ground, and floor-plan surface materials toward snow white.
|
||||
*
|
||||
* @param {number} snow_blend
|
||||
*/
|
||||
@ -386,7 +386,10 @@ beestat.component.scene.prototype.update_snow_surface_colors_ = function(snow_bl
|
||||
return;
|
||||
}
|
||||
|
||||
const blend = Math.max(0, Math.min(1, snow_blend));
|
||||
// Keep a small amount of base color visible at peak snow for definition.
|
||||
const normalized_blend = Math.max(0, Math.min(1, snow_blend));
|
||||
const blend = normalized_blend * 0.9;
|
||||
const foliage_blend = normalized_blend * 0.75;
|
||||
const snow_color = new THREE.Color(beestat.component.scene.snow_surface_color);
|
||||
const base_roof_color = new THREE.Color(this.get_appearance_value_('roof_color'));
|
||||
const base_ground_color = new THREE.Color(this.get_appearance_value_('ground_color'));
|
||||
@ -411,12 +414,38 @@ beestat.component.scene.prototype.update_snow_surface_colors_ = function(snow_bl
|
||||
this.layers_.environment.traverse(function(object) {
|
||||
if (
|
||||
object.userData !== undefined &&
|
||||
object.userData.is_ground_surface === true &&
|
||||
object.userData.is_ground === true &&
|
||||
object.material !== undefined &&
|
||||
object.material.color !== undefined
|
||||
) {
|
||||
object.material.color.copy(ground_color);
|
||||
}
|
||||
|
||||
if (
|
||||
object.userData !== undefined &&
|
||||
object.userData.is_surface === true &&
|
||||
object.material !== undefined &&
|
||||
object.material.color !== undefined
|
||||
) {
|
||||
const base_surface_color = new THREE.Color(
|
||||
object.userData.base_surface_color || object.material.color.getHex()
|
||||
);
|
||||
const surface_color = base_surface_color.clone().lerp(snow_color, blend);
|
||||
object.material.color.copy(surface_color);
|
||||
}
|
||||
|
||||
if (
|
||||
object.userData !== undefined &&
|
||||
object.userData.is_tree_foliage === true &&
|
||||
object.material !== undefined &&
|
||||
object.material.color !== undefined
|
||||
) {
|
||||
const base_foliage_color = new THREE.Color(
|
||||
object.userData.base_tree_foliage_color || object.material.color.getHex()
|
||||
);
|
||||
const foliage_color = base_foliage_color.clone().lerp(snow_color, foliage_blend);
|
||||
object.material.color.copy(foliage_color);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
@ -1879,7 +1908,7 @@ beestat.component.scene.prototype.add_surface_ = function(layer, group, surface)
|
||||
}
|
||||
shape.closePath();
|
||||
|
||||
const color = surface.color || '#9e9e9e';
|
||||
const color = surface.color || '#9a9a96';
|
||||
const height = Math.max(0, Number(surface.height || 0));
|
||||
const elevation = surface.elevation || group.elevation || 0;
|
||||
const z_lift = beestat.component.scene.surface_z_lift;
|
||||
@ -1917,6 +1946,7 @@ beestat.component.scene.prototype.add_surface_ = function(layer, group, surface)
|
||||
mesh.castShadow = true;
|
||||
mesh.userData.is_environment = true;
|
||||
mesh.userData.is_surface = true;
|
||||
mesh.userData.base_surface_color = color;
|
||||
|
||||
layer.add(mesh);
|
||||
};
|
||||
@ -2130,12 +2160,44 @@ beestat.component.scene.prototype.update_debug_ = function() {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a finite bounding box for scene layout. Empty floor plans can report
|
||||
* Infinity bounds; clamp those to a reasonable fallback around origin.
|
||||
*
|
||||
* @return {{left:number,right:number,top:number,bottom:number,width:number,height:number,x:number,y:number}}
|
||||
*/
|
||||
beestat.component.scene.prototype.get_scene_bounding_box_ = function() {
|
||||
const bounding_box = beestat.floor_plan.get_bounding_box(this.floor_plan_id_);
|
||||
|
||||
const is_finite_box =
|
||||
Number.isFinite(bounding_box.left) &&
|
||||
Number.isFinite(bounding_box.right) &&
|
||||
Number.isFinite(bounding_box.top) &&
|
||||
Number.isFinite(bounding_box.bottom);
|
||||
|
||||
if (is_finite_box === true) {
|
||||
return bounding_box;
|
||||
}
|
||||
|
||||
const fallback_half_size = 180;
|
||||
return {
|
||||
'left': -fallback_half_size,
|
||||
'right': fallback_half_size,
|
||||
'top': -fallback_half_size,
|
||||
'bottom': fallback_half_size,
|
||||
'width': fallback_half_size * 2,
|
||||
'height': fallback_half_size * 2,
|
||||
'x': -fallback_half_size,
|
||||
'y': -fallback_half_size
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a group containing all of the extruded geometry that can be transformed
|
||||
* all together.
|
||||
*/
|
||||
beestat.component.scene.prototype.add_main_group_ = function() {
|
||||
const bounding_box = beestat.floor_plan.get_bounding_box(this.floor_plan_id_);
|
||||
const bounding_box = this.get_scene_bounding_box_();
|
||||
|
||||
// Main group handles orientation and centering
|
||||
this.main_group_ = new THREE.Group();
|
||||
@ -3317,6 +3379,8 @@ beestat.component.scene.prototype.create_pine_tree_ = function(height, max_diame
|
||||
foliage_mesh.castShadow = true;
|
||||
foliage_mesh.receiveShadow = true;
|
||||
foliage_mesh.userData.is_environment = true;
|
||||
foliage_mesh.userData.is_tree_foliage = true;
|
||||
foliage_mesh.userData.base_tree_foliage_color = foliage_mesh.material.color.getHex();
|
||||
tree.add(foliage_mesh);
|
||||
|
||||
previous_apex_height = segment_base_height + segment_height;
|
||||
@ -3483,6 +3547,7 @@ beestat.component.scene.prototype.create_round_tree_ = function(height, max_diam
|
||||
const tree = new THREE.Group();
|
||||
tree.userData.is_environment = true;
|
||||
tree.userData.is_tree = true;
|
||||
const max_canopy_radius = Math.max(0.5, max_diameter / 2);
|
||||
|
||||
const wood_material = new THREE.MeshStandardMaterial({
|
||||
'color': 0x6a4d2f,
|
||||
@ -3492,7 +3557,6 @@ beestat.component.scene.prototype.create_round_tree_ = function(height, max_diam
|
||||
});
|
||||
|
||||
const trunk_height = height * 0.75;
|
||||
const size_scale = trunk_height / Math.max(1, height);
|
||||
const trunk_radius_bottom = Math.max(1.5, trunk_height * 0.03);
|
||||
const trunk_stick = this.create_stick_mesh_({
|
||||
'height': trunk_height,
|
||||
@ -3551,7 +3615,10 @@ beestat.component.scene.prototype.create_round_tree_ = function(height, max_diam
|
||||
positions.needsUpdate = true;
|
||||
geometry.computeVertexNormals();
|
||||
|
||||
return new THREE.Mesh(geometry, foliage_material.clone());
|
||||
const foliage_mesh = new THREE.Mesh(geometry, foliage_material.clone());
|
||||
foliage_mesh.userData.is_tree_foliage = true;
|
||||
foliage_mesh.userData.base_tree_foliage_color = foliage_mesh.material.color.getHex();
|
||||
return foliage_mesh;
|
||||
};
|
||||
const branch_height_samples = [];
|
||||
const branch_tips = [];
|
||||
@ -3559,6 +3626,9 @@ beestat.component.scene.prototype.create_round_tree_ = function(height, max_diam
|
||||
if (has_foliage === true && this.tree_foliage_meshes_ === undefined) {
|
||||
this.tree_foliage_meshes_ = [];
|
||||
}
|
||||
if (has_foliage === true && this.tree_branch_groups_ === undefined) {
|
||||
this.tree_branch_groups_ = [];
|
||||
}
|
||||
|
||||
for (let i = 0; i < branch_count; i++) {
|
||||
const stratified = (i + 0.5) / branch_count;
|
||||
@ -3601,6 +3671,22 @@ beestat.component.scene.prototype.create_round_tree_ = function(height, max_diam
|
||||
};
|
||||
|
||||
const create_branch = function(base, direction, length, radius_bottom, depth) {
|
||||
const horizontal_direction_length = Math.sqrt(
|
||||
(direction.x * direction.x) + (direction.y * direction.y)
|
||||
);
|
||||
if (horizontal_direction_length > 0) {
|
||||
const base_horizontal_radius = Math.sqrt((base.x * base.x) + (base.y * base.y));
|
||||
const max_length_from_diameter =
|
||||
(max_canopy_radius - base_horizontal_radius) / horizontal_direction_length;
|
||||
if (Number.isFinite(max_length_from_diameter) === true) {
|
||||
length = Math.max(0, Math.min(length, max_length_from_diameter));
|
||||
}
|
||||
}
|
||||
length = Math.max(0, length);
|
||||
if (length < 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const branch_stick = self.create_stick_mesh_({
|
||||
'height': length,
|
||||
'radius_bottom': radius_bottom,
|
||||
@ -3657,6 +3743,9 @@ beestat.component.scene.prototype.create_round_tree_ = function(height, max_diam
|
||||
child_radius_bottom,
|
||||
depth + 1
|
||||
);
|
||||
if (child_branch === null) {
|
||||
continue;
|
||||
}
|
||||
branch_tips.push(get_stick_point_world(child_branch, 1));
|
||||
add_sub_branches(child_branch, depth + 1);
|
||||
}
|
||||
@ -3666,7 +3755,16 @@ beestat.component.scene.prototype.create_round_tree_ = function(height, max_diam
|
||||
const t = branch_height_samples[i];
|
||||
const base_height = trunk_height * (0.5 + (t * 0.45));
|
||||
const base_offset = this.sample_stick_curve_offset_(trunk_stick.curve, base_height);
|
||||
const branch_length = Math.max(8, (max_diameter * (0.75 - (t * 0.34))) * size_scale);
|
||||
// Scale branch length by both canopy diameter and total tree height so
|
||||
// taller trees do not end up with disproportionately short limbs.
|
||||
const height_to_diameter_ratio = height / Math.max(1, max_diameter);
|
||||
const branch_height_scale = Math.max(0.75, Math.min(1.9, height_to_diameter_ratio / 1.4));
|
||||
// Stronger nonlinear taper so upper branches are visibly shorter.
|
||||
const vertical_taper = Math.pow(1 - t, 1.35);
|
||||
const branch_length = Math.max(
|
||||
4,
|
||||
(max_diameter * (0.2 + (0.8 * vertical_taper))) * branch_height_scale
|
||||
);
|
||||
const branch_radius_bottom = Math.max(0.35, trunk_radius_bottom * (0.42 - (t * 0.26)));
|
||||
const azimuth = ((i / branch_count) * Math.PI * 2) + ((Math.random() - 0.5) * 0.35);
|
||||
const elevation = (Math.PI / 180) * (16 + (Math.random() * 10));
|
||||
@ -3678,11 +3776,14 @@ beestat.component.scene.prototype.create_round_tree_ = function(height, max_diam
|
||||
const base = new THREE.Vector3(base_offset.x, base_offset.y, -base_height);
|
||||
|
||||
const primary_branch = create_branch(base, direction, branch_length, branch_radius_bottom, 0);
|
||||
if (primary_branch === null) {
|
||||
continue;
|
||||
}
|
||||
branch_tips.push(get_stick_point_world(primary_branch, 1));
|
||||
add_sub_branches(primary_branch, 0);
|
||||
}
|
||||
|
||||
if (has_foliage === true) {
|
||||
if (has_foliage === true) {
|
||||
const core_height = trunk_height * 0.75;
|
||||
const core_offset = this.sample_stick_curve_offset_(trunk_stick.curve, core_height);
|
||||
const core_center = new THREE.Vector3(core_offset.x, core_offset.y, -core_height);
|
||||
@ -3693,7 +3794,7 @@ beestat.component.scene.prototype.create_round_tree_ = function(height, max_diam
|
||||
coverage_radius = distance;
|
||||
}
|
||||
}
|
||||
const core_radius = Math.max(20, coverage_radius * 1.03);
|
||||
const core_radius = Math.min(max_canopy_radius, Math.max(4, coverage_radius * 1.03));
|
||||
const core_blob = create_foliage_blob(core_radius, 0.18);
|
||||
core_blob.position.copy(core_center);
|
||||
core_blob.scale.set(
|
||||
@ -3711,6 +3812,10 @@ beestat.component.scene.prototype.create_round_tree_ = function(height, max_diam
|
||||
this.tree_foliage_meshes_.push(core_blob);
|
||||
}
|
||||
|
||||
if (has_foliage === true) {
|
||||
this.tree_branch_groups_.push(branches);
|
||||
}
|
||||
branches.visible = has_foliage !== true;
|
||||
tree.add(branches);
|
||||
if (has_foliage === true) {
|
||||
tree.add(foliage);
|
||||
@ -3764,21 +3869,35 @@ beestat.component.scene.prototype.get_tree_foliage_state_ = function() {
|
||||
* Apply seasonal foliage appearance to deciduous canopy meshes.
|
||||
*/
|
||||
beestat.component.scene.prototype.update_tree_foliage_season_ = function() {
|
||||
if (this.tree_foliage_meshes_ === undefined || this.tree_foliage_meshes_.length === 0) {
|
||||
const has_foliage_meshes = this.tree_foliage_meshes_ !== undefined && this.tree_foliage_meshes_.length > 0;
|
||||
const has_branch_groups = this.tree_branch_groups_ !== undefined && this.tree_branch_groups_.length > 0;
|
||||
if (has_foliage_meshes === false && has_branch_groups === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
const state = this.get_tree_foliage_state_();
|
||||
for (let i = 0; i < this.tree_foliage_meshes_.length; i++) {
|
||||
const mesh = this.tree_foliage_meshes_[i];
|
||||
if (mesh === undefined || mesh.material === undefined) {
|
||||
continue;
|
||||
if (has_foliage_meshes === true) {
|
||||
for (let i = 0; i < this.tree_foliage_meshes_.length; i++) {
|
||||
const mesh = this.tree_foliage_meshes_[i];
|
||||
if (mesh === undefined || mesh.material === undefined) {
|
||||
continue;
|
||||
}
|
||||
mesh.material.color.copy(state.color);
|
||||
mesh.userData.base_tree_foliage_color = state.color.getHex();
|
||||
mesh.material.opacity = 1;
|
||||
mesh.material.transparent = false;
|
||||
mesh.material.needsUpdate = true;
|
||||
mesh.visible = state.visible;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_branch_groups === true) {
|
||||
for (let i = 0; i < this.tree_branch_groups_.length; i++) {
|
||||
const branch_group = this.tree_branch_groups_[i];
|
||||
if (branch_group !== undefined) {
|
||||
branch_group.visible = state.visible !== true;
|
||||
}
|
||||
}
|
||||
mesh.material.color.copy(state.color);
|
||||
mesh.material.opacity = 1;
|
||||
mesh.material.transparent = false;
|
||||
mesh.material.needsUpdate = true;
|
||||
mesh.visible = state.visible;
|
||||
}
|
||||
};
|
||||
|
||||
@ -3793,6 +3912,7 @@ beestat.component.scene.prototype.add_trees_ = function(ground_surface_z) {
|
||||
tree_group.userData.is_environment = true;
|
||||
this.environment_group_.add(tree_group);
|
||||
this.tree_foliage_meshes_ = [];
|
||||
this.tree_branch_groups_ = [];
|
||||
|
||||
const foliage_enabled = beestat.component.scene.environment_tree_foliage_enabled;
|
||||
|
||||
@ -3831,7 +3951,7 @@ beestat.component.scene.prototype.add_trees_ = function(ground_surface_z) {
|
||||
*/
|
||||
beestat.component.scene.prototype.add_environment_ = function() {
|
||||
const floor_plan = beestat.cache.floor_plan[this.floor_plan_id_];
|
||||
const bounding_box = beestat.floor_plan.get_bounding_box(this.floor_plan_id_);
|
||||
const bounding_box = this.get_scene_bounding_box_();
|
||||
const center_x = (bounding_box.right + bounding_box.left) / 2;
|
||||
const center_y = (bounding_box.bottom + bounding_box.top) / 2;
|
||||
const plan_width = bounding_box.right - bounding_box.left;
|
||||
@ -3886,7 +4006,7 @@ beestat.component.scene.prototype.add_environment_ = function() {
|
||||
mesh.position.z = current_z + stratum.thickness / 2;
|
||||
mesh.userData.is_environment = true;
|
||||
if (index === 0) {
|
||||
mesh.userData.is_ground_surface = true;
|
||||
mesh.userData.is_ground = true;
|
||||
}
|
||||
mesh.receiveShadow = true;
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user