Improving box of controls speed (C++-CLI WinForms) - c++-cli

I've made a control in c++ to allow the user to select a position in an irregular box with different positions, some of them blocked, some of them filled, with this characteristics:
Can create multiple columns in multiple rows (not the same, so it can have the first row with 3 columns, the second with 1 and the third with 5)
Different states each position: blocked, filled, empty, selected (so it can have some of them at the same time: blocked & empty, blocked & filled, empty & selected)
The user can select one or multiple position
It has tooltip texts and different background images for each filled or empty position.
Contextual menu to add/delete rows/columns.
Row and Column headers, numeric/alphanumeric, ascending/descending.
The structure of the control is Container->(nRows x RowClass) and RowClass->(nCols x ColumnClass). Each column contains a TableLayoutPanel so I can simulate the stuck and raised effects.
Loocking different topics on the web, I found some ideas and the improvements I've done for the speed are basically SuspendDrawing with SendMessage (to create and resize all the rows and columns before painting them), double buffering each control and adding a BeginEdit/EndEdit method to the Container that SuspendDrawing and blocks row and column resizing while creating rows and columns (and the same for each RowClass).
It works not so bad with relative small boxes, lets say 20 rows x 20 columns, but when it has 40 rows x 30 columns (1.200 TableLayoutPanel's) it is going really slow even in a fast computer.
I also tried with one TableLayoutPanel (with the required number of columns) per RowClass, but the problem is how to draw borders in order to select each column separatedly, tooltips and background image for each cell.
So, the question: is there any improvements I can try?
I've been thinking that strategy games like warcraft or similar they have good speed on screen with thousands of graphics and calculations, but I'm not a professional programmer so I don't know wich could be the technology and if it is the right way..
Maybe it could be something like an image with a row of images, or something different than a class of classes of classes, but I have no idea...
Probably it is a design problem so, any idea on how would you create a fast control with those specifications will be great.
Thanks anyway for reading my question, any comments will be wellcome!
Miguel

There is no clean fix for this, you will have to give up on TLP. A control is a much too expensive object, any form that has more than a hundred of them is going to suck mud.
First look at the built-in control types that support a grid-like display. ListView with View = Details for non-editable grids, DataGridView for editable ones. If that doesn't fit your bill then you'll have to make your own. Key things you'll need to implement is the OnPaint() method to draw the visual appearance of the control and OnMouseDown or OnClick to do hit-testing and implement behavior.

Videogames offload the graphical representations atleast on the GPU (which are built for just that purpose). I do not think the representation of the boxes is the bottleneck, rather the calculations/steps to get there.
Either post your code / a snippet of your code or explain your use of algorithms, because a bad algorithm will make your program very slow when input increases -- have a look on Computational Complexity, something brought up by algorithm courses.
http://en.wikipedia.org/wiki/Computational_complexity_theory

Here is a part of the code (the whole code is really huge) where some calculations are done..
In ControlClass::
void BeginEdit(int nFilas) {
if (!_editando) {
if (_muestraBarraProgreso) {
this->BarraProgreso->Value = 0;
this->BarraProgreso->Visible = true;
}
_editando = true;
_lastEdit = nFilas;
this->Cursor = Cursors::WaitCursor;
this->Refresh();
SendMessageClass::SuspendDrawing();
}
};
/// <summary>Provoca se realicen los cambios visuales no realizados desde que se
/// llamó a BeginEdit, como this->Controls.Add(...), Ajustar los tamaños de los controles, ...</summary>
void EndEdit(void) {
if (_editando) {
int f;
_editando = false;
AjustaHeaders();
AjustaTamaños(); //Provoca el ajuste de las filas sin ajustar las columnas
//Esto lo hago para minimizar el número de AjustaTamaños()
for (f=_lastEdit; f<_nFilas; f++)
_fila[f]->EndEdit(false); //Provoca el ajuste de las columnas
this->MuestraBarraProgreso = false;
this->Cursor = Cursors::Default;
SendMessageClass::ResumeDrawing();
}
};
void AjustaTamaños(void) {
if (!_editando && _nFilas>0) {
int topOffset = 0, leftOffset = 0, bottomOffset = 0;
if (_showColumnHeaders) topOffset = this->tlpColumnHeaders->Height;
if (_showRowHeaders) leftOffset = this->tlpRowHeaders->Width;
if (_muestraEstado) bottomOffset = this->EstadoRecipiente->Height;
int hOffset = leftOffset + 2;
int vOffset = topOffset + bottomOffset + 2;
int i, j = 0, stp;
if (_varsCom->ordenFilas == TipoOrdenFilas::Descendiente) {
j = 0; stp = 1;
}
else {
j = _nFilas-1; stp = -1;
}
for (i=0; i<_nFilas; i++) {
_fila[j]->Left = hOffset;
_fila[j]->Width = this->Width - hOffset;
_fila[j]->Top = (_nFilas - 1 - i) * (this->Height - vOffset) / _nFilas + topOffset;
_fila[j]->Height = (this->Height - vOffset) / _nFilas;
j+=stp;
}
}
};
in FilaClass:
/// <summary>Provoca que no se realicen los cambios visuales hasta que se llame EndEdit</summary>
void BeginEdit(void) {
if (!_editando) {
_editando = true;
_lastEdit = _nColumnas;
SendMessageClass::SuspendDrawing();
}
};
/// <summary>Provoca se realicen los cambios visuales no realizados desde que se
/// llamó a BeginEdit, como this->Controls.Add(...), Ajustar los tamaños de los controles, ...</summary>
void EndEdit(bool resumirDrawing) {
if (_editando) {
_editando = false;
AjustaTamaños();
for (int c=_lastEdit; c<_nColumnas;c++)
_columna[c]->EndEdit(resumirDrawing);
if (resumirDrawing) SendMessageClass::ResumeDrawing();
}
};
void AjustaTamaños(void) {
if (!_editando) {
int j, stp;
if (_varsCom->ordenColumnas == TipoOrdenColumnas::IzquierdaDerecha) {
j = 0; stp = 1;
}
else {
j = _nColumnas-1; stp = -1;
}
for (int i=0; i<_nColumnas; i++) {
_columna[j]->Width = this->Width / _nColumnas;
_columna[j]->Left = i * this->Width / _nColumnas;
_columna[j]->Top = 0;
_columna[j]->Height = this->Height;
j+=stp;
}
}
};
in ColumnaClass:
void BeginEdit(void) {
_editando = true;
SendMessageClass::SuspendDrawing();
};
void EndEdit(bool resumirDrawing) {
if (_editando) {
_editando = false;
_CambiaEstado = _estado;
if (resumirDrawing) SendMessageClass::ResumeDrawing();
}
};
internal:
/// <summary>Obtiene o establece el estado de la posición sin activar el evento ColumnaChange</summary>
property EstadoColumna _CambiaEstado {
EstadoColumna get() { return _estado; }
void set(EstadoColumna value) {
_estado = value;
if ( (_varsCom->bloquearLlenas && ((value & EstadoColumna::llena) == EstadoColumna::llena)) ||
(_varsCom->bloquearVacias && ((value & EstadoColumna::llena) != EstadoColumna::llena)) ) {
if ((_estado & EstadoColumna::seleccionada) == EstadoColumna::seleccionada)
_estado = (EstadoColumna) (_estado ^ EstadoColumna::seleccionada);
_estado = (EstadoColumna) (_estado | EstadoColumna::bloqueada);
}
else {
if ((_estado & EstadoColumna::bloqueada) == EstadoColumna::bloqueada)
_estado = (EstadoColumna) (_estado | EstadoColumna::bloqueada);
}
if (!_editando) {
ActualizaEstado();
ActualizaToolTip();
}
}
};
/// <summary>Actualiza la visualización de la posición leyendo de nuevo los parámetros</summary>
void ActualizaEstado(void) {
if (!_editando && _varsCom->estilo) {
this->ColumnaCtl->BackColor = _varsCom->estilo->GetEstilo[(int) _estado]->colorFondo;
this->ColumnaCtl->CellBorderStyle = _varsCom->estilo->GetEstilo[(int) _estado]->bordeCelda;
this->ColumnaCtl->BackgroundImageLayout = _varsCom->imageLayout;
if (this->Estado[EstadoColumna::llena])
this->ColumnaCtl->BackgroundImage = _varsCom->imagenLlena;
else if (this->Estado[EstadoColumna::vacia])
this->ColumnaCtl->BackgroundImage = _varsCom->imagenVacia;
}
};
/// <summary>Actualiza los textos del ToolTipText de la posición</summary>
void ActualizaToolTip(void) {
if (!_editando && _varsCom->etiquetas) {
String^ sTmp = "";
int __tmp__ = 0;
if (_varsCom->tipoHeaderFilas == TipoHeader::Numerica)
sTmp = _varsCom->etiquetas->Fila + ": " + _nombreFila;
else if (System::Int32::TryParse(_nombreFila, __tmp__))
sTmp = _varsCom->etiquetas->Fila + ": " + gcnew String((wchar_t) ('A' + __tmp__ - 1), 1);
else
sTmp = _varsCom->etiquetas->Fila + ": " + _nombreFila;
if (_varsCom->tipoHeaderColumnas == TipoHeader::Numerica)
sTmp += ", " + _varsCom->etiquetas->Columna + ": " + _posicion + "\n";
else
sTmp += ", " + _varsCom->etiquetas->Columna + ": " + gcnew String((wchar_t) ('A' + _posicion - 1), 1) + "\n";
if (_nombre != "") {
if ((_estado & EstadoColumna::llena) == EstadoColumna::llena)
sTmp += _varsCom->etiquetas->Posicion + ": " + _nombre + "\n";
else
sTmp += _varsCom->etiquetas->PosicionVacia + ": " + _nombre + "\n";
}
if ((_estado & EstadoColumna::llena) == EstadoColumna::llena) sTmp += "Ocupada, ";
else sTmp += "Vacía, ";
if ((_estado & EstadoColumna::seleccionada) != EstadoColumna::vacia) sTmp += "Seleccionada";
else sTmp += "Sin seleccionar";
this->ToolTip1->ShowAlways = false;
this->ToolTip1->SetToolTip(this->ColumnaCtl, sTmp);
this->ToolTip1->ShowAlways = true;
}
};
I think those are most part of the calculations..
A little bit uncommented, and in spanish, but any help will be really useful!
Thanks anyway!

Related

How to Convert Geohash to Geometry in BigQuery?

PostGIS has this function ST_GeomFromGeoHash to get the bounding box geometry of the geohash area (https://postgis.net/docs/ST_GeomFromGeoHash.html), but it has not been ported to BigQuery yet. Is there any workaround?
I've implemented the following BigQuery UDF that converts a geohash of arbitrary precision to a bounding box geometry:
CREATE OR REPLACE FUNCTION dataset.geohash_to_bbox(geohash STRING)
RETURNS STRING
LANGUAGE js AS """
var BASE32_CODES = "0123456789bcdefghjkmnpqrstuvwxyz";
var BASE32_CODES_DICT = {};
for (var i = 0; i < BASE32_CODES.length; i++) {
BASE32_CODES_DICT[BASE32_CODES.charAt(i)] = i;
}
var ENCODE_AUTO = 'auto';
var MIN_LAT = -90;
var MAX_LAT = 90;
var MIN_LON = -180;
var MAX_LON = 180;
var decode_bbox = function (hash_string) {
var isLon = true,
maxLat = MAX_LAT,
minLat = MIN_LAT,
maxLon = MAX_LON,
minLon = MIN_LON,
mid;
var hashValue = 0;
for (var i = 0, l = hash_string.length; i < l; i++) {
var code = hash_string[i].toLowerCase();
hashValue = BASE32_CODES_DICT[code];
for (var bits = 4; bits >= 0; bits--) {
var bit = (hashValue >> bits) & 1;
if (isLon) {
mid = (maxLon + minLon) / 2;
if (bit === 1) {
minLon = mid;
} else {
maxLon = mid;
}
} else {
mid = (maxLat + minLat) / 2;
if (bit === 1) {
minLat = mid;
} else {
maxLat = mid;
}
}
isLon = !isLon;
}
}
return "POLYGON (( " + minLon + " " + minLat + ", " + maxLon + " " + minLat + ", " + maxLon + " " + maxLat + ", " + minLon + " " + maxLat + ", " + minLon + " " + minLat + "))";
};
return decode_bbox(geohash);
""";
Example usage:
select <dataset>.geohash_to_geom("ttnfv2u");
>> POLYGON((77.2119140625 28.6083984375, 77.2119140625 28.65234375, 77.255859375 28.65234375, 77.255859375 28.6083984375, 77.2119140625 28.6083984375))
BigQuery has ST_GEOGPOINTFROMGEOHASH which returns the central point. There is currently no function that returns the box though. The UDF in another answer is often a reasonable workaround, but you should be aware of its usage limitation.
GeoHash normally represents a rectangle on a flat 2D map. BigQuery works with Geography, with geodesic edges, so an edge between two points with same latitude does not follow the parallel, but being geodesic line is a shorter route closer to the pole. So the BigQuery polygon is a bit different from 2D box. You can often ignore the differences, but it might give you wrong results depending on how you use this polygon.

Running solve multiple timese

I need to run a solve three times. Every time solve needs to have different input from different columns of a tuple. That is why I need to access the loop variable with in the OPL as a parameter and need to change that parameter with every loop. Please suggest how to do that in ODM OPL.
(I am able to do it when running a standalone model with a physical .dat file by introducing a int in dat file and changing its values with each loop, but same is not possible when running through an ODM application).
You can do this using a scripting main() function:
.dat file:
param = 0; // This value is actually never used
.mod file:
tuple T {
int round1;
int round2;
}
T t = <1, 2>;
int param = ...;
dvar float x;
minimize x;
subject to { x >= param; }
main {
thisOplModel.generate();
var def = thisOplModel.modelDefinition;
var data = thisOplModel.dataElements;
for (var i = 0; i < 2; ++i) {
if (i == 0)
data.param = thisOplModel.t.round1;
else
data.param = thisOplModel.t.round2;
var opl = new IloOplModel(def, cplex);
opl.addDataSource(data);
opl.generate();
cplex.solve();
writeln("Round " + i + ": " + cplex.getObjValue() + ", " + data.param);
opl.end();
}
}
The scripting code modifies the data before creating a new model in each iteration. You have a more elaborate version of code like this in the cutstock_main.mod example that ships with CPLEX.
What Daniel wrote works fine. If you do not want to have the non necessary .dat file you could write
sub.mod
tuple T {
int round1;
int round2;
}
T t = <1, 2>;
int param = ...;
dvar float x;
minimize x;
subject to { x >= param; }
and then in another model that will be the main one:
tuple T {
int round1;
int round2;
}
T t = <1, 2>;
main {
thisOplModel.generate();
var src = new IloOplModelSource("sub.mod");
var def=new IloOplModelDefinition(src);
var data = new IloOplDataElements();;
for (var i = 0; i < 2; ++i) {
if (i == 0)
data.param = thisOplModel.t.round1;
else
data.param = thisOplModel.t.round2;
var opl = new IloOplModel(def, cplex);
opl.addDataSource(data);
opl.generate();
cplex.solve();
writeln("Round " + i + ": " + cplex.getObjValue() + ", " + data.param);
opl.end();
}
}
which will give
Round 0: 1, 1
Round 1: 2, 2
and
tuple T {
int round1;
int round2;
}
T t = <1, 2>;
int solutions[0..1];
main {
thisOplModel.generate();
var src = new IloOplModelSource("sub.mod");
var def=new IloOplModelDefinition(src);
var data = new IloOplDataElements();;
for (var i = 0; i < 2; ++i) {
if (i == 0)
data.param = thisOplModel.t.round1;
else
data.param = thisOplModel.t.round2;
var opl = new IloOplModel(def, cplex);
opl.addDataSource(data);
opl.generate();
cplex.solve();
writeln("Round " + i + ": " + cplex.getObjValue() + ", " + data.param);
thisOplModel.solutions[i]=opl.x.solutionValue;
opl.end();
}
writeln(thisOplModel.solutions);
}
to address your next question about populating tables
which gives
Round 0: 1, 1
Round 1: 2, 2
[1 2]

Excel VBA Punycode Support for International Domain Names (IDNA2003, IDNA2008, and UTS46)

Does Excel VBA provide any sort of method, function, or API for Punycode or converting to and from Unicode/Ascii for Internationalized Domain Names?
I was able to find a JavaScript version (below) which seems to work for IDNA2008 Standards. I am still able to use this in VBA, but I think this is a little clunky...
Another method I've tried is to Web Scrape the responses from an Online Punycode converter (http://www.unicode.org/cldr/utility/idna.jsp?a=), but I'm really not crazy about this as websites are sometimes not available and its not very efficient...
Since the different web browsers (Internet Explorer, Firefox, and Chrome) utilize different standards. I need to be able to convert to and from Puny using all standards (IDNA2003, UTS46, IDNA2008) so that I can produce/capture all possible variations.
For example using the domain "schüloß.de" I should be able to produce 2 different variations:
Unicode:
schüloss.de <- IDNA2003
schüloß.de <- IDNA2008
ASCII:
xn--schloss-p2a.de <- IDNA2003
xn--schlo-pqa4r.de <- IDNA2008
I'm not aware of any web object or built in functionality to help with Punycode...
Would someone have a reliable and efficient method to perform Punycode conversions for all standards? Any help would be greatly appreciated.
Option Explicit
'code loosely based on 'http://stackoverflow.com/questions/5773683/excel-vba-parsed-json-object-loop
'Tools->References->
'MSScriptControl; Microsoft Script Control 1.0; {0E59F1D2-1FBE-11D0-8FF2-00A0D10038BC}; C:\Windows\SysWOW64\msscript.ocx
Private moScriptEngine As ScriptControl
Private Property Get ScriptEngine()
If moScriptEngine Is Nothing Then
Set moScriptEngine = New ScriptControl
moScriptEngine.Language = "JScript"
'moScriptEngine.AllowUI = True
End If
Set ScriptEngine = moScriptEngine
End Property
Private Function IDNA2008_JScript() As String
' toASCII = Unicode -> Punycode Example: ScriptEngine.Eval("punycode.toASCII('UNICODE')") = PUNYCODE
' toUnicode = Punycode -> Unicode Example: ScriptEngine.Eval("punycode.toUnicode('PUNYCODE')") = UNICODE
' TakenFrom: https://github.com/bestiejs/punycode.js/
IDNA2008_JScript = ";(function(root) { var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports; var freeModule = typeof module == 'object' && module && !module.nodeType && module; var freeGlobal = typeof global == 'object' && global; " & _
"if ( freeGlobal.global === freeGlobal || freeGlobal.window === freeGlobal || freeGlobal.self === freeGlobal ) { root = freeGlobal;} var punycode, maxInt = 2147483647, base = 36, tMin = 1, tMax = 26, skew = 38, damp = 700, initialBias = 72, initialN = 128, " & _
"delimiter = '-', regexPunycode = /^xn--/, regexNonASCII = /[^\x20-\x7E]/, regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g, errors = { 'overflow': 'Overflow: input needs wider integers to process', 'not-basic': 'Illegal input >= 0x80 (not a basic code point)', 'invalid-input': 'Invalid input' }, " & _
"baseMinusTMin = base - tMin, floor = Math.floor, stringFromCharCode = String.fromCharCode, key; " & _
"function error(type) { throw new RangeError(errors[type]); } function map(array, fn) { var length = array.length; var result = []; while (length--) { result[length] = fn(array[length]); } return result; } " & _
"function mapDomain(string, fn) { var parts = string.split('#'); var result = ''; if (parts.length > 1) { result = parts[0] + '#'; string = parts[1]; } string = string.replace(regexSeparators, '\x2E'); var labels = string.split('.'); var encoded = map(labels, fn).join('.'); return result + encoded; } " & _
"function ucs2decode(string) { var output = [], counter = 0, length = string.length, value, extra; while (counter < length) { value = string.charCodeAt(counter++); if (value >= 0xD800 && value <= 0xDBFF && counter < length) { extra = string.charCodeAt(counter++); if ((extra & 0xFC00) == 0xDC00) { " & _
"output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000); } else { output.push(value); counter--; } } else { output.push(value); } } return output; } function ucs2encode(array) { return map(array, function(value) { var output = ''; if (value > 0xFFFF) { " & _
"value -= 0x10000; output += stringFromCharCode(value >>> 10 & 0x3FF | 0xD800); value = 0xDC00 | value & 0x3FF; } output += stringFromCharCode(value); return output; }).join(''); } function basicToDigit(codePoint) { if (codePoint - 48 < 10) { return codePoint - 22; } " & _
"if (codePoint - 65 < 26) { return codePoint - 65; } if (codePoint - 97 < 26) { return codePoint - 97; } return base; } function digitToBasic(digit, flag) { return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5); } function adapt(delta, numPoints, firstTime) { var k = 0; delta = firstTime " & _
"? floor(delta / damp) : delta >> 1; delta += floor(delta / numPoints); for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) { delta = floor(delta / baseMinusTMin); } return floor(k + (baseMinusTMin + 1) * delta / (delta + skew)); } function decode(input) { " & _
"var output = [], inputLength = input.length, out, i = 0, n = initialN, bias = initialBias, basic, j, index, oldi, w, k, digit, t, baseMinusT; basic = input.lastIndexOf(delimiter); if (basic < 0) { basic = 0; } for (j = 0; j < basic; ++j) { if (input.charCodeAt(j) >= 0x80) { error('not-basic'); } " & _
"output.push(input.charCodeAt(j)); } for (index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) { for (oldi = i, w = 1, k = base; /* no condition */; k += base) { if (index >= inputLength) { error('invalid-input'); } digit = basicToDigit(input.charCodeAt(index++)); " & _
"if (digit >= base || digit > floor((maxInt - i) / w)) { error('overflow'); } i += digit * w; t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); if (digit < t) { break; } baseMinusT = base - t; if (w > floor(maxInt / baseMinusT)) { error('overflow'); } w *= baseMinusT; } " & _
"out = output.length + 1; bias = adapt(i - oldi, out, oldi == 0); if (floor(i / out) > maxInt - n) { error('overflow'); } n += floor(i / out); i %= out; output.splice(i++, 0, n); } return ucs2encode(output); } function encode(input) { var n, delta, handledCPCount, basicLength, bias, j, m, q, k, t, " & _
"currentValue, output = [], inputLength, handledCPCountPlusOne, baseMinusT, qMinusT; input = ucs2decode(input); inputLength = input.length; n = initialN; delta = 0; bias = initialBias; for (j = 0; j < inputLength; ++j) { currentValue = input[j]; if (currentValue < 0x80) { " & _
"output.push(stringFromCharCode(currentValue)); } } handledCPCount = basicLength = output.length; if (basicLength) { output.push(delimiter); } while (handledCPCount < inputLength) { for (m = maxInt, j = 0; j < inputLength; ++j) { currentValue = input[j]; " & _
"if (currentValue >= n && currentValue < m) { m = currentValue; } } handledCPCountPlusOne = handledCPCount + 1; if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) { error('overflow'); } delta += (m - n) * handledCPCountPlusOne; n = m; for (j = 0; j < inputLength; ++j) { " & _
"currentValue = input[j]; if (currentValue < n && ++delta > maxInt) { error('overflow'); } if (currentValue == n) { for (q = delta, k = base; /* no condition */; k += base) { t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); if (q < t) { break; } qMinusT = q - t; baseMinusT = base - t; " & _
"output.push( stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0)) ); q = floor(qMinusT / baseMinusT); } output.push(stringFromCharCode(digitToBasic(q, 0))); bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength); delta = 0; ++handledCPCount; } } " & _
"++delta; ++n; } return output.join(''); } function toUnicode(input) { return mapDomain(input, function(string) { return regexPunycode.test(string) ? decode(string.slice(4).toLowerCase()) : string; }); } function toASCII(input) { return mapDomain(input, function(string) { " & _
"return regexNonASCII.test(string) ? 'xn--' + encode(string) : string; }); } punycode = { 'version': '1.4.1', 'ucs2': { 'decode': ucs2decode, 'encode': ucs2encode }, 'decode': decode, 'encode': encode, 'toASCII': toASCII, 'toUnicode': toUnicode }; if ( typeof define == 'function' && " & _
"typeof define.amd == 'object' && define.amd ) { define('punycode', function() { return punycode; }); } else if (freeExports && freeModule) { if (module.exports == freeExports) { freeModule.exports = punycode; } else { for (key in punycode) { punycode.hasOwnProperty(key) " & _
"&& (freeExports[key] = punycode[key]); } } } else { root.punycode = punycode; } }(this));"
End Function
Public Function IDNA2008_toPUNY(ByVal strINPUT As String)
ScriptEngine.AddCode IDNA2008_JScript
On Error Resume Next
IDNA2008_toPUNY = ScriptEngine.Eval("punycode.toASCII('" & strINPUT & "')")
On Error GoTo 0
Set moScriptEngine = Nothing
If IsEmpty(IDNA2008_toPUNY) Then IDNA2008_toPUNY = vbNullString
End Function
Public Function IDNA2008_toUNICODE(ByVal strINPUT As String)
ScriptEngine.AddCode IDNA2008_JScript
On Error Resume Next
IDNA2008_toUNICODE = ScriptEngine.Eval("punycode.toUnicode('" & strINPUT & "')")
On Error GoTo 0
Set moScriptEngine = Nothing
If IsEmpty(IDNA2008_toUNICODE) Then IDNA2008_toUNICODE = vbNullString
End Function
Sub TestPunyCode()
Debug.Print IDNA2008_toPUNY("schüloß.de")
Debug.Print IDNA2008_toUNICODE("xn--schlo-pqa4r.de")
End Sub
Additional Info:
Using MS Office 2013 (32 bit) with Windows 7.
The solution can not attempt to communicate with the Unicode domains. I'm not against using the WinHttp object if that's even possible, but it cannot send or make any attempt to send outgoing network traffic to the domain for security reasons...
Unfortunately this is one of those questions that require a VBA solution without the need to install external software.
Again, any help is greatly appreciated!

apply bind pose to a kinect skeleton

I want to normalize a skeleton in order to make it invariant to the size of the person
in front of the kinect; in the same way as the aveteering example.
But I don't want to animate a 3D model using XNA, the only thing I need is to normalize an
skeleton.
So in order to do this task, I have divided it in two functions:
(a) apply a bind pose to an skeleton in order to see how to work this matrix. Obviously this is not what i want to do, but it is a first step in order to
know how to work whit matrix, and so on.
(b) apply any arbitrary pose to a normalized-size-skeleton
First of all, I want to apply a bind pose to an skeleton (a).
First, I have to load the matrix that describe the bone length/ offset between bones and store it in
List BindPose.
Due to I have no idea how to do it, I modified the Aveteering example and write in a file all the Matrix that define
the BindPose, InverseBindPose and SkeletonHierarchy of the dude. I only need BindPose to this first task, but I have the
code prepared in order to do the second task (b)
The file looks like this:
1,331581E-06;-5,551115E-17;1;0;1;-4,16881E-11;-1,331581E-06;0;4,16881E-11;1;8,153579E-23;0;0,03756338;37,46099;2,230549;1
1,110223E-16;-4,435054E-22;1;0;1;1,426127E-06;-2,220446E-16;0;-1,426127E-06;1;-7,654181E-22;0;-0,9558675;-4,079016E-08;-6,266987E-12;1
0,9954988;-0,09477358;1,501821E-06;0;0,09477358;0,9954988;-4,019565E-06;0;-1,114112E-06;4,143805E-06;1;0;3,786007;-0,003599779;5,107028E-06;1
0,9948416;-0,101441;-3,23556E-07;0;0,101441;0,9948416;-2,266755E-08;0;3,241862E-07;-1,027114E-08;1;0;4,543321;-0,00359975;-1,33061E-07;1
0,9950595;0,09927933;2,388133E-07;0;-0,09927933;0,9950595;-2,333792E-08;0;-2,399506E-07;-4,86646E-10;1;0;4,544049;-0,003599948;6,324596E-08;1
0,9992647;0,02747673;0,02674458;0;-0,02928042;0,9971476;0,06956656;0;-0,02475683;-0,07029849;0,9972187;0;4,543965;-0,004398902;2,258555E-07;1
0,9154034;0,4025377;1,107153E-06;0;-0,4025377;0,9154033;-2,437432E-07;0;-1,109319E-06;-2,115673E-07;1;0;5,536249;-0,00288291;1,332601E-07;1
0,9812952;-0,1925096;-4,732622E-07;0;0,1925095;0,9812951;-3,00921E-08;0;4,697166E-07;-5,889972E-08;1;0;3,953898;1,702301E-07;4,88653E-08;1
.......
So each line is a 4X4 matrix defining the BindPose.
To generate this file, the code is like this:
private void ViewSkinningData(SkinningData data)
{
string nameFile = "bind_pose_transformations";
bool append = false;
// The using statement automatically closes the stream and calls IDisposable.Dispose on the stream object.
using (System.IO.StreamWriter file = new System.IO.StreamWriter(#nameFile, append))
{
for (int i = 0; i < data.BindPose.Count; i++)
{
Matrix m = data.BindPose[i];
string matrixString = MatrixToString(m);
file.WriteLine(matrixString);
}
for (int i = 0; i < data.InverseBindPose.Count; i++)
{
Matrix m = data.InverseBindPose[i];
string matrixString = MatrixToString(m);
file.WriteLine(matrixString);
}
for (int i = 0; i < data.SkeletonHierarchy.Count; i++)
{
file.Write(data.SkeletonHierarchy[i] + ";");
}
}
}
string MatrixToString(Matrix m)
{
string result;
result = m.M11 + ";" + m.M12 + ";" + m.M13 + ";" + m.M14 + ";" + m.M21 + ";" + m.M22 + ";" + m.M23 + ";" + m.M24 + ";" + m.M31 + ";" + m.M32 + ";" + m.M33 + ";" + m.M34 + ";" + m.M41 + ";" + m.M42 + ";" + m.M43 + ";" + m.M44;
return result;
}
Next step is to load all this Skinning data in my program:
private void InitializeSkinningDataFromFile()
{
string filename = "bind_pose_transformations";
int number_avatar_joints = 58;
List<Matrix> binpose = new System.Collections.Generic.List<Matrix>();
List<Matrix> inversebindpose = new System.Collections.Generic.List<Matrix>();
List<int> skeletonhierarchy = new System.Collections.Generic.List<int>();
// The using statement automatically closes the stream and calls IDisposable.Dispose on the stream object.
using (System.IO.StreamReader file = new System.IO.StreamReader(filename))
{
string s;
int count = 0;
while (!String.IsNullOrEmpty(s = file.ReadLine()))
{
string[] values = s.Split(';');
Matrix m = BuildMatrix(values);
binpose.Add(m);
count++;
if (count == number_avatar_joints)
{
break;
}
}
count = 0;
while (!String.IsNullOrEmpty(s = file.ReadLine()))
{
string[] values = s.Split(';');
Matrix m = BuildMatrix(values);
inversebindpose.Add(m);
count++;
if (count == number_avatar_joints)
{
break;
}
}
string[] skeletonHierarchy = file.ReadLine().Split(';'); //lee un caracter de separacion al final...
//for (int i = 0; i < skeletonHierarchy.Count(); i++)
for (int i = 0; i < number_avatar_joints; i++)
{
skeletonhierarchy.Add(int.Parse(skeletonHierarchy[i]));
}
}
skinningDataValue = new SkinningData(binpose, inversebindpose, skeletonhierarchy);
}
After, I have to construct boneTransforms structure:
// Bone matrices for the "dude" model
this.boneTransforms = new Matrix[skinningDataValue.BindPose.Count];
this.skinningDataValue.BindPose.CopyTo(this.boneTransforms, 0);
Now boneTransforms have the transformation for my skeleton. So now, i have to apply these trasnformations to an skeleton
Skeleton skeleton = new Skeleton();
foreach (Joint joint in skeleton.Joints)
{
int indexMatrix = AvatarBoneToNuiJointIndex(joint.JointType);
Matrix transform;
if (indexMatrix >= 0)
{
transform = this.boneTransforms[indexMatrix];
}
else
{
transform = Matrix.Identity;
}
Joint aux = ApplyMatrixTransformationToJoint(joint, transform);
normalizeSkel.Joints[joint.JointType] = aux;
}
This is a helper function AvatarBoneToNuiJointIndex:
public int AvatarBoneToNuiJointIndex(JointType jointType)
{
switch (jointType)
{
case JointType.HipCenter:
return 1;
case JointType.Spine:
return 4;
case JointType.ShoulderCenter:
return 6;
case JointType.Head:
return 7;
case JointType.ShoulderLeft:
return 12;
case JointType.ElbowLeft:
return 13;
case JointType.WristLeft:
return 14;
case JointType.HandLeft:
return 15;
case JointType.ShoulderRight:
return 31;
case JointType.ElbowRight:
return 32;
case JointType.WristRight:
return 33;
case JointType.HandRight:
return 34;
case JointType.KneeLeft:
return 50;
case JointType.AnkleLeft:
return 51;
case JointType.FootLeft:
return 52;
case JointType.KneeRight:
return 54;
case JointType.AnkleRight:
return 55;
case JointType.FootRight:
return 56;
default: return -1;
}
}
This is a helper function ApplyMatrixTransformationToJoint:
public Joint ApplyMatrixTransformationToJoint(Joint skeletonJoint, Matrix tranformations)
{
Vector3 pos = SkeletonPointToVector3(skeletonJoint.Position);
Vector3 result = ApplyMatrixTransformationToVector(pos, tranformations);
SkeletonPoint newPosition = new SkeletonPoint()
{
X = result.X,
Y = result.Y,
Z = result.Z
};
skeletonJoint.Position = newPosition;
return skeletonJoint;
}
This is the code for ApplyMatrixTransformationToVector:
static Vector3 ApplyMatrixTransformationToVector(Vector3 v, Matrix m)
{
return Vector3.Transform(v, m);
}
But the problem is that I can't see anything.
I don't know if this approach is correct.
Any help would be fantastic.
Many thanks!

Android 2D animation drawing: bad performance

I have an app that draws a grid of dots (let's say 5x5). The user is asked to draw lines on that grid. If the user's finger touches one of the dots in the grid, this dot is being colored to show that this dot is part of a path drawn. In addition a line will be drawn between each two touched dots.
The issue - I get very bad performance, which causes few things:
The application gets really slow.
Motion events in event.getAction() get bad granularity. I meanenter code here that instead of registering a movement each 10 pixels for example, it registers movements each 100 pixels. This, in turn, will causes the app to NOT redraw some dots the user had touched.
Sometimes the motion coordinates are simple wrong: lets say the user is moving her finger from pixel 100 to pixel 500, the reading might show 100...200...150...140...300...400. For some reason the touch location gets messed up in some cases.
Look at the example on how the app "misses out" on dots the user have touched and doesn't draw the green dots:
I've tried few thing:
Adding Thread.sleep(100); to else if(event.getAction() == MotionEvent.ACTION_MOVE) inside onTouchEvent(MotionEvent event), I read that this might give the CPU time to catch up on all those touch events - didn't change a thing
Adding this.destroyDrawingCache() to the very end of doDraw() (I use it instead of onDraw, as was suggested by one tutorial I used). I thought this will clear all event/drawing caching which seems to be slowing down the system - didn't change a thing.
I am fairly new to Android animation so I am not sure how to proceed:
I understand I should do as little as possible in doDraw() (my onDraw()) and onTouchEvent().
I read some stuff about invalidate() but not sure how and when to use it. If I understand correctly, my View gets drawn anew each time doDraw() is called. My grid, for instance, is static - how can I avoid redrawing it?
++++++++++++++++++++++++ UPDATE 7th Oct +++++++++++++++++++++
I tried using canvas.drawCircle(xPos, yPos, 8, mNodePaint); instead of canvas.drawBitmap(mBitmap, xPos, yPos, null);. I thought that if I DIDN'T use actual bitmaps this might improve performance. As a matter of fact - it didn't! I am a bit confused how such a simple application can pose such a heavy load on the device. I must be doing something really the wrong way.
++++++++++++++++++++++++ UPDATE 12th Oct +++++++++++++++++++++
I took into account what #LadyWoodi suggested - I've eliminated all variable declarations out of the loops - anyway it is a bad practice and I also got rid of all the "System.Out" lines I use so I can log app behavior to better understand why I get such a lame performance. I am sad to say that if there was a change in performance (I didn't actually measure frame rate change) it is negligible.
Any other ideas?
++++++++++++++++++++++++ UPDATE 13th Oct +++++++++++++++++++++
As I have a static grid of dots (see hollow black/white dots in screenShot) that never changes during the game I did the following:
-Draw the grid once.
-Capture the drawing as bitmap using Bitmap.createBitmap().
-Use canvas.drawBitmap() to draw the bitmap of the static dots grid.
-When my thread runs I check to see it the grid of dots is drawn. If it is running I will NOT recreate the static dots grid. I will only render it from my previously rendered bitmap.
Surprisingly this changed nothing with my performance! Redrawing the dots grid each time didn't have a true visual effect on app performance.
I decided to use canvas = mHolder.lockCanvas(new Rect(50, 50, 150, 150)); inside my drawing thread. It was just for testing purposes to see if I limit the area rendered each time, I can get the performance better. This DID NOT help either.
Then I turned to the DDMS tool in Eclipse to try and profile the app. What it came up with, was that canvas.drawPath(path, mPathPaint); (Canvas.native_drawPath) consumed about 88.5% of CPU time!!!
But why??! My path drawing is rather simple, mGraphics contains a collection of Paths and all I do is figure out if each path is inside the boundaries of the game screen and then I draw a path:
//draw path user is creating with her finger on screen
for (Path path : mGraphics)
{
//get path values
mPm = new PathMeasure(path, true);
mPm.getPosTan(0f, mStartCoordinates, null);
//System.out.println("aStartCoordinates X:" + aStartCoordinates[0] + " aStartCoordinates Y:" + aStartCoordinates[1]);
mPm.getPosTan(mPm.getLength(), mEndCoordinates, null);
//System.out.println("aEndCoordinates X:" + aEndCoordinates[0] + " aEndCoordinates Y:" + aEndCoordinates[1]);
//coordinates are within game board boundaries
if((mStartCoordinates[0] >= 1 && mStartCoordinates[1] >= 1) && (mEndCoordinates[0] >= 1 && mEndCoordinates[1] >= 1))
{
canvas.drawPath(path, mPathPaint);
}
}
Can anyone see any ill programmed lines of code in my examples?
++++++++++++++++++++++++ UPDATE 14th Oct +++++++++++++++++++++
I've made changes to my doDraw()method. Basically what I do is draw the screen ONLY if something was changed. In all other cases I simply store a cached bitmap of the screen and render it. Please take a look:
public void doDraw(Canvas canvas)
{
synchronized (mViewThread.getSurefaceHolder())
{
if(mGraphics.size() > mPathsCount)
{
mPathsCount = mGraphics.size();
//draw path user is creating with her finger on screen
for (Path path : mGraphics)
{
//get path values
mPm = new PathMeasure(path, true);
mPm.getPosTan(0f, mStartCoordinates, null);
//System.out.println("aStartCoordinates X:" + aStartCoordinates[0] + " aStartCoordinates Y:" + aStartCoordinates[1]);
mPm.getPosTan(mPm.getLength(), mEndCoordinates, null);
//System.out.println("aEndCoordinates X:" + aEndCoordinates[0] + " aEndCoordinates Y:" + aEndCoordinates[1]);
//coordinates are within game board boundaries
if((mStartCoordinates[0] >= 1 && mStartCoordinates[1] >= 1) && (mEndCoordinates[0] >= 1 && mEndCoordinates[1] >= 1))
{
canvas.drawPath(path, mPathPaint);
}
}
//nodes that the path goes through, are repainted green
//these nodes are building the drawn pattern
for (ArrayList<PathPoint> nodePattern : mNodesHitPatterns)
{
for (PathPoint nodeHit : nodePattern)
{
canvas.drawBitmap(mDotOK, nodeHit.x - ((mDotOK.getWidth()/2) - (mNodeBitmap.getWidth()/2)), nodeHit.y - ((mDotOK.getHeight()/2) - (mNodeBitmap.getHeight()/2)), null);
}
}
mGameField = Bitmap.createBitmap(mGridNodesCount * mNodeGap, mGridNodesCount * mNodeGap, Bitmap.Config.ARGB_8888);
}
else
{
canvas.drawBitmap(mGameField, 0f, 0f, null);
}
Now for the results - as long as the device doesn't have to render no paths and simply draws from a bitmap, stuff goes very fast. But the moment I have to rerender the screen using canvas.drawPath() performance becomes as sluggish as a turtle on morphine... The more paths I have (up to 6 and more, which is NOTHING!) the slower the rendering. How odd is this?? - My paths are even not really curvy - the are all straight lines with an occasional turn. What I mean is that the line is not very "complex".
I've add more code below - if you have any improvements ideas.
Many thanks in advance,
D.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Class "Panel" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public class Panel extends SurfaceView implements SurfaceHolder.Callback {
Bitmap mNodeBitmap;
int mNodeBitmapWidthCenter;
int mNodeBitmapHeightCenter;
Bitmap mDotOK;
ViewThread mViewThread;
ArrayList<PathPoint> mPathPoints;
private ArrayList<Path> mGraphics = new ArrayList<Path>(3);
private ArrayList<ArrayList<PathPoint>> mNodesHitPatterns = new ArrayList<ArrayList<PathPoint>>();
private Paint mPathPaint;
Path mPath = new Path();
//private ArrayList<Point> mNodeCoordinates = new ArrayList<Point>();
private int mGridNodesCount = 5;
private int mNodeGap = 100;
PathPoint mNodeCoordinates[][] = new PathPoint[mGridNodesCount][mGridNodesCount];
PathMeasure mPm;
float mStartCoordinates[] = {0f, 0f};
float mEndCoordinates[] = {0f, 0f};
PathPoint mPathPoint;
Boolean mNodesGridDrawn = false;
Bitmap mGameField = null;
public Boolean getNodesGridDrawn() {
return mNodesGridDrawn;
}
public Panel(Context context) {
super(context);
mNodeBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.dot);
mNodeBitmapWidthCenter = mNodeBitmap.getWidth()/2;
mNodeBitmapHeightCenter = mNodeBitmap.getHeight()/2;
mDotOK = BitmapFactory.decodeResource(getResources(), R.drawable.dot_ok);
getHolder().addCallback(this);
mViewThread = new ViewThread(this);
mPathPaint = new Paint();
mPathPaint.setAntiAlias(true);
mPathPaint.setDither(true); //for better color
mPathPaint.setColor(0xFFFFFF00);
mPathPaint.setStyle(Paint.Style.STROKE);
mPathPaint.setStrokeJoin(Paint.Join.ROUND);
mPathPaint.setStrokeCap(Paint.Cap.ROUND);
mPathPaint.setStrokeWidth(5);
}
public ArrayList<ArrayList<PathPoint>> getNodesHitPatterns()
{
return this.mNodesHitPatterns;
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
public void surfaceCreated(SurfaceHolder holder) {
//setPadding(100, 100, 0, 0);
if (!mViewThread.isAlive()) {
mViewThread = new ViewThread(this);
mViewThread.setRunning(true);
mViewThread.start();
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
if (mViewThread.isAlive()) {
mViewThread.setRunning(false);
}
}
//draw the basic nodes grid that the user will use to draw the lines on
//store as bitmap
public void drawNodesGrid(Canvas canvas)
{
canvas.drawColor(Color.WHITE);
for (int i = 0; i < mGridNodesCount; i++)
{
for (int j = 0; j < mGridNodesCount; j++)
{
int xPos = j * mNodeGap;
int yPos = i * mNodeGap;
try
{
//TODO - changed
mNodeCoordinates[i][j] = new PathPoint(xPos, yPos, null);
}
catch (Exception e)
{
e.printStackTrace();
}
canvas.drawBitmap(mNodeBitmap, xPos, yPos, null);
}
}
mNodesGridDrawn = true;
mGameField = Bitmap.createBitmap(mGridNodesCount * mNodeGap, mGridNodesCount * mNodeGap, Bitmap.Config.ARGB_8888);
}
public void doDraw(Canvas canvas)
{
canvas.drawBitmap(mGameField, 0f, 0f, null);
synchronized (mViewThread.getSurefaceHolder())
{
//draw path user is creating with her finger on screen
for (Path path : mGraphics)
{
//get path values
mPm = new PathMeasure(path, true);
mPm.getPosTan(0f, mStartCoordinates, null);
//System.out.println("aStartCoordinates X:" + aStartCoordinates[0] + " aStartCoordinates Y:" + aStartCoordinates[1]);
mPm.getPosTan(mPm.getLength(), mEndCoordinates, null);
//System.out.println("aEndCoordinates X:" + aEndCoordinates[0] + " aEndCoordinates Y:" + aEndCoordinates[1]);
//coordinates are within game board boundaries
if((mStartCoordinates[0] >= 1 && mStartCoordinates[1] >= 1) && (mEndCoordinates[0] >= 1 && mEndCoordinates[1] >= 1))
{
canvas.drawPath(path, mPathPaint);
}
}
//nodes that the path goes through, are repainted green
//these nodes are building the drawn pattern
for (ArrayList<PathPoint> nodePattern : mNodesHitPatterns)
{
for (PathPoint nodeHit : nodePattern)
{
canvas.drawBitmap(mDotOK, nodeHit.x - ((mDotOK.getWidth()/2) - (mNodeBitmap.getWidth()/2)), nodeHit.y - ((mDotOK.getHeight()/2) - (mNodeBitmap.getHeight()/2)), null);
}
}
this.destroyDrawingCache();
}
}
#Override
public boolean onTouchEvent(MotionEvent event) {
synchronized (mViewThread.getSurefaceHolder()) {
if(event.getAction() == MotionEvent.ACTION_DOWN)
{
//System.out.println("Action downE x: " + event.getX() + " y: " + event.getY());
for (int i = 0; i < mGridNodesCount; i++)
{
for (int j = 0; j < mGridNodesCount; j++)
{
//TODO - changed
//PathPoint pathPoint = mNodeCoordinates[i][j];
mPathPoint = mNodeCoordinates[i][j];
if((Math.abs((int)event.getX() - mPathPoint.x) <= 35) && (Math.abs((int)event.getY() - mPathPoint.y) <= 35))
{
//mPath.moveTo(pathPoint.x + mBitmap.getWidth() / 2, pathPoint.y + mBitmap.getHeight() / 2);
//System.out.println("Action down x: " + pathPoint.x + " y: " + pathPoint.y);
ArrayList<PathPoint> newNodesPattern = new ArrayList<PathPoint>();
mNodesHitPatterns.add(newNodesPattern);
//mNodesHitPatterns.add(nh);
//pathPoint.setAction("down");
break;
}
}
}
}
else if(event.getAction() == MotionEvent.ACTION_MOVE)
{
final int historySize = event.getHistorySize();
//System.out.println("historySize: " + historySize);
//System.out.println("Action moveE x: " + event.getX() + " y: " + event.getY());
coordinateFound:
for (int i = 0; i < mGridNodesCount; i++)
{
for (int j = 0; j < mGridNodesCount; j++)
{
//TODO - changed
//PathPoint pathPoint = mNodeCoordinates[i][j];
mPathPoint = mNodeCoordinates[i][j];
if((Math.abs((int)event.getX() - mPathPoint.x) <= 35) && (Math.abs((int)event.getY() - mPathPoint.y) <= 35))
{
int lastPatternIndex = mNodesHitPatterns.size()-1;
ArrayList<PathPoint> lastPattern = mNodesHitPatterns.get(lastPatternIndex);
int lastPatternLastNode = lastPattern.size()-1;
if(lastPatternLastNode != -1)
{
if(!mPathPoint.equals(lastPattern.get(lastPatternLastNode).x, lastPattern.get(lastPatternLastNode).y))
{
lastPattern.add(mPathPoint);
//System.out.println("Action moveC [add point] x: " + pathPoint.x + " y: " + pathPoint.y);
}
}
else
{
lastPattern.add(mPathPoint);
//System.out.println("Action moveC [add point] x: " + pathPoint.x + " y: " + pathPoint.y);
}
break coordinateFound;
}
else //no current match => try historical
{
if(historySize > 0)
{
for (int k = 0; k < historySize; k++)
{
//System.out.println("Action moveH x: " + event.getHistoricalX(k) + " y: " + event.getHistoricalY(k));
if((Math.abs((int)event.getHistoricalX(k) - mPathPoint.x) <= 35) && (Math.abs((int)event.getHistoricalY(k) - mPathPoint.y) <= 35))
{
int lastPatternIndex = mNodesHitPatterns.size()-1;
ArrayList<PathPoint> lastPattern = mNodesHitPatterns.get(lastPatternIndex);
int lastPatternLastNode = lastPattern.size()-1;
if(lastPatternLastNode != -1)
{
if(!mPathPoint.equals(lastPattern.get(lastPatternLastNode).x, lastPattern.get(lastPatternLastNode).y))
{
lastPattern.add(mPathPoint);
//System.out.println("Action moveH [add point] x: " + pathPoint.x + " y: " + pathPoint.y);
}
}
else
{
lastPattern.add(mPathPoint);
//System.out.println("Action moveH [add point] x: " + pathPoint.x + " y: " + pathPoint.y);
}
break coordinateFound;
}
}
}
}
}
}
}
else if(event.getAction() == MotionEvent.ACTION_UP)
{
// for (int i = 0; i < mGridSize; i++) {
//
// for (int j = 0; j < mGridSize; j++) {
//
// PathPoint pathPoint = mNodeCoordinates[i][j];
//
// if((Math.abs((int)event.getX() - pathPoint.x) <= 35) && (Math.abs((int)event.getY() - pathPoint.y) <= 35))
// {
// //the location of the node
// //mPath.lineTo(pathPoint.x + mBitmap.getWidth() / 2, pathPoint.y + mBitmap.getHeight() / 2);
//
// //System.out.println("Action up x: " + pathPoint.x + " y: " + pathPoint.y);
//
// //mGraphics.add(mPath);
// // mNodesHit.add(pathPoint);
// // pathPoint.setAction("up");
// break;
// }
// }
// }
}
//System.out.println(mNodesHitPatterns.toString());
//create mPath
for (ArrayList<PathPoint> nodePattern : mNodesHitPatterns)
{
for (int i = 0; i < nodePattern.size(); i++)
{
if(i == 0) //first node in pattern
{
mPath.moveTo(nodePattern.get(i).x + mNodeBitmapWidthCenter, nodePattern.get(i).y + mNodeBitmapHeightCenter);
}
else
{
mPath.lineTo(nodePattern.get(i).x + mNodeBitmapWidthCenter, nodePattern.get(i).y + mNodeBitmapWidthCenter);
}
//mGraphics.add(mPath);
}
}
mGraphics.add(mPath);
return true;
}
}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Class "ViewThread" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
public class ViewThread extends Thread {
private Panel mPanel;
private SurfaceHolder mHolder;
private boolean mRun = false;
public ViewThread(Panel panel) {
mPanel = panel;
mHolder = mPanel.getHolder();
}
public void setRunning(boolean run) {
mRun = run;
}
public SurfaceHolder getSurefaceHolder()
{
return mHolder;
}
#Override
public void run()
{
Canvas canvas = null;
while (mRun)
{
canvas = mHolder.lockCanvas();
//canvas = mHolder.lockCanvas(new Rect(50, 50, 150, 150));
if (canvas != null)
{
if(!mPanel.getNodesGridDrawn())
{
mPanel.drawNodesGrid(canvas);
}
mPanel.doDraw(canvas);
mHolder.unlockCanvasAndPost(canvas);
}
}
}
}
It's just the idea, but I would try to take all the declarations out of the loops. I know that it can be useful to have them localized, however it's usually really time consuming so it could help a little. My second idea was already tested by you in your update so now I am also curious how it will go ;)
You are using a SurfaceView? First of all, I recommend you to use a graphic library for your game... AndEngine for example is pretty easy to use and you will achieve to develop a much more beautiful game than using the Java canvas. The performance is better too.
I can´t find anything wrong with your code, but there is a lot of processing in the draw method, and more important, in the onTouch event. You should avoid to use divisions or heavy math operations in the loops and try to pre-calculate everything before.
But I insist; for something like what you are doing, take a look at this and you will have it up and running in no time!