1
0
mirror of https://github.com/beestat/app.git synced 2026-02-26 05:00:21 -05:00

Cleanup Refactor 1

This commit is contained in:
Jon Ziebell 2026-02-22 12:14:59 -05:00
parent 236bf6caa5
commit 5c33d96d1d
14 changed files with 399 additions and 1248 deletions

View File

@ -13,23 +13,6 @@ beestat.component.card.floor_plan_editor = function(thermostat_id) {
beestat.setting('visualize.floor_plan_id')
).x === Infinity;
/* const change_function = beestat.debounce(function() {
// todo replace these with (if entity set active false?)
delete self.state_.active_group;
self.rerender();
// Center the content if the floor plan changed.
if (self.floor_plan_ !== undefined) {
self.floor_plan_.center_content();
}
}, 10);
beestat.dispatcher.addEventListener(
'setting.visualize.floor_plan_id',
change_function
);*/
beestat.component.card.apply(this, arguments);
// Snapping initial
@ -58,6 +41,39 @@ beestat.component.card.floor_plan_editor = function(thermostat_id) {
};
beestat.extend(beestat.component.card.floor_plan_editor, beestat.component.card);
beestat.component.card.floor_plan_editor.layer_type_meta_ = {
'rooms': {
'active_state_key': 'active_room_entity',
'id_key': 'room_id',
'getter_name': 'get_room',
'clears_geometry_selection': true
},
'surfaces': {
'active_state_key': 'active_surface_entity',
'id_key': 'surface_id',
'getter_name': 'get_surface',
'clears_geometry_selection': true
},
'openings': {
'active_state_key': 'active_opening_entity',
'id_key': 'opening_id',
'getter_name': 'get_opening',
'clears_geometry_selection': false
},
'trees': {
'active_state_key': 'active_tree_entity',
'id_key': 'tree_id',
'getter_name': 'get_tree',
'clears_geometry_selection': false
},
'light_sources': {
'active_state_key': 'active_light_source_entity',
'id_key': 'light_source_id',
'getter_name': 'get_light_source',
'clears_geometry_selection': false
}
};
/**
* Decorate.
*
@ -474,58 +490,28 @@ beestat.component.card.floor_plan_editor.prototype.decorate_drawing_pane_ = func
self.floor_plan_.set_width(drawing_canvas_container.getBoundingClientRect().width);
});
// Rerender when stuff happens
this.floor_plan_.addEventListener('add_room', function() {
self.update_floor_plan_();
self.rerender();
});
this.floor_plan_.addEventListener('remove_room', function() {
self.update_floor_plan_();
self.rerender();
});
this.floor_plan_.addEventListener('remove_point', function() {
self.update_floor_plan_();
self.rerender();
});
this.floor_plan_.addEventListener('add_surface', function() {
self.update_floor_plan_();
self.rerender();
});
this.floor_plan_.addEventListener('remove_surface', function() {
self.update_floor_plan_();
self.rerender();
});
this.floor_plan_.addEventListener('add_tree', function() {
self.update_floor_plan_();
self.rerender();
});
this.floor_plan_.addEventListener('remove_tree', function() {
self.update_floor_plan_();
self.rerender();
});
this.floor_plan_.addEventListener('add_opening', function() {
self.update_floor_plan_();
self.rerender();
});
this.floor_plan_.addEventListener('remove_opening', function() {
self.update_floor_plan_();
self.rerender();
});
this.floor_plan_.addEventListener('add_light_source', function() {
self.update_floor_plan_();
self.rerender();
});
this.floor_plan_.addEventListener('remove_light_source', function() {
self.update_floor_plan_();
self.rerender();
});
this.floor_plan_.addEventListener('undo', function() {
self.update_floor_plan_();
self.rerender();
});
this.floor_plan_.addEventListener('redo', function() {
// Rerender when floor-plan model changes.
const rerender_events = [
'add_room',
'remove_room',
'remove_point',
'add_surface',
'remove_surface',
'add_tree',
'remove_tree',
'add_opening',
'remove_opening',
'add_light_source',
'remove_light_source',
'undo',
'redo'
];
const handle_floor_plan_mutation = function() {
self.update_floor_plan_();
self.rerender();
};
rerender_events.forEach(function(event_name) {
self.floor_plan_.addEventListener(event_name, handle_floor_plan_mutation);
});
this.floor_plan_.addEventListener('change_group', self.rerender.bind(this));
@ -887,48 +873,10 @@ beestat.component.card.floor_plan_editor.prototype.set_layer_object_visibility_
object.editor_hidden = visible !== true;
if (visible !== true) {
if (
type === 'rooms' &&
this.state_.active_room_entity !== undefined &&
this.state_.active_room_entity.get_room().room_id === object_id
) {
this.state_.active_room_entity.set_active(false);
}
if (
type === 'surfaces' &&
this.state_.active_surface_entity !== undefined &&
this.state_.active_surface_entity.get_surface().surface_id === object_id
) {
this.state_.active_surface_entity.set_active(false);
}
if (
type === 'trees' &&
this.state_.active_tree_entity !== undefined &&
this.state_.active_tree_entity.get_tree().tree_id === object_id
) {
this.state_.active_tree_entity.set_active(false);
}
if (
type === 'openings' &&
this.state_.active_opening_entity !== undefined &&
this.state_.active_opening_entity.get_opening().opening_id === object_id
) {
this.state_.active_opening_entity.set_active(false);
}
if (
type === 'light_sources' &&
this.state_.active_light_source_entity !== undefined &&
this.state_.active_light_source_entity.get_light_source().light_source_id === object_id
) {
this.state_.active_light_source_entity.set_active(false);
}
this.deactivate_active_entity_for_layer_object_(type, object_id);
}
this.floor_plan_.update_infobox();
this.floor_plan_.update_toolbar();
this.update_info_pane_();
this.update_floor_plan_();
this.rerender();
this.sync_after_layer_change_();
};
/**
@ -948,48 +896,10 @@ beestat.component.card.floor_plan_editor.prototype.set_layer_object_locked_ = fu
object.editor_locked = locked;
if (locked === true) {
if (
type === 'rooms' &&
this.state_.active_room_entity !== undefined &&
this.state_.active_room_entity.get_room().room_id === object_id
) {
this.state_.active_room_entity.set_active(false);
}
if (
type === 'surfaces' &&
this.state_.active_surface_entity !== undefined &&
this.state_.active_surface_entity.get_surface().surface_id === object_id
) {
this.state_.active_surface_entity.set_active(false);
}
if (
type === 'trees' &&
this.state_.active_tree_entity !== undefined &&
this.state_.active_tree_entity.get_tree().tree_id === object_id
) {
this.state_.active_tree_entity.set_active(false);
}
if (
type === 'openings' &&
this.state_.active_opening_entity !== undefined &&
this.state_.active_opening_entity.get_opening().opening_id === object_id
) {
this.state_.active_opening_entity.set_active(false);
}
if (
type === 'light_sources' &&
this.state_.active_light_source_entity !== undefined &&
this.state_.active_light_source_entity.get_light_source().light_source_id === object_id
) {
this.state_.active_light_source_entity.set_active(false);
}
this.deactivate_active_entity_for_layer_object_(type, object_id);
}
this.floor_plan_.update_infobox();
this.floor_plan_.update_toolbar();
this.update_info_pane_();
this.update_floor_plan_();
this.rerender();
this.sync_after_layer_change_();
};
/**
@ -1039,7 +949,7 @@ beestat.component.card.floor_plan_editor.prototype.set_layer_visible_ = function
* @param {boolean} locked
*/
beestat.component.card.floor_plan_editor.prototype.set_group_locked_ = function(group, locked) {
['rooms', 'surfaces', 'openings', 'trees', 'light_sources'].forEach(function(type) {
this.get_layer_types_().forEach(function(type) {
const collection = group[type] || [];
collection.forEach(function(object) {
object.editor_locked = locked;
@ -1047,11 +957,9 @@ beestat.component.card.floor_plan_editor.prototype.set_group_locked_ = function(
});
if (locked === true) {
this.deactivate_active_entity_for_group_type_(group, 'rooms');
this.deactivate_active_entity_for_group_type_(group, 'surfaces');
this.deactivate_active_entity_for_group_type_(group, 'openings');
this.deactivate_active_entity_for_group_type_(group, 'trees');
this.deactivate_active_entity_for_group_type_(group, 'light_sources');
this.get_layer_types_().forEach(function(type) {
this.deactivate_active_entity_for_group_type_(group, type);
}, this);
}
this.sync_after_layer_change_();
@ -1064,7 +972,7 @@ beestat.component.card.floor_plan_editor.prototype.set_group_locked_ = function(
* @param {boolean} visible
*/
beestat.component.card.floor_plan_editor.prototype.set_group_visible_ = function(group, visible) {
['rooms', 'surfaces', 'openings', 'trees', 'light_sources'].forEach(function(type) {
this.get_layer_types_().forEach(function(type) {
const collection = group[type] || [];
collection.forEach(function(object) {
object.editor_hidden = visible !== true;
@ -1072,11 +980,9 @@ beestat.component.card.floor_plan_editor.prototype.set_group_visible_ = function
});
if (visible !== true) {
this.deactivate_active_entity_for_group_type_(group, 'rooms');
this.deactivate_active_entity_for_group_type_(group, 'surfaces');
this.deactivate_active_entity_for_group_type_(group, 'openings');
this.deactivate_active_entity_for_group_type_(group, 'trees');
this.deactivate_active_entity_for_group_type_(group, 'light_sources');
this.get_layer_types_().forEach(function(type) {
this.deactivate_active_entity_for_group_type_(group, type);
}, this);
}
this.sync_after_layer_change_();
@ -1089,38 +995,13 @@ beestat.component.card.floor_plan_editor.prototype.set_group_visible_ = function
* @param {string} type rooms|surfaces|openings|trees
*/
beestat.component.card.floor_plan_editor.prototype.deactivate_active_entity_for_group_type_ = function(group, type) {
if (type === 'rooms' && this.state_.active_room_entity !== undefined) {
if (this.state_.active_room_entity.group_ === group) {
this.state_.active_room_entity.set_active(false);
}
const metadata = this.get_layer_type_meta_(type);
if (metadata === undefined) {
return;
}
if (type === 'surfaces' && this.state_.active_surface_entity !== undefined) {
if (this.state_.active_surface_entity.group_ === group) {
this.state_.active_surface_entity.set_active(false);
}
return;
}
if (type === 'trees' && this.state_.active_tree_entity !== undefined) {
if (this.state_.active_tree_entity.group_ === group) {
this.state_.active_tree_entity.set_active(false);
}
return;
}
if (type === 'openings' && this.state_.active_opening_entity !== undefined) {
if (this.state_.active_opening_entity.group_ === group) {
this.state_.active_opening_entity.set_active(false);
}
return;
}
if (type === 'light_sources' && this.state_.active_light_source_entity !== undefined) {
if (this.state_.active_light_source_entity.group_ === group) {
this.state_.active_light_source_entity.set_active(false);
}
const active_entity = this.state_[metadata.active_state_key];
if (active_entity !== undefined && active_entity.group_ === group) {
active_entity.set_active(false);
}
};
@ -1175,59 +1056,28 @@ beestat.component.card.floor_plan_editor.prototype.reorder_layer_object_ = funct
* Ensure hidden active entities are cleared.
*/
beestat.component.card.floor_plan_editor.prototype.ensure_active_entity_visibility_ = function() {
if (
this.state_.active_room_entity !== undefined &&
(
this.state_.active_room_entity.get_room().editor_hidden === true ||
this.state_.active_room_entity.get_room().editor_locked === true
)
) {
delete this.state_.active_room_entity;
delete this.state_.active_wall_entity;
delete this.state_.active_point_entity;
}
if (
this.state_.active_surface_entity !== undefined &&
(
this.state_.active_surface_entity.get_surface().editor_hidden === true ||
this.state_.active_surface_entity.get_surface().editor_locked === true
)
) {
delete this.state_.active_surface_entity;
delete this.state_.active_wall_entity;
delete this.state_.active_point_entity;
}
if (
this.state_.active_tree_entity !== undefined &&
(
this.state_.active_tree_entity.get_tree().editor_hidden === true ||
this.state_.active_tree_entity.get_tree().editor_locked === true
)
) {
delete this.state_.active_tree_entity;
}
if (
this.state_.active_opening_entity !== undefined &&
(
this.state_.active_opening_entity.get_opening().editor_hidden === true ||
this.state_.active_opening_entity.get_opening().editor_locked === true
)
) {
delete this.state_.active_opening_entity;
}
if (
this.state_.active_light_source_entity !== undefined &&
(
this.state_.active_light_source_entity.get_light_source().editor_hidden === true ||
this.state_.active_light_source_entity.get_light_source().editor_locked === true
)
) {
delete this.state_.active_light_source_entity;
}
this.get_layer_types_().forEach(function(type) {
const metadata = this.get_layer_type_meta_(type);
const active_entity = this.state_[metadata.active_state_key];
if (active_entity === undefined) {
return;
}
const getter = active_entity[metadata.getter_name];
if (typeof getter !== 'function') {
return;
}
const active_object = getter.call(active_entity);
if (
active_object !== undefined &&
(active_object.editor_hidden === true || active_object.editor_locked === true)
) {
delete this.state_[metadata.active_state_key];
if (metadata.clears_geometry_selection === true) {
delete this.state_.active_wall_entity;
delete this.state_.active_point_entity;
}
}
}, this);
};
/**
@ -1261,26 +1111,51 @@ beestat.component.card.floor_plan_editor.prototype.apply_pending_layer_selection
};
/**
* Get the id key by object type.
* Layer metadata by type.
*
* @param {string} type rooms|surfaces|trees
* @param {string} type rooms|surfaces|openings|trees|light_sources
*
* @return {string}
* @return {object|undefined}
*/
beestat.component.card.floor_plan_editor.prototype.get_layer_object_id_key_ = function(type) {
if (type === 'rooms') {
return 'room_id';
beestat.component.card.floor_plan_editor.prototype.get_layer_type_meta_ = function(type) {
return beestat.component.card.floor_plan_editor.layer_type_meta_[type];
};
/**
* Get all supported layer types.
*
* @return {string[]}
*/
beestat.component.card.floor_plan_editor.prototype.get_layer_types_ = function() {
return Object.keys(beestat.component.card.floor_plan_editor.layer_type_meta_);
};
/**
* Deactivate active entity for a specific object id/type.
*
* @param {string} type rooms|surfaces|openings|trees|light_sources
* @param {string} object_id
*/
beestat.component.card.floor_plan_editor.prototype.deactivate_active_entity_for_layer_object_ = function(type, object_id) {
const metadata = this.get_layer_type_meta_(type);
if (metadata === undefined) {
return;
}
if (type === 'surfaces') {
return 'surface_id';
const active_entity = this.state_[metadata.active_state_key];
if (active_entity === undefined) {
return;
}
if (type === 'openings') {
return 'opening_id';
const getter = active_entity[metadata.getter_name];
if (typeof getter !== 'function') {
return;
}
if (type === 'light_sources') {
return 'light_source_id';
const active_object = getter.call(active_entity);
if (
active_object !== undefined &&
active_object[metadata.id_key] === object_id
) {
active_entity.set_active(false);
}
return 'tree_id';
};
/**
@ -1293,10 +1168,13 @@ beestat.component.card.floor_plan_editor.prototype.get_layer_object_id_key_ = fu
* @return {object|undefined}
*/
beestat.component.card.floor_plan_editor.prototype.get_layer_object_by_id_ = function(group, type, object_id) {
const metadata = this.get_layer_type_meta_(type);
if (metadata === undefined) {
return;
}
const collection = group[type] || [];
const id_key = this.get_layer_object_id_key_(type);
for (let i = 0; i < collection.length; i++) {
if (collection[i][id_key] === object_id) {
if (collection[i][metadata.id_key] === object_id) {
return collection[i];
}
}
@ -2570,17 +2448,6 @@ beestat.component.card.floor_plan_editor.prototype.update_floor_plan_ = function
beestat.floor_plan.queue_data_save_(floor_plan_id, 1000);
};
/**
* Get cloned floor plan data.
*
* @param {number} floor_plan_id Floor plan ID
*
* @return {object} The modified floor plan data.
*/
beestat.component.card.floor_plan_editor.prototype.get_floor_plan_data_ = function(floor_plan_id) {
return beestat.clone(beestat.cache.floor_plan[floor_plan_id].data);
};
/**
* Decorate the menu.
*

View File

@ -29,7 +29,6 @@ beestat.component.card.three_d = function() {
};
// Things that update the scene that don't require a rerender.
// TODO these probably need moved to the layer instead of here
beestat.dispatcher.addEventListener(
[
'setting.visualize.data_type',
@ -325,137 +324,74 @@ beestat.component.card.three_d.prototype.decorate_contents_ = function(parent) {
);
}
// Don't go before there's data.
/* required_begin = moment.max(
required_begin,
moment.utc(thermostat.data_begin)
);*/
// Don't go after now.
/* required_end = moment.min(
required_end,
moment().subtract(1, 'hour')
);*/
/**
* If the needed data exists in the database and the runtime_sensor
* cache is empty, then query the data. If the needed data does not exist in
* the database, check every 2 seconds until it does.
*/
// TODO somewhat problematic because I need to check if data is synced from multiple thermostats now
// if (beestat.thermostat.data_synced(this.thermostat_id_, required_begin, required_end) === true) {
const sensor_ids = Object.keys(beestat.floor_plan.get_sensor_ids_map(this.floor_plan_id_));
if (sensor_ids.length > 0) {
if (true) {
if (
beestat.cache.data.three_d__runtime_sensor === undefined ||
beestat.cache.data.three_d__runtime_thermostat === undefined
) {
// console.log('data is undefined need to load it');
this.show_loading_('Fetching');
if (
beestat.cache.data.three_d__runtime_sensor === undefined ||
beestat.cache.data.three_d__runtime_thermostat === undefined
) {
this.show_loading_('Fetching');
const value = [
required_begin.format(),
required_end.format()
];
const operator = 'between';
const value = [
required_begin.format(),
required_end.format()
];
const operator = 'between';
const sensor_ids = Object.keys(beestat.floor_plan.get_sensor_ids_map(this.floor_plan_id_));
const thermostat_ids = Object.keys(beestat.floor_plan.get_thermostat_ids_map(this.floor_plan_id_));
// if (sensor_ids.length > 0) {
const api_call = new beestat.api();
const thermostat_ids = Object.keys(beestat.floor_plan.get_thermostat_ids_map(this.floor_plan_id_));
const api_call = new beestat.api();
// Sensor data
sensor_ids.forEach(function(sensor_id) {
api_call.add_call(
'runtime_sensor',
'read',
{
'attributes': {
'sensor_id': sensor_id,
'timestamp': {
'value': value,
'operator': operator
}
}
},
'runtime_sensor_' + sensor_id
);
});
// Thermostat data
thermostat_ids.forEach(function(thermostat_id) {
api_call.add_call(
'runtime_thermostat',
'read',
{
'attributes': {
'thermostat_id': thermostat_id,
'timestamp': {
'value': value,
'operator': operator
}
}
},
'runtime_thermostat_' + thermostat_id
);
});
api_call.set_callback(function(response) {
let runtime_sensors = [];
let runtime_thermostats = [];
for (let alias in response) {
if (alias.includes('runtime_sensor_') === true) {
runtime_sensors = runtime_sensors.concat(response[alias]);
} else {
runtime_thermostats = runtime_thermostats.concat(response[alias]);
// Sensor data
sensor_ids.forEach(function(sensor_id) {
api_call.add_call(
'runtime_sensor',
'read',
{
'attributes': {
'sensor_id': sensor_id,
'timestamp': {
'value': value,
'operator': operator
}
}
beestat.cache.set('data.three_d__runtime_sensor', runtime_sensors);
beestat.cache.set('data.three_d__runtime_thermostat', runtime_thermostats);
});
},
'runtime_sensor_' + sensor_id
);
});
api_call.send();
// }
} else if (this.has_data_() === false) {
console.info('has data false');
/*chart_container.style('filter', 'blur(3px)');
var no_data = $.createElement('div');
no_data.style({
'position': 'absolute',
'top': 0,
'left': 0,
'width': '100%',
'height': '100%',
'display': 'flex',
'flex-direction': 'column',
'justify-content': 'center',
'text-align': 'center'
});
no_data.innerText('No data to display');
container.appendChild(no_data);*/
}
} else {
this.show_loading_('Syncing');
window.setTimeout(function() {
new beestat.api()
.add_call(
'thermostat',
'read_id',
{
'attributes': {
'inactive': 0
// Thermostat data
thermostat_ids.forEach(function(thermostat_id) {
api_call.add_call(
'runtime_thermostat',
'read',
{
'attributes': {
'thermostat_id': thermostat_id,
'timestamp': {
'value': value,
'operator': operator
}
},
'thermostat'
)
.set_callback(function(response) {
beestat.cache.set('thermostat', response);
self.rerender();
})
.send();
}, 2000);
}
},
'runtime_thermostat_' + thermostat_id
);
});
api_call.set_callback(function(response) {
let runtime_sensors = [];
let runtime_thermostats = [];
for (let alias in response) {
if (alias.includes('runtime_sensor_') === true) {
runtime_sensors = runtime_sensors.concat(response[alias]);
} else {
runtime_thermostats = runtime_thermostats.concat(response[alias]);
}
}
beestat.cache.set('data.three_d__runtime_sensor', runtime_sensors);
beestat.cache.set('data.three_d__runtime_thermostat', runtime_thermostats);
});
api_call.send();
}
}
};
@ -498,7 +434,6 @@ beestat.component.card.three_d.prototype.decorate_drawing_pane_ = function(paren
});
// Set the initial date.
// if (this.has_data_() === true) {
this.update_scene_();
this.scene_.render($(parent));
@ -895,15 +830,6 @@ beestat.component.card.three_d.prototype.apply_weather_setting_to_scene_ = funct
}
};
/**
* Get whether or not this user can access scene settings controls.
*
* @return {boolean}
*/
beestat.component.card.three_d.prototype.can_access_scene_settings_ = function() {
return true;
};
/**
* Ensure local scene settings state exists.
*/
@ -1122,7 +1048,6 @@ beestat.component.card.three_d.prototype.decorate_scene_settings_panel_ = functi
this.scene_settings_container_.innerHTML = '';
this.scene_settings_panel_content_ = undefined;
if (
this.can_access_scene_settings_() !== true ||
this.get_show_environment_() !== true ||
this.scene_settings_menu_open_ !== true
) {
@ -1868,7 +1793,6 @@ beestat.component.card.three_d.prototype.update_fps_visibility_ = function() {
}
const show = (
this.can_access_scene_settings_() === true &&
this.get_show_environment_() === true &&
this.scene_settings_menu_open_ === true
);
@ -1954,7 +1878,7 @@ beestat.component.card.three_d.prototype.decorate_toolbar_ = function(parent) {
);
}
if (this.can_access_scene_settings_() === true && show_environment === true) {
if (show_environment === true) {
tile_group.add_tile(new beestat.component.tile()
.set_icon('tune')
.set_title('Scene Settings')
@ -2560,6 +2484,31 @@ beestat.component.card.three_d.prototype.remove_global_listeners_ = function() {
beestat.dispatcher.removeEventListener('resize.three_d');
};
/**
* Shared teardown path for stale-instance disposal and normal disposal.
*/
beestat.component.card.three_d.prototype.teardown_ = function() {
if (this.rerender_timeout_id_ !== undefined) {
window.clearTimeout(this.rerender_timeout_id_);
this.rerender_timeout_id_ = undefined;
this.rerender_pending_delay_ms_ = undefined;
}
this.rerender_waiting_for_visibility_ = false;
if (this.visibility_observer_ !== undefined) {
this.visibility_observer_.disconnect();
this.visibility_observer_ = undefined;
}
this.hide_loading_();
window.clearInterval(this.fps_interval_);
delete this.fps_interval_;
this.remove_global_listeners_();
if (this.scene_ !== undefined) {
this.scene_.dispose();
delete this.scene_;
}
};
/**
* Force teardown for stale card instances that were not formally disposed.
*/
@ -2569,49 +2518,12 @@ beestat.component.card.three_d.prototype.force_dispose_stale_instance_ = functio
}
this.disposed_ = true;
if (this.rerender_timeout_id_ !== undefined) {
window.clearTimeout(this.rerender_timeout_id_);
this.rerender_timeout_id_ = undefined;
this.rerender_pending_delay_ms_ = undefined;
}
this.rerender_waiting_for_visibility_ = false;
if (this.visibility_observer_ !== undefined) {
this.visibility_observer_.disconnect();
this.visibility_observer_ = undefined;
}
this.hide_loading_();
window.clearInterval(this.fps_interval_);
delete this.fps_interval_;
this.remove_global_listeners_();
if (this.scene_ !== undefined) {
this.scene_.dispose();
delete this.scene_;
}
this.teardown_();
};
beestat.component.card.three_d.prototype.dispose = function() {
this.disposed_ = true;
if (this.rerender_timeout_id_ !== undefined) {
window.clearTimeout(this.rerender_timeout_id_);
this.rerender_timeout_id_ = undefined;
this.rerender_pending_delay_ms_ = undefined;
}
this.rerender_waiting_for_visibility_ = false;
if (this.visibility_observer_ !== undefined) {
this.visibility_observer_.disconnect();
this.visibility_observer_ = undefined;
}
this.hide_loading_();
window.clearInterval(this.fps_interval_);
delete this.fps_interval_;
this.remove_global_listeners_();
if (this.scene_ !== undefined) {
this.scene_.dispose();
delete this.scene_;
}
this.teardown_();
if (beestat.component.card.three_d.active_instance_ === this) {
delete beestat.component.card.three_d.active_instance_;
}

View File

@ -256,3 +256,80 @@ beestat.component.floor_plan_entity.prototype.get_x = function() {
beestat.component.floor_plan_entity.prototype.get_y = function() {
return this.y_;
};
/**
* Collect snap x/y coordinates from selected shape collections.
*
* @param {{
* groups: object[],
* shape_specs: Array<{collection: string, point_mode: string}>,
* should_skip_shape: (function(object, object, object): boolean)=
* }} options
*
* @return {{snap_x: number[], snap_y: number[]}}
*/
beestat.component.floor_plan_entity.prototype.collect_snap_points_ = function(options) {
const snap_x = {};
const snap_y = {};
const groups = Array.isArray(options.groups) ? options.groups : [];
const shape_specs = Array.isArray(options.shape_specs) ? options.shape_specs : [];
const should_skip_shape = typeof options.should_skip_shape === 'function'
? options.should_skip_shape
: function() {
return false;
};
groups.forEach(function(group) {
if (group === undefined || group === null) {
return;
}
shape_specs.forEach(function(shape_spec) {
const shapes = group[shape_spec.collection];
if (Array.isArray(shapes) !== true) {
return;
}
shapes.forEach(function(shape) {
if (shape === undefined || shape.editor_hidden === true) {
return;
}
if (should_skip_shape(shape, shape_spec, group) === true) {
return;
}
if (shape_spec.point_mode === 'point') {
snap_x[Number(shape.x || 0)] = true;
snap_y[Number(shape.y || 0)] = true;
return;
}
if (Array.isArray(shape.points) !== true) {
return;
}
shape.points.forEach(function(point) {
const point_x = Number(point.x || 0);
const point_y = Number(point.y || 0);
if (shape_spec.point_mode === 'absolute') {
snap_x[point_x] = true;
snap_y[point_y] = true;
return;
}
snap_x[point_x + Number(shape.x || 0)] = true;
snap_y[point_y + Number(shape.y || 0)] = true;
});
});
});
});
return {
'snap_x': Object.keys(snap_x).map(function(key) {
return Number(key);
}),
'snap_y': Object.keys(snap_y).map(function(key) {
return Number(key);
})
};
};

View File

@ -308,66 +308,42 @@ beestat.component.floor_plan_entity.light_source.prototype.after_mouseup_handler
*/
beestat.component.floor_plan_entity.light_source.prototype.update_snap_points_ = function() {
const self = this;
const snap_x = {};
const snap_y = {};
const append_shapes = function(shapes, skip_self_light_source) {
if (Array.isArray(shapes) !== true) {
return;
}
shapes.forEach(function(shape) {
if (shape.editor_hidden === true) {
return;
const group_below = this.floor_plan_.get_group_below(this.group_);
const groups = [this.group_];
if (group_below !== undefined) {
groups.push(group_below);
}
const snap_points = this.collect_snap_points_({
'groups': groups,
'shape_specs': [
{
'collection': 'rooms',
'point_mode': 'relative'
},
{
'collection': 'surfaces',
'point_mode': 'relative'
},
{
'collection': 'openings',
'point_mode': 'absolute'
},
{
'collection': 'light_sources',
'point_mode': 'point'
}
if (
skip_self_light_source === true &&
],
'should_skip_shape': function(shape, shape_spec) {
return (
shape_spec.collection === 'light_sources' &&
shape.light_source_id !== undefined &&
self.light_source_ !== undefined &&
self.light_source_.light_source_id === shape.light_source_id
) {
return;
}
if (Array.isArray(shape.points) === true) {
shape.points.forEach(function(point) {
const is_opening = shape.opening_id !== undefined;
const absolute_x = is_opening
? Number(point.x || 0)
: Number(point.x || 0) + Number(shape.x || 0);
const absolute_y = is_opening
? Number(point.y || 0)
: Number(point.y || 0) + Number(shape.y || 0);
snap_x[absolute_x] = true;
snap_y[absolute_y] = true;
});
} else {
snap_x[Number(shape.x || 0)] = true;
snap_y[Number(shape.y || 0)] = true;
}
});
};
append_shapes(this.group_.rooms, false);
append_shapes(this.group_.surfaces, false);
append_shapes(this.group_.openings, false);
append_shapes(this.group_.light_sources, true);
const group_below = this.floor_plan_.get_group_below(this.group_);
if (group_below !== undefined) {
append_shapes(group_below.rooms, false);
append_shapes(group_below.surfaces, false);
append_shapes(group_below.openings, false);
append_shapes(group_below.light_sources, false);
}
this.snap_x_ = Object.keys(snap_x).map(function(key) {
return Number(key);
});
this.snap_y_ = Object.keys(snap_y).map(function(key) {
return Number(key);
);
}
});
this.snap_x_ = snap_points.snap_x;
this.snap_y_ = snap_points.snap_y;
};
/**

View File

@ -441,58 +441,39 @@ beestat.component.floor_plan_entity.opening.prototype.clear_snap_lines_ = functi
*/
beestat.component.floor_plan_entity.opening.prototype.update_snap_points_ = function() {
const self = this;
const snap_x = {};
const snap_y = {};
const append_shapes = function(shapes, skip_self_opening) {
if (Array.isArray(shapes) !== true) {
return;
}
shapes.forEach(function(shape) {
if (shape.editor_hidden === true || Array.isArray(shape.points) !== true) {
return;
const group_below = this.floor_plan_.get_group_below(this.group_);
const groups = [this.group_];
if (group_below !== undefined) {
groups.push(group_below);
}
const snap_points = this.collect_snap_points_({
'groups': groups,
'shape_specs': [
{
'collection': 'rooms',
'point_mode': 'relative'
},
{
'collection': 'surfaces',
'point_mode': 'relative'
},
{
'collection': 'openings',
'point_mode': 'absolute'
}
if (
skip_self_opening === true &&
],
'should_skip_shape': function(shape, shape_spec) {
return (
shape_spec.collection === 'openings' &&
self.opening_ !== undefined &&
shape.opening_id !== undefined &&
self.opening_.opening_id !== undefined &&
shape.opening_id === self.opening_.opening_id
) {
return;
}
shape.points.forEach(function(point) {
const is_opening = shape.opening_id !== undefined;
const absolute_x = is_opening
? Number(point.x || 0)
: Number(point.x || 0) + Number(shape.x || 0);
const absolute_y = is_opening
? Number(point.y || 0)
: Number(point.y || 0) + Number(shape.y || 0);
snap_x[absolute_x] = true;
snap_y[absolute_y] = true;
});
});
};
append_shapes(this.group_.rooms, false);
append_shapes(this.group_.surfaces, false);
append_shapes(this.group_.openings, true);
const group_below = this.floor_plan_.get_group_below(this.group_);
if (group_below !== undefined) {
append_shapes(group_below.rooms, false);
append_shapes(group_below.surfaces, false);
append_shapes(group_below.openings, false);
}
this.snap_x_ = Object.keys(snap_x).map(function(key) {
return Number(key);
});
this.snap_y_ = Object.keys(snap_y).map(function(key) {
return Number(key);
);
}
});
this.snap_x_ = snap_points.snap_x;
this.snap_y_ = snap_points.snap_y;
};
/**

View File

@ -195,9 +195,6 @@ beestat.component.floor_plan_entity.prototype.decorate_walls_ = function(parent)
wall_entity.addEventListener('mousedown', function() {
wall_entity.set_active(true);
});
wall_entity.addEventListener('mousedown', function() {
wall_entity.set_active(true);
});
// Add toolbar button on activate.
wall_entity.addEventListener('activate', function() {
@ -305,60 +302,26 @@ beestat.component.floor_plan_entity.room.prototype.set_active = function(active)
* Pre-generate a list of snappable x/y values.
*/
beestat.component.floor_plan_entity.room.prototype.update_snap_points_ = function() {
const snap_x = {};
const snap_y = {};
// Snap to rooms in this group.
this.group_.rooms.forEach(function(room) {
if (room.editor_hidden === true) {
return;
}
room.points.forEach(function(point) {
snap_x[point.x + room.x] = true;
snap_y[point.y + room.y] = true;
});
});
(this.group_.openings || []).forEach(function(opening) {
if (opening.editor_hidden === true || Array.isArray(opening.points) !== true) {
return;
}
opening.points.forEach(function(point) {
// Opening points are stored in absolute editor coordinates.
snap_x[point.x] = true;
snap_y[point.y] = true;
});
});
// Snap to rooms in the group under this one.
const group_below = this.floor_plan_.get_group_below(this.group_);
const groups = [this.group_];
if (group_below !== undefined) {
group_below.rooms.forEach(function(room) {
if (room.editor_hidden === true) {
return;
}
room.points.forEach(function(point) {
snap_x[point.x + room.x] = true;
snap_y[point.y + room.y] = true;
});
});
(group_below.openings || []).forEach(function(opening) {
if (opening.editor_hidden === true || Array.isArray(opening.points) !== true) {
return;
}
opening.points.forEach(function(point) {
// Opening points are stored in absolute editor coordinates.
snap_x[point.x] = true;
snap_y[point.y] = true;
});
});
groups.push(group_below);
}
this.snap_x_ = Object.keys(snap_x).map(function(key) {
return Number(key);
});
this.snap_y_ = Object.keys(snap_y).map(function(key) {
return Number(key);
const snap_points = this.collect_snap_points_({
'groups': groups,
'shape_specs': [
{
'collection': 'rooms',
'point_mode': 'relative'
},
{
'collection': 'openings',
'point_mode': 'absolute'
}
]
});
this.snap_x_ = snap_points.snap_x;
this.snap_y_ = snap_points.snap_y;
};
/**

View File

@ -165,43 +165,28 @@ beestat.component.floor_plan_entity.surface.prototype.set_active = function(acti
* Pre-generate a list of snappable x/y values.
*/
beestat.component.floor_plan_entity.surface.prototype.update_snap_points_ = function() {
const snap_x = {};
const snap_y = {};
const append_shapes = function(shapes) {
if (shapes === undefined) {
return;
}
shapes.forEach(function(shape) {
if (shape.editor_hidden === true || Array.isArray(shape.points) !== true) {
return;
}
shape.points.forEach(function(point) {
const is_opening = shape.opening_id !== undefined;
const absolute_x = is_opening ? Number(point.x || 0) : Number(point.x || 0) + Number(shape.x || 0);
const absolute_y = is_opening ? Number(point.y || 0) : Number(point.y || 0) + Number(shape.y || 0);
snap_x[absolute_x] = true;
snap_y[absolute_y] = true;
});
});
};
append_shapes(this.group_.rooms);
append_shapes(this.group_.surfaces);
append_shapes(this.group_.openings);
const group_below = this.floor_plan_.get_group_below(this.group_);
const groups = [this.group_];
if (group_below !== undefined) {
append_shapes(group_below.rooms);
append_shapes(group_below.surfaces);
append_shapes(group_below.openings);
groups.push(group_below);
}
this.snap_x_ = Object.keys(snap_x).map(function(key) {
return Number(key);
});
this.snap_y_ = Object.keys(snap_y).map(function(key) {
return Number(key);
const snap_points = this.collect_snap_points_({
'groups': groups,
'shape_specs': [
{
'collection': 'rooms',
'point_mode': 'relative'
},
{
'collection': 'surfaces',
'point_mode': 'relative'
},
{
'collection': 'openings',
'point_mode': 'absolute'
}
]
});
this.snap_x_ = snap_points.snap_x;
this.snap_y_ = snap_points.snap_y;
};

View File

@ -112,34 +112,6 @@ beestat.component.scene.room_floor_thickness = 6;
*/
beestat.component.scene.surface_z_lift = 0.75;
/**
* Default number of decorative trees to place around the environment.
*
* @type {number}
*/
beestat.component.scene.environment_tree_count = 14;
/**
* Toggle tree foliage visibility for environment trees.
*
* @type {boolean}
*/
beestat.component.scene.environment_tree_foliage_enabled = true;
/**
* Debug opacity for round/oval canopies when foliage is visible.
*
* @type {number}
*/
beestat.component.scene.debug_tree_canopy_opacity = 1;
/**
* Keep round/oval branch meshes visible even when foliage is visible.
*
* @type {boolean}
*/
beestat.component.scene.debug_show_branches_with_foliage = true;
/**
* Round/oval primary branch density in branches per height unit.
*
@ -221,27 +193,6 @@ beestat.component.scene.sun_light_intensity = 0.6;
*/
beestat.component.scene.moon_light_intensity = 0.13125;
/**
* Peak per-room interior light intensity used at night.
*
* @type {number}
*/
beestat.component.scene.interior_light_intensity = 0.9;
/**
* Max number of interior point lights allowed to cast shadows.
*
* @type {number}
*/
beestat.component.scene.interior_light_shadow_max = 1;
/**
* Number of star sprites generated in the sky dome.
*
* @type {number}
*/
beestat.component.scene.star_count = 900;
/**
* Minimum star sprite size.
*
@ -428,7 +379,6 @@ beestat.component.scene.prototype.rerender = function() {
this.add_main_group_();
this.add_floor_plan_();
}.bind(this));
this.apply_appearance_rotation_to_lights_();
// Ensure everything gets updated with the latest info.
if (this.rendered_ === true) {
@ -550,8 +500,6 @@ beestat.component.scene.prototype.reset_celestial_lights_for_rerender_ = functio
delete this.celestial_light_group_;
delete this.sun_light_;
delete this.moon_light_;
delete this.sun_light_helper_;
delete this.moon_light_helper_;
delete this.sun_path_line_;
delete this.sun_visual_group_;
delete this.sun_core_mesh_;
@ -591,19 +539,6 @@ beestat.component.scene.prototype.get_scene_setting_ = function(key) {
return beestat.component.scene.default_settings[key];
};
/**
* Get all currently effective scene settings.
*
* @return {object}
*/
beestat.component.scene.prototype.get_scene_settings = function() {
const current_settings = Object.assign({}, beestat.component.scene.default_settings);
if (this.scene_settings_ !== undefined) {
Object.assign(current_settings, this.scene_settings_);
}
return current_settings;
};
/**
* Update scene settings.
*
@ -695,25 +630,12 @@ beestat.component.scene.prototype.decorate_ = function(parent) {
this.scene_settings_ = {};
}
this.debug_ = {
'axes': false,
'directional_light_helpers': false,
'sun_light_helper': false,
'moon_light_helper': false,
'watcher': false,
'roof_edges': false,
'straight_skeleton': false,
'openings': false,
'opening_cutters': false,
'hide_tree_branches': false,
'light_source_orbs': false
};
this.room_interaction_enabled_ = true;
this.width_ = this.initial_width_ || this.state_.scene_width || 800;
this.height_ = 500;
this.add_scene_(parent);
this.add_scene_();
this.add_renderer_(parent);
this.add_camera_();
this.add_controls_(parent);
@ -783,37 +705,9 @@ beestat.component.scene.prototype.set_initial_camera_state = function(camera_sta
/**
* Add the scene. Everything gets added to the scene.
*
* @param {rocket.Elements} parent Parent
*/
beestat.component.scene.prototype.add_scene_ = function(parent) {
beestat.component.scene.prototype.add_scene_ = function() {
this.scene_ = new THREE.Scene();
if (this.debug_.axes === true) {
this.scene_.add(
new THREE.AxesHelper(800)
.setColors(
0xff0000,
0x00ff00,
0x0000ff
)
);
}
if (this.debug_.watcher === true) {
this.debug_info_ = {};
this.debug_container_ = $.createElement('div').style({
'position': 'absolute',
'top': (beestat.style.size.gutter / 2),
'left': (beestat.style.size.gutter / 2),
'padding': (beestat.style.size.gutter / 2),
'background': 'rgba(0, 0, 0, 0.5)',
'color': '#fff',
'font-family': 'Consolas, Courier, Monospace',
'white-space': 'pre'
});
parent.appendChild(this.debug_container_);
}
};
/**
@ -1034,185 +928,6 @@ beestat.component.scene.prototype.update_ = function() {
}
this.update_tree_foliage_season_();
// Update debug watcher
if (this.debug_.watcher === true) {
this.debug_info_.sun_light_intensity = this.sun_light_ !== undefined ? this.sun_light_.intensity.toFixed(3) : 'N/A';
this.debug_info_.moon_light_intensity = this.moon_light_ !== undefined ? this.moon_light_.intensity.toFixed(3) : 'N/A';
this.update_debug_();
}
};
/**
* Add a helpful debug window that can be refreshed with the contents of
* this.debug_info_.
*
* @param {rocket.Elements} parent
*/
beestat.component.scene.prototype.add_debug_ = function(parent) {
if (this.debug_.watcher === true) {
this.debug_info_ = {};
this.debug_container_ = $.createElement('div').style({
'position': 'absolute',
'top': (beestat.style.size.gutter / 2),
'left': (beestat.style.size.gutter / 2),
'padding': (beestat.style.size.gutter / 2),
'background': 'rgba(0, 0, 0, 0.5)',
'color': '#fff',
'font-family': 'Consolas, Courier, Monospace',
'white-space': 'pre'
});
parent.appendChild(this.debug_container_);
}
};
/**
* Update the debug window.
*/
beestat.component.scene.prototype.update_debug_ = function() {
if (this.debug_.watcher === true) {
this.debug_container_.innerHTML(
JSON.stringify(this.debug_info_, null, 2)
);
}
};
/**
* Add red outline visualization for exposed ceiling areas (future roof locations).
*/
beestat.component.scene.prototype.add_roof_outline_debug_ = function() {
const floor_plan = beestat.cache.floor_plan[this.floor_plan_id_];
const exposed_areas = this.compute_exposed_ceiling_areas_(floor_plan);
// Create layer for roof outlines
const roof_outlines_layer = new THREE.Group();
this.floor_plan_group_.add(roof_outlines_layer);
this.layers_['roof_outlines'] = roof_outlines_layer;
// Render each exposed area as red outline
exposed_areas.forEach(function(area) {
area.polygons.forEach(function(polygon) {
if (polygon.length < 3) {
return;
}
// Create line points
const points = [];
polygon.forEach(function(point) {
points.push(new THREE.Vector3(point.x, point.y, area.ceiling_z));
});
// Close the loop
points.push(new THREE.Vector3(polygon[0].x, polygon[0].y, area.ceiling_z));
// Create red line
const geometry = new THREE.BufferGeometry().setFromPoints(points);
const material = new THREE.LineBasicMaterial({
'color': 0xff0000, // Red
'linewidth': 2
});
const line = new THREE.Line(geometry, material);
line.layers.set(beestat.component.scene.layer_visible);
roof_outlines_layer.add(line);
});
});
};
/**
* Visualize the straight skeleton for each roof polygon with debug lines.
*/
beestat.component.scene.prototype.add_roof_skeleton_debug_ = function() {
const skeleton_builder = this.get_skeleton_builder_();
if (skeleton_builder === undefined) {
return;
}
const floor_plan = beestat.cache.floor_plan[this.floor_plan_id_];
const exposed_areas = this.compute_exposed_ceiling_areas_(floor_plan);
// Create layer for skeleton debug lines
const skeleton_debug_layer = new THREE.Group();
this.floor_plan_group_.add(skeleton_debug_layer);
this.layers_['roof_skeleton_debug'] = skeleton_debug_layer;
let total_polygons = 0;
let successful_skeletons = 0;
// Process each exposed area
exposed_areas.forEach(function(area) {
area.polygons.forEach(function(polygon) {
if (polygon.length < 3) {
return;
}
total_polygons++;
try {
// Simplify polygon to remove self-intersections and clean up topology
// This splits complex polygons (L-shapes, T-shapes) into simpler ones
const simplified = ClipperLib.Clipper.SimplifyPolygon(
polygon,
ClipperLib.PolyFillType.pftNonZero
);
// SimplifyPolygon can return multiple polygons if the original was self-intersecting
simplified.forEach(function(simple_polygon) {
if (simple_polygon.length < 3) {
return;
}
// Convert ClipperLib format {x, y} to SkeletonBuilder format [[x, y], ...]
const ring = simple_polygon.map(function(point) {
return [point.x, point.y];
});
// Close the ring by repeating the first point
ring.push([simple_polygon[0].x, simple_polygon[0].y]);
// Build the straight skeleton
const coordinates = [ring]; // Array of rings (outer ring only, no holes)
const result = skeleton_builder.buildFromPolygon(coordinates);
if (!result) {
return;
}
successful_skeletons++;
// Visualize each skeleton polygon face with blue lines
result.polygons.forEach(function(face) {
if (face.length < 2) {
return;
}
// Create line points from the face vertices
const points = [];
face.forEach(function(vertex_index) {
const vertex = result.vertices[vertex_index];
points.push(new THREE.Vector3(vertex[0], vertex[1], area.ceiling_z));
});
// Close the loop
const first_vertex = result.vertices[face[0]];
points.push(new THREE.Vector3(first_vertex[0], first_vertex[1], area.ceiling_z));
// Create blue line for skeleton edges
const geometry = new THREE.BufferGeometry().setFromPoints(points);
const material = new THREE.LineBasicMaterial({
'color': 0x00ffff, // Cyan
'linewidth': 1
});
const line = new THREE.Line(geometry, material);
line.layers.set(beestat.component.scene.layer_visible);
skeleton_debug_layer.add(line);
});
}); // End simplified.forEach
} catch (error) {
console.error('Error building skeleton for polygon:', error, polygon);
}
});
});
};
/**

View File

@ -66,9 +66,6 @@ beestat.component.scene.prototype.update_tree_foliage_season_ = function() {
}
mesh.material.color.copy(state.color);
mesh.userData.base_tree_foliage_color = state.color.getHex();
mesh.material.opacity = beestat.component.scene.debug_tree_canopy_opacity;
mesh.material.transparent = beestat.component.scene.debug_tree_canopy_opacity < 1;
mesh.material.depthWrite = beestat.component.scene.debug_tree_canopy_opacity >= 1;
mesh.material.needsUpdate = true;
mesh.visible = tree_foliage_enabled === true;
}
@ -78,11 +75,7 @@ beestat.component.scene.prototype.update_tree_foliage_season_ = function() {
for (let i = 0; i < this.tree_branch_groups_.length; i++) {
const branch_group = this.tree_branch_groups_[i];
if (branch_group !== undefined) {
// Hide branches when canopy is visible; show them when canopy is not visible.
// Debug override can force branch meshes hidden at all times.
branch_group.visible =
this.debug_.hide_tree_branches !== true &&
tree_branch_enabled === true;
branch_group.visible = tree_branch_enabled === true;
}
}
}

View File

@ -91,18 +91,7 @@ beestat.component.scene.prototype.add_floor_plan_ = function() {
self.add_walls_(walls_layer, group);
});
let opening_cutter_debug_layer;
if (this.debug_.opening_cutters === true) {
opening_cutter_debug_layer = new THREE.Group();
this.floor_plan_group_.add(opening_cutter_debug_layer);
this.layers_['opening_cutters_debug'] = opening_cutter_debug_layer;
}
this.apply_opening_cuts_(
walls_layer,
floor_plan,
opening_cutter_debug_layer
);
this.apply_opening_cuts_(walls_layer, floor_plan);
const openings_layer = new THREE.Group();
this.floor_plan_group_.add(openings_layer);
@ -119,27 +108,9 @@ beestat.component.scene.prototype.add_floor_plan_ = function() {
self.add_light_sources_(light_sources_layer, group);
});
if (this.debug_.openings === true) {
const openings_debug_layer = new THREE.Group();
this.floor_plan_group_.add(openings_debug_layer);
this.layers_['openings_debug'] = openings_debug_layer;
floor_plan.data.groups.forEach(function(group) {
self.add_openings_debug_(openings_debug_layer, group);
});
}
// Add roofs using straight skeleton
this.add_roofs_();
if (this.debug_.roof_edges) {
this.add_roof_outline_debug_();
}
if (this.debug_.straight_skeleton) {
this.add_roof_skeleton_debug_();
}
this.add_environment_();
};

View File

@ -40,15 +40,6 @@ beestat.component.scene.prototype.add_directional_lights_ = function() {
this.static_light_group_.add(top_light);
this.directional_lights_.push(top_light);
// Add helpers for debugging
if (this.debug_.directional_light_helpers === true) {
this.directional_light_helpers_ = [];
this.directional_lights_.forEach((light) => {
const helper = new THREE.DirectionalLightHelper(light, 100);
this.static_light_group_.add(helper);
this.directional_light_helpers_.push(helper);
});
}
};
@ -80,7 +71,6 @@ beestat.component.scene.prototype.add_static_lights_ = function() {
// Add directional fill lights
this.add_directional_lights_();
this.apply_appearance_rotation_to_lights_();
};
@ -209,14 +199,6 @@ beestat.component.scene.prototype.add_celestial_lights_ = function() {
this.sun_glow_sprite_.renderOrder = 11;
this.sun_visual_group_.add(this.sun_glow_sprite_);
if (this.debug_.sun_light_helper === true) {
this.sun_light_helper_ = new THREE.DirectionalLightHelper(
this.sun_light_,
100
);
this.celestial_light_group_.add(this.sun_light_helper_);
}
// Moon light
this.moon_light_ = new THREE.DirectionalLight(
0xaaccff, // Cool bluish color for moonlight
@ -258,17 +240,7 @@ beestat.component.scene.prototype.add_celestial_lights_ = function() {
this.moon_sprite_.scale.set(405, 405, 1);
this.moon_visual_group_.add(this.moon_sprite_);
if (this.debug_.moon_light_helper === true) {
this.moon_light_helper_ = new THREE.DirectionalLightHelper(
this.moon_light_,
100
);
this.celestial_light_group_.add(this.moon_light_helper_);
}
this.add_stars_();
this.apply_appearance_rotation_to_lights_();
};
@ -397,17 +369,6 @@ beestat.component.scene.prototype.update_sun_path_arc_ = function(date, latitude
};
/**
* Static (ambient/directional fill) lights should not rotate with floor-plan
* appearance. Celestial lights are handled in update_celestial_lights_.
*/
beestat.component.scene.prototype.apply_appearance_rotation_to_lights_ = function() {
if (this.static_light_group_ !== undefined) {
this.static_light_group_.rotation.y = 0;
}
};
/**
* Build target sun colors from altitude.
* Warmer near horizon and more neutral when the sun is high.
@ -506,12 +467,6 @@ beestat.component.scene.prototype.update_celestial_lights_ = function(date, lati
Math.min(1, (-sun_pos.altitude - 0.05) / 0.25)
);
const interior_night_factor = Math.max(
0,
Math.min(1, (-sun_pos.altitude + 0.03) / 0.3)
);
this.target_interior_light_intensity_ =
beestat.component.scene.interior_light_intensity * interior_night_factor;
const max_sun_intensity = Math.max(0.0001, Number(beestat.component.scene.sun_light_intensity || 0.0001));
const normalized_sun_brightness = Math.max(
0,
@ -576,18 +531,6 @@ beestat.component.scene.prototype.update_celestial_lights_ = function(date, lati
: moon_intensity;
}
this.target_moon_intensity_ *= cloud_dimming;
// Update helpers
if (this.debug_.sun_light_helper) {
this.sun_light_.updateMatrixWorld();
this.sun_light_.target.updateMatrixWorld();
this.sun_light_helper_.update();
}
if (this.debug_.moon_light_helper) {
this.moon_light_.updateMatrixWorld();
this.moon_light_.target.updateMatrixWorld();
this.moon_light_helper_.update();
}
};
@ -607,14 +550,6 @@ beestat.component.scene.prototype.update_celestial_light_intensities_ = function
if (this.target_moon_intensity_ === undefined) {
this.target_moon_intensity_ = 0;
}
if (this.target_interior_light_intensity_ === undefined) {
const hour = this.date_ !== undefined ? Number(this.date_.format('H')) : 12;
this.target_interior_light_intensity_ = (
(hour >= 19 || hour <= 5)
? beestat.component.scene.interior_light_intensity
: 0
);
}
if (this.target_light_source_intensity_multiplier_ === undefined) {
const hour = this.date_ !== undefined ? Number(this.date_.format('H')) : 12;
this.target_light_source_intensity_multiplier_ = (hour >= 19 || hour <= 5) ? 1 : 0;
@ -640,11 +575,6 @@ beestat.component.scene.prototype.update_celestial_light_intensities_ = function
const color_lerp_factor = 0.08;
this.sun_light_.color.lerp(this.target_sun_light_color_, color_lerp_factor);
if (this.interior_lights_ !== undefined) {
this.interior_lights_.forEach((light) => {
light.intensity += (this.target_interior_light_intensity_ - light.intensity) * lerp_factor;
});
}
if (Array.isArray(this.light_sources_) === true) {
this.light_sources_.forEach((light) => {
const base_intensity = Number(light.userData.base_intensity || 0);
@ -789,29 +719,6 @@ beestat.component.scene.prototype.add_light_sources_ = function(layer, group) {
this.light_sources_ = [];
}
if (this.debug_.light_source_orbs === true) {
if (this.light_source_marker_geometry_ === undefined) {
this.light_source_marker_geometry_ = new THREE.SphereGeometry(2.2, 12, 12);
}
if (this.light_source_glow_geometry_ === undefined) {
this.light_source_glow_geometry_ = new THREE.SphereGeometry(6, 16, 16);
}
if (this.light_source_marker_material_ === undefined) {
this.light_source_marker_material_ = new THREE.MeshStandardMaterial({
'roughness': 0.2,
'metalness': 0.05
});
}
if (this.light_source_glow_material_ === undefined) {
this.light_source_glow_material_ = new THREE.MeshBasicMaterial({
'transparent': true,
'opacity': 0.28,
'depthWrite': false,
'blending': THREE.AdditiveBlending
});
}
}
const group_elevation = Number(group.elevation || 0);
const floor_thickness = Number(beestat.component.scene.room_floor_thickness || 0);
const user_light_cast_shadows = this.get_scene_setting_('light_user_cast_shadows') === true;
@ -830,33 +737,6 @@ beestat.component.scene.prototype.add_light_sources_ = function(layer, group) {
const light_intensity = 0.9 * intensity_level;
const light_color = this.get_light_color_from_temperature_(light_source.temperature_k);
if (this.debug_.light_source_orbs === true) {
const marker = new THREE.Mesh(
this.light_source_marker_geometry_,
this.light_source_marker_material_.clone()
);
marker.material.color.copy(light_color);
marker.material.emissive.copy(light_color);
marker.material.emissiveIntensity = 0.9 + (intensity_level * 0.35);
marker.position.set(x, y, z);
marker.castShadow = false;
marker.receiveShadow = false;
marker.userData.is_light_source = true;
layer.add(marker);
const glow = new THREE.Mesh(
this.light_source_glow_geometry_,
this.light_source_glow_material_.clone()
);
glow.material.color.copy(light_color);
glow.material.opacity = 0.15 + (intensity_level * 0.08);
glow.position.set(x, y, z);
glow.castShadow = false;
glow.receiveShadow = false;
glow.userData.is_light_source = true;
layer.add(glow);
}
const light = new THREE.PointLight(light_color, light_intensity, 240, 2);
light.userData.base_intensity = light_intensity;
light.intensity = light_intensity * Number(this.target_light_source_intensity_multiplier_ || 0);
@ -910,61 +790,4 @@ beestat.component.scene.prototype.update_user_light_shadow_settings_ = function(
};
/**
* Add warm interior point lights, one per room. Lights are invisible and their
* intensity is animated based on night/day state.
*
* @param {object} floor_plan The floor plan data.
*/
beestat.component.scene.prototype.add_interior_lights_ = function(floor_plan) {
this.interior_lights_ = [];
this.interior_light_group_ = new THREE.Group();
this.floor_plan_group_.add(this.interior_light_group_);
this.layers_['interior_lights'] = this.interior_light_group_;
let shadowed_light_count = 0;
floor_plan.data.groups.forEach(function(group) {
group.rooms.forEach((room) => {
if (room.points === undefined || room.points.length < 3) {
return;
}
const geojson_polygon = [];
room.points.forEach(function(point) {
geojson_polygon.push([
point.x,
point.y
]);
});
const light_point = polylabel([geojson_polygon]);
const group_elevation = Number(group.elevation || 0);
const room_height = Number(room.height || group.height || 96);
const room_elevation = Number(room.elevation !== undefined ? room.elevation : group_elevation);
const light = new THREE.PointLight(0xffd79a, 0, 170, 2);
light.position.set(
Number(room.x || 0) + light_point[0],
Number(room.y || 0) + light_point[1],
-room_elevation - (room_height * 0.45)
);
if (shadowed_light_count < beestat.component.scene.interior_light_shadow_max) {
light.castShadow = true;
light.shadow.mapSize.width = 512;
light.shadow.mapSize.height = 512;
light.shadow.bias = -0.0012;
light.shadow.normalBias = 0.025;
light.shadow.radius = 2;
light.shadow.camera.near = 1;
light.shadow.camera.far = 220;
shadowed_light_count++;
} else {
light.castShadow = false;
}
this.interior_light_group_.add(light);
this.interior_lights_.push(light);
});
}, this);
};

View File

@ -160,40 +160,15 @@ beestat.component.scene.prototype.get_opening_center_z_ = function(group, openin
};
/**
* Add a debug wireframe for an opening cutter.
*
* @param {THREE.Group} layer The debug layer.
* @param {THREE.Mesh} cutter The cutter mesh.
*/
beestat.component.scene.prototype.add_opening_cutter_debug_ = function(layer, cutter) {
const edges_geometry = new THREE.EdgesGeometry(cutter.geometry);
const wireframe = new THREE.LineSegments(
edges_geometry,
new THREE.LineBasicMaterial({
'color': 0xff7700
})
);
wireframe.position.copy(cutter.position);
wireframe.rotation.copy(cutter.rotation);
wireframe.scale.copy(cutter.scale);
wireframe.layers.set(beestat.component.scene.layer_visible);
layer.add(wireframe);
};
/**
* Subtract opening cutters from wall meshes.
*
* @param {THREE.Group} walls_layer The wall mesh layer.
* @param {object} floor_plan The floor plan data.
* @param {THREE.Group=} opening_cutter_debug_layer Optional debug cutter layer.
*/
beestat.component.scene.prototype.apply_opening_cuts_ = function(
walls_layer,
floor_plan,
opening_cutter_debug_layer
floor_plan
) {
if (window.CSG === undefined || typeof window.CSG.subtract !== 'function') {
return;
@ -227,10 +202,6 @@ beestat.component.scene.prototype.apply_opening_cuts_ = function(
return;
}
if (opening_cutter_debug_layer !== undefined) {
this.add_opening_cutter_debug_(opening_cutter_debug_layer, cutter);
}
const cutter_box = new THREE.Box3().setFromObject(cutter);
group_wall_meshes.forEach(function(wall_mesh) {
@ -280,50 +251,6 @@ beestat.component.scene.prototype.apply_opening_cuts_ = function(
};
/**
* Add red wireframe boxes to visualize opening placement in 3D.
*
* @param {THREE.Group} layer The layer to add opening debug to.
* @param {object} group The floor plan group.
*/
beestat.component.scene.prototype.add_openings_debug_ = function(layer, group) {
if (group.openings === undefined || group.openings.length === 0) {
return;
}
const wall_thickness = beestat.component.scene.wall_thickness;
group.openings.forEach(function(opening) {
const opening_line = this.get_opening_line_params_(opening);
const width = opening_line.width;
const height = Math.max(1, Number(opening.height || this.get_opening_default_height_(opening.type)));
const center_z = this.get_opening_center_z_(group, opening, height);
const geometry = new THREE.BoxGeometry(
width,
wall_thickness,
height
);
const edges_geometry = new THREE.EdgesGeometry(geometry);
const wireframe = new THREE.LineSegments(
edges_geometry,
new THREE.LineBasicMaterial({
'color': 0xff0000
})
);
wireframe.position.x = opening_line.center_x;
wireframe.position.y = opening_line.center_y;
wireframe.position.z = center_z;
wireframe.rotation.z = opening_line.rotation_radians;
wireframe.layers.set(beestat.component.scene.layer_visible);
layer.add(wireframe);
}, this);
};
/**
* Add 3D opening fixtures.
*

View File

@ -670,15 +670,11 @@ beestat.component.scene.prototype.create_round_tree_ = function(height, max_diam
const branch_axis = new THREE.Vector3(0, 0, -1);
const foliage = new THREE.Group();
foliage.userData.is_environment = true;
const canopy_opacity = beestat.component.scene.debug_tree_canopy_opacity;
const foliage_material = new THREE.MeshStandardMaterial({
'color': 0x4f9f2f,
'roughness': 0.82,
'metalness': 0.0,
'flatShading': true,
'transparent': canopy_opacity < 1,
'opacity': canopy_opacity,
'depthWrite': canopy_opacity >= 1,
'side': THREE.DoubleSide
});
const create_canopy_from_branch_function_ = function() {
@ -1063,8 +1059,7 @@ beestat.component.scene.prototype.create_round_tree_ = function(height, max_diam
if (has_foliage === true) {
this.tree_branch_groups_.push(branches);
}
branches.visible =
this.debug_.hide_tree_branches !== true;
branches.visible = true;
tree.add(branches);
if (has_foliage === true) {
tree.add(foliage);

View File

@ -3,40 +3,6 @@
*/
/**
* Set weather on the floor-plan appearance.
*
* @param {string} weather
*
* @return {beestat.component.scene}
*/
beestat.component.scene.prototype.set_weather = function(weather) {
const floor_plan = beestat.cache.floor_plan[this.floor_plan_id_];
if (floor_plan.data.appearance === undefined) {
floor_plan.data.appearance = {};
}
const normalized_weather = beestat.weather.get_settings_(weather).condition;
floor_plan.data.appearance.weather = normalized_weather;
const weather_settings = {
'cloud_density': beestat.weather.get_cloud_density(normalized_weather),
'cloud_darkness': beestat.weather.get_cloud_darkness(normalized_weather),
'rain_density': beestat.weather.get_rain_density(normalized_weather),
'snow_density': beestat.weather.get_snow_density(normalized_weather),
'lightning_frequency': beestat.weather.get_lightning_frequency(normalized_weather),
'wind_speed': beestat.weather.get_wind_speed(normalized_weather)
};
this.set_scene_settings(weather_settings, {
'rerender': false
});
if (this.rendered_ === true) {
this.update_();
}
return this;
};
/**
* Get design count at density 1 for a weather channel.
*