diff --git a/index.html b/index.html
index 1b8316e..0f9caae 100644
--- a/index.html
+++ b/index.html
@@ -76,7 +76,9 @@
diff --git a/src/app/grid-coord.js b/src/app/grid-coord.js
new file mode 100644
index 0000000..b0d9ff4
--- /dev/null
+++ b/src/app/grid-coord.js
@@ -0,0 +1,48 @@
+/* global AFRAME */
+/**
+ * change position of element by it's grid coordinates
+ *
+ */
+AFRAME.registerComponent('grid-coord', {
+ schema: {
+ gridCoord: {type: 'string', default: ''},
+ gridSize: {type: 'number', default: 5},
+ gridDivisions: {type: 'number', default: 20}
+ },
+ init: function () {
+ const data = this.data;
+ const el = this.el;
+
+ if (!data.gridCoord) { return; }
+
+ [this.lon, this.lat] = data.gridCoord.split(',');
+
+ const gridPos = this.stateToLocalGrid(data.gridSize, data.gridDivisions);
+
+ el.setAttribute('position', gridPos);
+
+ },
+ // Convert long / lat state grid coordinates to local grid position
+ stateToLocalGrid: function (gridSize, gridDivisions) {
+ // grid divisions (# of cells) ie 20 divisions
+ // grid size (meter) ie 5 meters
+ // cellsPerMeter: gridDivisions / gridSize = 4 cells per meter
+ const cellsPerMeter = gridDivisions / gridSize;
+ const snap = this.el.getAttribute('snap');
+ let snapOffset = (snap) ? snap['offset'] : 0.01;
+
+ const [posLong, posLat] =
+ [this.lon, this.lat].map((strVal) => {
+ const numVal = Number(strVal);
+ // add - or + snap offset is used to snap element to the desired grid cell
+ return (numVal + (numVal >= 0 ? -snapOffset : snapOffset )) / cellsPerMeter;
+ });
+ // console.log(`Lat Y: ${posLat}, Long X: ${posLong}`);
+
+ const localPos = {
+ x: posLong,
+ z: posLat * -1
+ };
+ return localPos;
+ }
+});
\ No newline at end of file
diff --git a/src/app/grid-model.js b/src/app/grid-model.js
new file mode 100644
index 0000000..0a3f169
--- /dev/null
+++ b/src/app/grid-model.js
@@ -0,0 +1,25 @@
+/* global AFRAME */
+/**
+ * change gltf-model of element by model name
+ *
+ */
+AFRAME.registerComponent('grid-model', {
+ schema: {
+ model: {type: 'string', default: ''}
+ },
+ addGltfModel: function (modelName) {
+ const el = this.el;
+ const sceneEl = el.sceneEl;
+
+ if (sceneEl.catalogIsloaded) {
+ el.setAttribute('gltf-model', './' + sceneEl.systems.state.state.model.list[modelName].dist);
+ } else {
+ sceneEl.addEventListener('catalogIsLoaded', () => {
+ el.setAttribute('gltf-model', './' + sceneEl.systems.state.state.model.list[modelName].dist);
+ })
+ }
+ },
+ update: function(oldData) {
+ this.addGltfModel(this.data.model);
+ }
+});
diff --git a/src/app/grid-rotation.js b/src/app/grid-rotation.js
new file mode 100644
index 0000000..490ce2b
--- /dev/null
+++ b/src/app/grid-rotation.js
@@ -0,0 +1,16 @@
+/* global AFRAME */
+/**
+ * change rotation of element by rotation value of y-axis
+ *
+ */
+AFRAME.registerComponent('grid-rotation', {
+ schema: {
+ rotation: {type: 'number', default: 0}
+ },
+ update: function(oldData) {
+ const data = this.data;
+ const el = this.el;
+
+ el.setAttribute('rotation', {x:0, y: data.rotation, z: 0});
+ }
+});
\ No newline at end of file
diff --git a/src/app/intersection-spawn.js b/src/app/intersection-spawn.js
index 45b2af4..adbe5ba 100644
--- a/src/app/intersection-spawn.js
+++ b/src/app/intersection-spawn.js
@@ -31,7 +31,6 @@ AFRAME.registerComponent('intersection-spawn', {
const localPos = _worldToLocal(evt.detail.intersection.point, targetEl); // convert world intersection position to local position
const gridPos = this.localToStateGrid(localPos, data.gridSize, data.gridDivisions);
- // console.log('Grid Position:', gridPos);
AFRAME.scenes[0].emit('addGridObject', {
lon: gridPos.x,
diff --git a/src/app/load-model.js b/src/app/load-model.js
deleted file mode 100644
index 53b2eec..0000000
--- a/src/app/load-model.js
+++ /dev/null
@@ -1,60 +0,0 @@
-/* global AFRAME */
-/**
- * add model to scene ground by its name and grid coordinats.
- *
- * `` will spawn
- * `` at intersection point.
- */
-AFRAME.registerComponent('add-model', {
- schema: {
- name: {type: 'string', default: ''},
- gridCoord: {type: 'array', default: []},
- //lat: {type: 'number', default: ''},
- gridSize: {type: 'number', default: 5},
- gridDivisions: {type: 'number', default: 20}
- },
-
- init: function () {
- const data = this.data;
- const el = this.el;
- const sceneEl = el.sceneEl;
- this.helperVector = new THREE.Vector3();
-
- if (!data.name && data.gridCoord) { return; }
-
- const gridPos = this.stateToLocalGrid(data.gridCoord, data.gridSize, data.gridDivisions);
-
- el.setAttribute('position', gridPos);
-
- if (sceneEl.catalogIsloaded) {
- el.setAttribute('gltf-model', './' + sceneEl.systems.state.state.model.list[data.name].dist);
- } else {
- sceneEl.addEventListener('catalogIsLoaded', () => {
- el.setAttribute('gltf-model', './' + sceneEl.systems.state.state.model.list[data.name].dist);
- })
- }
- },
- // Convert long / lat state grid coordinates to local grid position
- stateToLocalGrid: function (lon_lat, gridSize, gridDivisions) {
- // grid divisions (# of cells) ie 20 divisions
- // grid size (meter) ie 5 meters
- // cellsPerMeter: gridDivisions / gridSize = 4 cells per meter
- const cellsPerMeter = gridDivisions / gridSize;
- const snap = this.el.getAttribute('snap');
- let snapOffset = (snap) ? snap['offset'] : 0.01;
-
- const [posLong, posLat] =
- lon_lat.map((strVal) => {
- const numVal = Number(strVal);
- // add - or + snap offset is used to snap element to the desired grid cell
- return (numVal + (numVal >= 0 ? -snapOffset : snapOffset )) / cellsPerMeter;
- });
- // console.log(`Lat Y: ${posLat}, Long X: ${posLong}`);
-
- const localPos = {
- x: posLong,
- z: posLat * -1
- };
- return localPos;
- }
-});
\ No newline at end of file
diff --git a/src/app/state.js b/src/app/state.js
index b5f4a91..e0628ed 100644
--- a/src/app/state.js
+++ b/src/app/state.js
@@ -36,11 +36,14 @@ const GRID_SIZE = 10; // Example grid size (10x10)
// Initial grid state setup with City Hall at the center
const INITIAL_GRID_STATE = [];
+
const INIT_GRID_COORDS = [[-1, -1], [-1, 1], [1, -1], [1, 1]];
-INIT_GRID_COORDS.forEach(lon_lat_values => {
+INIT_GRID_COORDS.forEach(coord => {
+ const lon = coord[0];
+ const lat = coord[1];
INITIAL_GRID_STATE.push(
- {coord: lon_lat_values, model: 'park_base', rotation: 0, elevation: 0}
+ {coord: coord.join(','), model: 'park_base', rotation: 0, elevation: 0}
);
});
@@ -103,7 +106,10 @@ AFRAME.registerState({
let { lon, lat, model, rotation, elevation } = payload;
- if (isCityHall(lon, lat)) return;
+ if (isCityHall(lon, lat)) {
+ console.log('Cannot place object on city hall');
+ return;
+ }
if (!model) {
model = getModelNameFromState(state);
@@ -117,39 +123,56 @@ AFRAME.registerState({
console.log('model', model);
const keyCell = findGridIndex(state.grid, lon, lat);
- // Prevent modification of City Hall cells and exists cells
if (!keyCell) {
+ // If the cell is free add selected model to the spawned cell
state.grid.push(
- { coord: [lon, lat], model: model, rotation: rotation, elevation: elevation }
+ { coord: [lon, lat].join(','), model: model, rotation: rotation, elevation: elevation }
);
} else {
- console.log('Cannot place object on city hall');
+
+ if (state.grid[keyCell].model === model) {
+ // if the cell model is the same as the selected model
+ this.rotateOrElevateGridObject(state, {keyCell, addRotation: 90});
+ } else {
+ // change the current model of element to selected model
+ // Setting the __dirty flag is needed to track changes
+ // in the value of individual elements, but not the entire array
+ state.grid.__dirty = true;
+ state.grid[keyCell].model = model;
+ state.grid[keyCell].rotation = 0;
+ }
}
console.log('Updated Grid State:', state.grid);
},
// Handler to rotate or elevate an object
// Note: City Hall cells cannot be rotated or elevated
rotateOrElevateGridObject: function (state, payload) {
- const { lon, lat, rotation, elevation } = payload;
+ let { lon, lat, addRotation, addElevation, keyCell } = payload;
if (isCityHall(lon, lat)) return;
- const keyCell = findGridIndex(state.grid, lon, lat);
+ keyCell = keyCell ?? findGridIndex(state.grid, lon, lat);
- if (keyCell) {
-
- state.grid[keyCell].rotation += rotation;
- state.grid[keyCell].elevation += elevation;
+ if (addRotation) {
+ // Setting the __dirty flag is needed to track changes
+ // in the value of individual elements, but not the entire array
+ state.grid.__dirty = true;
+ const currentRot = state.grid[keyCell].rotation;
+ // prevent big rotation values
+ state.grid[keyCell].rotation = (currentRot + addRotation) % 360;
}
+
+ if (addElevation) state.grid[keyCell].elevation += addElevation;
+
},
// Handler to clear a cell
// Note: City Hall cells cannot be cleared
clearGridCell: function (state, payload) {
- const { lon, lat } = payload;
+ const { lon, lat, keyCell } = payload;
if (isCityHall(lon, lat)) return;
- const keyCell = findGridIndex(state.grid, lon, lat);
+ keyCell = keyCell ?? findGridIndex(state.grid, lon, lat);
if (keyCell) {
state.grid.splice(keyCell, 1);
@@ -168,16 +191,21 @@ AFRAME.registerState({
// check for City Hall cells
function isCityHall(lon, lat) {
+ if (! lon && lat) return false;
+
const cityHallCell = INIT_GRID_COORDS.findIndex(
- (elData) => elData[0] == lon && elData[1] == lat
+ (coord) => coord[0] == lon && coord[1] == lat
);
return (cityHallCell == -1) ? false: true;
}
// find index of grid array element by grid coordinates
function findGridIndex(gridArray, lon, lat) {
+ if (! lon && lat) return;
+ const strCoord = [lon, lat].join(',');
+
const gridArrayIndex = gridArray.findIndex(
- (elData) => elData.coord[0] == lon && elData.coord[1] == lat
+ (elData) => elData.coord == strCoord
);
return gridArrayIndex === -1 ? null : gridArrayIndex;
}
@@ -225,6 +253,7 @@ AFRAME.registerComponent('load-catalog', {
}
this.el.catalogIsloaded = true;
this.el.emit('catalogIsLoaded');
+
}
});
diff --git a/src/index.js b/src/index.js
index 788c23a..8a982db 100644
--- a/src/index.js
+++ b/src/index.js
@@ -12,4 +12,6 @@ require('./app/intersection-spawn.js');
require('./app/snap.js');
require('./app/set-rotation-from-anchor.js');
require('./app/state.js');
-require('./app/load-model.js');
\ No newline at end of file
+require('./app/grid-coord.js');
+require('./app/grid-model.js');
+require('./app/grid-rotation.js');
\ No newline at end of file