Allow point delete in mapbox draw mod - mapbox

I am trying to rewrite a MapboxDraw.modes.draw_line_string.clickAnywhere function to allow deletion of a previous point with the SHIFT key in draw_line_string mode.
It works, but when it removes the previous point it creates a new one on the new place.
Draw a line
Hold shift and click on any previous point
Will delete the old one create a new one
I want to delete the old point and continue adding new points
An example is here
https://jsfiddle.net/benderlio/5fxwhkdp/7/
MapboxDraw.modes.draw_line_string.clickAnywhere = function (state, e) {
if (e.originalEvent.shiftKey) {
const line = state.line.coordinates;
const lastClick = line[line.length - 1]
const a = map.project(lastClick)
const res = line.map((i, index) => {
const b = map.project(i)
const d = distance(a, b)
return {
index,
distance: d
};
}).find(i => i.distance < 5 && i.distance > 0)
console.log('res', res);
if (res) {
//state.line.removeCoordinate(res.index)
state.line.coordinates = state.line.coordinates.filter((i, index) => index !== res.index)
}
} else {
console.log('NO SHiFT');
if (state.currentVertexPosition > 0 && isEventAtCoordinates(e, state.line.coordinates[state.currentVertexPosition - 1]) ||
state.direction === 'backwards' && isEventAtCoordinates(e, state.line.coordinates[state.currentVertexPosition + 1])) {
return this.changeMode('simple_select', { featureIds: [state.line.id] });
}
this.updateUIClasses({ mouse: 'add' });
state.line.updateCoordinate(state.currentVertexPosition, e.lngLat.lng, e.lngLat.lat);
if (state.direction === 'forward') {
state.currentVertexPosition++;
state.line.updateCoordinate(state.currentVertexPosition, e.lngLat.lng, e.lngLat.lat);
} else {
state.line.addCoordinate(0, e.lngLat.lng, e.lngLat.lat);
}
}
};
UPD: ok, the point is to set currentVertexPosition
state.line.coordinates.splice(res.index,1)
state.currentVertexPosition = state.currentVertexPosition - 1

Related

I cannot get the html doc to load the javascript to make my calculator working

I've followed this calculator video on YouTube. I have 3 files in a folder .html, .css & .js.
I open the calculator by double-clicking the HTML file which seems to load up fine in opera with the CSS changing the appearance, however the functionality of the buttons do not work at all. Now I have double-checked the code and even gone as far as cheating with the resource material to be sure it's not an error in the code. I feel that the JavaScript isn't being read/used at all when it loads up.
https://www.youtube.com/watch?v=j59qQ7YWLxw
class calculator {
constructor(perviousOperandTextElement, currentOperandTextElement) {
this.perviousOperandTextElement = perviousOperandTextElement
this.currentOperandTextElement = currentOperandTextElement
this.clear()
}
clear() {
this.currentOperand = ''
this.perviousOperand = ''
this.operation = undefined
}
delete() {
this.currentOperand = this.currentOperand.toString().slice(0, -1)
}
appendNumber(number) {
if (number === '.' && this.currentOperand.includes('.')) return
this.currentOperand = this.currentOperand.toString() + number.tostring()
}
chooseOperation(operation) {
if (this.currentOperand === '') return
if (this.perviousOperand !== '') {
this.compute()
}
this.operation = operation
this.perviousOperand = this.currentOperand
this.currentOperand = ''
}
compute() {
let computation
const prev = parseFloat(this.previousOperand)
const current = parseFloat(this.currentOperand)
if (isNaN(prev) || isNaN(current)) return
switch (this.operation) {
case'+':
computation = prev + current
break
case '-':
computation = prev - current
break
case '*':
computation = prev * current
break
case 'รท':
computation = prev / current
break
default:
return
}
this.currentOperand = computation
this.operation = undefined
this.perviousOperand = ''
}
getDisplayNumber(number) {
const stringNumber = number.toString()
const integerDigits = parseFloat(stringNumber.split('.')[0])
const decimalDigits = stringNumber.split('.')[1]
let integerDisplay
if (isNaN(integerDigits)) {
integerDisplay = ''
} else {
integerDisplay integerDigits.toLocaleString('en', { maximumFractionDigits: 0 })
}
if (decimalDigits != null) {
return `${integerDisplay}.${decimalDigits}`
} else {
return integerDisplay
}
}
updateDisplay() {
this.currentOperandTextElement.innerText =
this.getDisplayNumber(this.currentOperand)
if (this.operation != null) {
this.previousOperandTextElement.innerText =
`${this.getDisplayNumber(this.perviousOperand)} ${this.operation}`
} else {
this.previousOperandTextElement.innerText = ''
}
}
}
const numberButtons = document.querySelectorAll('[data-number]')
const operationButtons = document.querySelectorAll('[data-operation]')
const equalsButton = document.querySelector('[data-equals]')
const deleteButton = document.querySelector('[data-delete]')
const allClearButton = document.querySelector('[data-all-clear]')
const previousOperandTextElement = document.querySelector('[data-previous-operand]')
const currentOperandTextElement = document.querySelector('[data-current-operand]')
const calculator = new calculator(previousOperandTextElement, currentOperandTextElement)
numberButtons.forEach(button => {
button.addEventListener('click', () => {
calculator.appendNumber(button.innerText)
calculator.updateDisplay()
})
})
operationButtons.forEach(button => {
button.addEventListener('click', () => {
calculator.chooseOperation(button.innerText)
calculator.updateDisplay()
})
})
equalsButton.addEventListener('click', button => {
calculator.compute()
calculator.updateDisplay()
})
allClearButton.addEventListener('click', button => {
calculator.clear()
calculator.updateDisplay()
})
deleteButton.addEventListener('click', button => {
calculator.delete()
calculator.updateDisplay()
})
Your javascript code is wrong at line 67.
We can see that at line 67, your code writes
integerDisplay integerDigits.toLocaleString('en', { maximumFractionDigits: 0 })
This is clearly wrong as you are missing a =, which assigns the value. A solution to this is to replace the line with
integerDisplay = integerDigits.toLocaleString('en', { maximumFractionDigits: 0 })

Updating data doesnt expand the data tree inside material-table

Im trying to build a table with nested tree folder inside.
When trying to add nested data into the datasource data the structure will not updated and will not toggle anymore.
Code below:
https://stackblitz.com/edit/angular-table-tree-example-k2zqmt?file=app%2Ftable-basic-example.ts&file=app%2Ftable-basic-example.html,app%2Ftable-basic-example.ts
Environment
Angular:
Material Table
Material tree system
These are the things that are happening when logNode method is called
The item is getting added but the treeControl.toggle method does not work anymore.
When you are assigning a new dataset to the dataSource all the nodes get reset and the tree closes, so this.treeControl.toggle is trying to toggle a node that does not exist.
You need to find the node to be toggled from the list you get from treeControl.dataNodes
I would suggest having the toggle code in a separate method and adding a node code in a separate method, and a separate button to add the node.
The below code should work for your scenario, also remove this line from your HTML, (click)="treeControl.toggle(data)"
interface ExampleFlatNode {
expandable: boolean;
RoleName: string;
Access: boolean;
level: number;
CatId: number;
}
private transformer = (node: FoodNode, level: number) => {
return {
expandable:
!!node.CategoryPermissions && node.CategoryPermissions.length > 0,
RoleName: node.RoleName,
Access: node.Access,
level: level,
CatId: node.CatId,
};
};
tempNodes = []
constructor() {
this.dataSource.data = TREE_DATA;
}
logNode(clickedNode) {
this.tempNodes = [];
this.treeControl.dataNodes.forEach((node) =>
this.tempNodes.push({
...node,
expanded: this.treeControl.isExpanded(node),
})
);
if (!this.treeControl.isExpanded(clickedNode)) {
const temp = {
Access: true,
RoleName: 'test 1 2',
CatId: 113,
};
const clickedNodeIdx = this.treeControl.dataNodes.findIndex(
(node: any) =>
node.CatId === clickedNode.CatId &&
node.RoleName === clickedNode.RoleName &&
node.level === clickedNode.level
);
const childIdx = 1;
let child;
if (clickedNode.level === 0) {
child =
this.dataSource.data[clickedNodeIdx].CategoryPermissions[childIdx];
} else {
this.dataSource.data.forEach(
(item) => (child = this.findDataSource(item, clickedNode))
);
}
child.CategoryPermissions.push(temp);
this.dataSource.data = this.dataSource.data;
const addedNode = this.treeControl.dataNodes.find(
(node: any) =>
node.CatId === temp.CatId && node.RoleName === temp.RoleName
);
this.expandParent(addedNode);
this.setPreviousState();
} else {
this.treeControl.collapse(clickedNode);
}
}
findDataSource(item, node) {
if (item.RoleName === node.RoleName) {
return item;
} else if (item.CategoryPermissions) {
let matchedItem;
item.CategoryPermissions.forEach((e) => {
const temp = this.findDataSource(e, node);
if (temp) {
matchedItem = temp;
}
});
return matchedItem;
}
}
setPreviousState() {
for (let i = 0, j = 0; i < this.treeControl.dataNodes.length; i++) {
if (
this.tempNodes[j] &&
this.treeControl.dataNodes[i].RoleName === this.tempNodes[j].RoleName &&
this.treeControl.dataNodes[i].CatId === this.tempNodes[j].CatId &&
this.treeControl.dataNodes[i].level === this.tempNodes[j].level
) {
if (this.tempNodes[j].expanded) {
this.treeControl.expand(this.treeControl.dataNodes[i]);
}
j++;
}
}
}
expandParent(node: ExampleFlatNode) {
const { treeControl } = this;
const currentLevel = treeControl.getLevel(node);
const index = treeControl.dataNodes.indexOf(node) - 1;
for (let i = index; i >= 0; i--) {
const currentNode = treeControl.dataNodes[i];
if (currentLevel === 0) {
this.treeControl.expand(currentNode);
return null;
}
if (treeControl.getLevel(currentNode) < currentLevel) {
this.treeControl.expand(currentNode);
this.expandParent(currentNode);
break;
}
}
}

Render lit / lit-html TemplateResult as string

In lit/lit-html/lit-element, a standard component is the TemplateResult (usually HTMLTemplateResult), created like:
function renderMe(msg) {
return html`<div>Hello ${msg}!</div>`;
}
and of course the power and efficiency of the library is that subsequent calls will reuse the same <div> Element and only replace the changed fragments.
For testing the renderMe() function above, however, it would be helpful to be able to see the return value as a standard string, like:
assert.equal(RENDER_AS_STRING(renderMe('kimiko')), '<div>Hello kimiko!</div>');
and fix any bugs in the function before testing how it renders into the browser itself.
Is there a function like RENDER_AS_STRING either in lit itself or in a testing library? I have searched and not found one.
The result of execution contains html strings and values that alternate:
We can combine them in the same order:
function renderMe(msg) {
return html`<div>Hello ${msg}!</div>`;
}
const getRenderString = (data) => {
const {strings, values} = data;
const v = [...values, ''] // + last emtpty part
return strings.reduce((acc,s, i) => acc + s + v[i], '')
}
console.log(getRenderString(renderMe('SO')))
You can test it in the playground
And the recursive version
import {html, css, LitElement} from 'lit';
function renderMe(msg) {
return html`<p>Hello ${msg}!</p>`;
}
function renderBlock(msg) {
return html`<div>${renderMe(msg)}</div>`;
}
const getRenderString = (data) => {
const {strings, values} = data;
const v = [...values, ''].map(e => typeof e === 'object' ? getRenderString(e) : e )
return strings.reduce((acc,s, i) => acc + s + v[i], '')
}
document.getElementById('output').textContent = getRenderString(renderBlock('SO'))
#Daniil Loban's answer works great if the arguments are strings, but if they might themselves be TemplateResults or arrays of TemplateResults (which are all allowed by spec), it needs a more complex answer:
export function template_as_string(data) {
const {strings, values} = data;
const value_list = [...values, '']; // + last empty part
let output = '';
for (let i = 0; i < strings.length; i++) {
let v = value_list[i];
if (v._$litType$ !== undefined) {
v = template_as_string(v); // embedded Template
} else if (v instanceof Array) {
// array of strings or templates.
let new_v = '';
for (const inner_v of [...v]) {
new_v += template_as_string(inner_v);
}
v = new_v;
}
output += strings[i] + v;
}
return output;
}

efficient way to determine Quad Tree neighbors for Quad Sphere Face edges?

I've been trying to optimise how I lookup the neighbors for the Quad Tree faces in my Quad Sphere's Top and Bottom faces with the rest of the faces. I've attempted several methods to determine neighbors, where the latest one improved the lookup speed, but I'm wondering if there is something better
Method 1:
Keep a lookup table of all users of all vertices used by all Quads and then, for each Quad, find any other Quads that aren't ancestors that share edge vertices with the original Quad (minus the corner vertices because these are shared by multiple, non-neighbors). This works great for low numbers of subdivisions and vertices, but as each of these increases, the performance becomes much worse.
See example here of this implementation: https://github.com/bicarbon8/QuadSphere/blob/master/Assets/Scripts/QuadVertMap.cs#L104
Method 2:
Keep a lookup table of all Quads at each Level of subdivision, indexed by level and then for each Quad, find any other Quads at either the same level or one level less (parents level) that aren't ancestors and check their edge vertices to see if they match with the original Quad's edge vertices. This works better than Method 1, but still starts to suffer if you get too deep in the levels of subdivision. This looks like the following code snippet:
public Quad FindNeighbor(Quad quad, EdgeType edge)
{
Vector3[] edgeVerts = quad.GetWorldVerts(quad.GetEdgeVerts(edge));
int level = quad.GetLevel(); // neighbors can only be equal or 1 lower level
List<Quad> potentialNeighbors = Quads[level].Where(n => n != quad).ToList();
if (potentialNeighbors.Any())
{
foreach (Quad potentialNeighbor in potentialNeighbors)
{
var topEdge = potentialNeighbor.GetWorldVerts(potentialNeighbor.GetEdgeVerts(EdgeType.Top));
if (topEdge.All(v => edgeVerts.Contains(v)))
{
return potentialNeighbor;
}
var bottomEdge = potentialNeighbor.GetWorldVerts(potentialNeighbor.GetEdgeVerts(EdgeType.Bottom));
if (bottomEdge.All(v => edgeVerts.Contains(v)))
{
return potentialNeighbor;
}
var leftEdge = potentialNeighbor.GetWorldVerts(potentialNeighbor.GetEdgeVerts(EdgeType.Left));
if (leftEdge.All(v => edgeVerts.Contains(v)))
{
return potentialNeighbor;
}
var rightEdge = potentialNeighbor.GetWorldVerts(potentialNeighbor.GetEdgeVerts(EdgeType.Right));
if (rightEdge.All(v => edgeVerts.Contains(v)))
{
return potentialNeighbor;
}
}
}
if (level > 0)
{
// if we made it this far we haven't found a neighbor yet so try 1 level lower Quads
potentialNeighbors = Quads[level - 1].Where(n => n != quad.GetParent()).ToList();
if (potentialNeighbors.Any())
{
foreach (Quad potentialNeighbor in potentialNeighbors)
{
var topEdge = potentialNeighbor.GetWorldVerts(potentialNeighbor.GetEdgeVerts(EdgeType.Top));
if (topEdge.Any(v => edgeVerts.Contains(v)))
{
return potentialNeighbor;
}
var bottomEdge = potentialNeighbor.GetWorldVerts(potentialNeighbor.GetEdgeVerts(EdgeType.Bottom));
if (bottomEdge.Any(v => edgeVerts.Contains(v)))
{
return potentialNeighbor;
}
var leftEdge = potentialNeighbor.GetWorldVerts(potentialNeighbor.GetEdgeVerts(EdgeType.Left));
if (leftEdge.Any(v => edgeVerts.Contains(v)))
{
return potentialNeighbor;
}
var rightEdge = potentialNeighbor.GetWorldVerts(potentialNeighbor.GetEdgeVerts(EdgeType.Right));
if (rightEdge.Any(v => edgeVerts.Contains(v)))
{
return potentialNeighbor;
}
}
}
}
return null;
}
Is there anyone who has experience with this and is willing to share some other means of optimising the lookup? Thanks in advance.
What I've ended up doing, since this post didn't receive any responses, is assigning the sibling neighbors based on some basic rules and then for the non-sibling neighbors I locate the parent quad, get their neighbor children and see if any of them share an edge with this quad
private void AddNeighbors()
{
switch (QuadType)
{
case QuadType.BottomLeft:
// add siblings
AddNeighbor(EdgeType.Top, () => { return GetParent().GetChild(QuadType.TopLeft); });
AddNeighbor(EdgeType.Right, () => { return GetParent().GetChild(QuadType.BottomRight); });
// add non-siblings
AddNeighbor(EdgeType.Bottom, () =>
{
return GetParent().GetNeighbor(EdgeType.Bottom)?.GetChildren()?.FirstOrDefault(c => c != null && HasSharedEdge(EdgeType.Bottom, c));
});
AddNeighbor(EdgeType.Left, () =>
{
return GetParent().GetNeighbor(EdgeType.Left)?.GetChildren()?.FirstOrDefault(c => c != null && HasSharedEdge(EdgeType.Left, c));
});
break;
case QuadType.BottomRight:
// add siblings
AddNeighbor(EdgeType.Top, () => { return GetParent().GetChild(QuadType.TopRight); });
AddNeighbor(EdgeType.Left, () => { return GetParent().GetChild(QuadType.BottomLeft); });
// add non-siblings
AddNeighbor(EdgeType.Bottom, () =>
{
return GetParent().GetNeighbor(EdgeType.Bottom)?.GetChildren()?.FirstOrDefault(c => c != null && HasSharedEdge(EdgeType.Bottom, c));
});
AddNeighbor(EdgeType.Right, () =>
{
return GetParent().GetNeighbor(EdgeType.Right)?.GetChildren()?.FirstOrDefault(c => c != null && HasSharedEdge(EdgeType.Right, c));
});
break;
case QuadType.TopLeft:
// add siblings
AddNeighbor(EdgeType.Bottom, () => { return GetParent().GetChild(QuadType.BottomLeft); });
AddNeighbor(EdgeType.Right, () => { return GetParent().GetChild(QuadType.TopRight); });
// add non-siblings
AddNeighbor(EdgeType.Top, () =>
{
return GetParent().GetNeighbor(EdgeType.Top)?.GetChildren()?.FirstOrDefault(c => c != null && HasSharedEdge(EdgeType.Top, c));
});
AddNeighbor(EdgeType.Left, () =>
{
return GetParent().GetNeighbor(EdgeType.Left)?.GetChildren()?.FirstOrDefault(c => c != null && HasSharedEdge(EdgeType.Left, c));
});
break;
case QuadType.TopRight:
// add siblings
AddNeighbor(EdgeType.Bottom, () => { return GetParent().GetChild(QuadType.BottomRight); });
AddNeighbor(EdgeType.Left, () => { return GetParent().GetChild(QuadType.TopLeft); });
// add non-siblings
AddNeighbor(EdgeType.Top, () =>
{
return GetParent().GetNeighbor(EdgeType.Top)?.GetChildren()?.FirstOrDefault(c => c != null && HasSharedEdge(EdgeType.Top, c));
});
AddNeighbor(EdgeType.Right, () =>
{
return GetParent().GetNeighbor(EdgeType.Right)?.GetChildren()?.FirstOrDefault(c => c != null && HasSharedEdge(EdgeType.Right, c));
});
break;
}
}
this seems to work quickly as all Sibling neighbors are a direct assignment and the locating of non-sibling neighbors is limited to iterating over 4 sides of 4 quads. Here is the HasSharedEdge method:
public bool HasSharedEdge(EdgeType edge, Quad quad)
{
var topLeft = quad.ToWorldVert(quad.TopLeft);
var topRight = quad.ToWorldVert(quad.TopRight);
var bottomLeft = quad.ToWorldVert(quad.BottomLeft);
var bottomRight = quad.ToWorldVert(quad.BottomRight);
// shared Top edge
if (IsLineWithinEdge(edge, topLeft, topRight, Tolerance))
{
return true;
}
// shared Bottom edge
if (IsLineWithinEdge(edge, bottomLeft, bottomRight, Tolerance))
{
return true;
}
// shared Left edge
if (IsLineWithinEdge(edge, bottomLeft, topLeft, Tolerance))
{
return true;
}
// shared Right edge
if (IsLineWithinEdge(edge, bottomRight, topRight, Tolerance))
{
return true;
}
return false;
}
Maybe this can help someone else in the future

Tabbed panels and hashchange plugin

I'm using the following code to create tabbed panels and it's the only one that it's doing the job pretty good. I tried easytabs but it doesn't work properly with what I have.
So, here we go:
/*
* The following lines are for main tabbed panels
*/
$(function() {
$('.nav a').click(function() {
// save $(this) in a variable for efficiency
var $this = $(this);
// hide panels
$('.panel').hide();
$('.nav a.active').removeClass('active');
// add active state to new tab
$this.addClass('active').blur();
// retrieve href from link (is id of panel to display)
var panel = $this.attr('href');
// show panel
$(panel).fadeIn(250);
// don't follow link down page
return (false);
});
// end click
// open first tab
$('.nav li:first a').click();
});
// end function
I have found a plugin called BBQ, that uses Hashchange.
http://benalman.com/projects/jquery-bbq-plugin/
I'm getting crazy to understand how to use that with my code in order to be able to use the back button in the browser. Please help me.
Thank you in advance for helping.
Regards,
Deviad
I solved the problem this way.
/*
* jQuery hashchange event - v1.2 - 2/11/2010
* http://benalman.com/projects/jquery-hashchange-plugin/
*
* Copyright (c) 2010 "Cowboy" Ben Alman
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*/
(function($, i, b) {
var j, k = $.event.special, c = "location", d = "hashchange", l = "href", f = $.browser, g = document.documentMode, h = f.msie && (g === b || g < 8), e = "on" + d in i && !h;
function a(m) {
m = m || i[c][l];
return m.replace(/^[^#]*#?(.*)$/, "$1")
}
$[d + "Delay"] = 100;
k[d] = $.extend(k[d], {
setup : function() {
if (e) {
return false
}
$(j.start)
},
teardown : function() {
if (e) {
return false
}
$(j.stop)
}
});
j = (function() {
var m = {}, r, n, o, q;
function p() {
o = q = function(s) {
return s
};
if (h) {
n = $('<iframe src="javascript:0"/>').hide().insertAfter("body")[0].contentWindow;
q = function() {
return a(n.document[c][l])
};
o = function(u, s) {
if (u !== s) {
var t = n.document;
t.open().close();
t[c].hash = "#" + u
}
};
o(a())
}
}
m.start = function() {
if (r) {
return
}
var t = a();
o || p();
(function s() {
var v = a(), u = q(t);
if (v !== t) {
o( t = v, u);
$(i).trigger(d)
} else {
if (u !== t) {
i[c][l] = i[c][l].replace(/#.*/, "") + "#" + u
}
}
r = setTimeout(s, $[d + "Delay"])
})()
};
m.stop = function() {
if (!n) {
r && clearTimeout(r);
r = 0
}
};
return m
})()
})(jQuery, this);
/*
*
* Tabbed Browsing with back button support. Requires Hashchange plugin.
*
*/
$(function () {
$(".nav a").live("click", function(e) {
updateTabs($($(this).attr("href")));
});
//Grab hash off URL (default to first tab) and update
$(window).bind("hashchange", function(e) {
var anchor = $(location.hash);
if (anchor.length === 0) {
anchor = $(".panel div:eq(0)");
}
updateTabs(anchor);
});
//Pass in the tab and show appropriate contents
function updateTabs(tab) {
$(".nav a.active")
.removeClass("active")
.filter(function(index) {
return $(this).attr("href") === '#' + tab.attr("id");
}).addClass("active");
$(".panel").hide();
tab.show();
}
//Fire the hashchange event when the page first loads
$(window).trigger('hashchange');
$('.nav li:first a').click();
});
});