Atlas: Partial porting of the Terrain section to JS.
Slightly nicer rendering of circular brush previews. This was SVN commit r5168.
This commit is contained in:
parent
c113743348
commit
52aa29681c
@ -1,28 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<lists>
|
||||
|
||||
<attachpoints>
|
||||
<item> head </item>
|
||||
<item> helmet </item>
|
||||
<item> head_extra </item>
|
||||
<item> l_hand </item>
|
||||
<item> shield </item>
|
||||
<item> l_forearm </item>
|
||||
<item> l_shoulder </item>
|
||||
<item> r_hand </item>
|
||||
<item> r_forearm </item>
|
||||
<item> r_shoulder </item>
|
||||
<item> chest </item>
|
||||
<item> back </item>
|
||||
<item> shoulders </item>
|
||||
<item> l_leg </item>
|
||||
<item> r_leg </item>
|
||||
<item> l_hip </item>
|
||||
<item> r_hip </item>
|
||||
<item> tree </item>
|
||||
<item> props_main </item>
|
||||
<item> props_fancy </item>
|
||||
<item>head </item>
|
||||
<item>helmet </item>
|
||||
<item>head_extra </item>
|
||||
<item>l_hand </item>
|
||||
<item>shield </item>
|
||||
<item>l_forearm </item>
|
||||
<item>l_shoulder </item>
|
||||
<item>r_hand </item>
|
||||
<item>r_forearm </item>
|
||||
<item>r_shoulder </item>
|
||||
<item>chest </item>
|
||||
<item>back </item>
|
||||
<item>shoulders </item>
|
||||
<item>l_leg </item>
|
||||
<item>r_leg </item>
|
||||
<item>l_hip </item>
|
||||
<item>r_hip </item>
|
||||
<item>tree </item>
|
||||
<item>props_main </item>
|
||||
<item>props_fancy</item>
|
||||
</attachpoints>
|
||||
|
||||
<animations>
|
||||
@ -54,5 +52,5 @@
|
||||
<colour name="Player 7" rgb="207 159 111" buttontext="255 255 255"/>
|
||||
<colour name="Player 8" rgb="159 159 159" buttontext="255 255 255"/>
|
||||
</playercolours>
|
||||
|
||||
</lists>
|
||||
|
||||
</lists>
|
||||
|
142
binaries/data/tools/atlas/scripts/section/terrain.js
Normal file
142
binaries/data/tools/atlas/scripts/section/terrain.js
Normal file
@ -0,0 +1,142 @@
|
||||
var brushShapes = {
|
||||
'circle': {
|
||||
width: function (size) { return size },
|
||||
height: function (size) { return size },
|
||||
data: function (size) {
|
||||
var data = [];
|
||||
// All calculations are done in units of half-tiles, since that
|
||||
// is the required precision
|
||||
var mid_x = size-1;
|
||||
var mid_y = size-1;
|
||||
var scale = 1 / (Math.sqrt(2) - 1);
|
||||
for (var y = 0; y < size; ++y)
|
||||
{
|
||||
for (var x = 0; x < size; ++x)
|
||||
{
|
||||
var dist_sq = // scaled to 0 in centre, 1 on edge
|
||||
((2*x - mid_x)*(2*x - mid_x) +
|
||||
(2*y - mid_y)*(2*y - mid_y)) / (size*size);
|
||||
if (dist_sq <= 1)
|
||||
data.push((Math.sqrt(2 - dist_sq) - 1) * scale);
|
||||
else
|
||||
data.push(0);
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
},
|
||||
|
||||
'square': {
|
||||
width: function (size) { return size },
|
||||
height: function (size) { return size },
|
||||
data: function (size) {
|
||||
var data = [];
|
||||
for (var i = 0; i < size*size; ++i)
|
||||
data.push(1);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var brush = {
|
||||
shape: brushShapes['circle'],
|
||||
size: 4,
|
||||
strength: 1.0,
|
||||
active: false,
|
||||
send: function () {
|
||||
Atlas.Message.Brush(
|
||||
this.shape.width(this.size),
|
||||
this.shape.height(this.size),
|
||||
this.shape.data(this.size)
|
||||
);
|
||||
// TODO: rather than this hack to make things interact correctly with C++ tools,
|
||||
// implement the tools in JS and do something better
|
||||
Atlas.SetBrushStrength(this.strength);
|
||||
}
|
||||
};
|
||||
|
||||
function init(window)
|
||||
{
|
||||
window.sizer = new wxBoxSizer(wxOrientation.VERTICAL);
|
||||
|
||||
var tools = [
|
||||
/* text label; internal tool name; button */
|
||||
[ 'Modify', 'AlterElevation', undefined ],
|
||||
[ 'Flatten', 'FlattenElevation', undefined ],
|
||||
[ 'Paint', 'PaintTerrain', undefined ]
|
||||
];
|
||||
var selectedTool = null; // null if none selected, else an element of 'tools'
|
||||
|
||||
var toolSizer = new wxStaticBoxSizer(new wxStaticBox(window, -1, 'Elevation tools'), wxOrientation.HORIZONTAL);
|
||||
window.sizer.add(toolSizer, 0, wxStretch.EXPAND);
|
||||
for each (var tool in tools)
|
||||
{
|
||||
var button = new wxButton(window, -1, tool[0]);
|
||||
toolSizer.add(button, 1);
|
||||
tool[2] = button;
|
||||
|
||||
// Explicitly set the background to the default colour, so that the button
|
||||
// is always owner-drawn (by the wxButton code), rather than initially using the
|
||||
// native (standard colour) button appearance then changing inconsistently later.
|
||||
button.backgroundColour = wxSystemSettings.getColour(wxSystemSettings.COLOUR_BTNFACE);
|
||||
|
||||
(function(tool) { // (local scope)
|
||||
button.onClicked = function () {
|
||||
if (selectedTool == tool)
|
||||
{
|
||||
// Clicking on one tool twice should disable it
|
||||
selectedTool = null;
|
||||
this.backgroundColour = wxSystemSettings.getColour(wxSystemSettings.COLOUR_BTNFACE);
|
||||
Atlas.SetCurrentTool('');
|
||||
}
|
||||
else
|
||||
{
|
||||
if (selectedTool)
|
||||
selectedTool[2].backgroundColour = wxSystemSettings.getColour(wxSystemSettings.COLOUR_BTNFACE);
|
||||
selectedTool = tool;
|
||||
this.backgroundColour = new wxColour(0xEE, 0xCC, 0x55);
|
||||
Atlas.SetCurrentTool(tool[1]);
|
||||
brush.send();
|
||||
}
|
||||
};
|
||||
})(tool);
|
||||
}
|
||||
|
||||
var brushSizer = new wxStaticBoxSizer(new wxStaticBox(window, -1, 'Brush'), wxOrientation.VERTICAL);
|
||||
window.sizer.add(brushSizer);
|
||||
|
||||
var shapes = [
|
||||
[ 'Circle', brushShapes['circle'] ],
|
||||
[ 'Square', brushShapes['square'] ]
|
||||
];
|
||||
var shapeNames = [];
|
||||
for each (var s in shapes) shapeNames.push(s[0]);
|
||||
var shapeBox = new wxRadioBox(window, -1, 'Shape', wxDefaultPosition, wxDefaultSize, shapeNames);
|
||||
brushSizer.add(shapeBox);
|
||||
shapeBox.onRadioBox = function(evt)
|
||||
{
|
||||
brush.shape = shapes[evt.integer][1];
|
||||
brush.send();
|
||||
};
|
||||
|
||||
var brushSettingsSizer = new wxFlexGridSizer(2);
|
||||
|
||||
brushSizer.add(brushSettingsSizer);
|
||||
brushSettingsSizer.add(new wxStaticText(window, -1, 'Size'), 0, wxAlignment.RIGHT);
|
||||
var sizeSpinner = new wxSpinCtrl(window, -1, 4, wxDefaultPosition, wxDefaultSize, wxSpinCtrl.ARROW_KEYS, 1, 100);
|
||||
brushSettingsSizer.add(sizeSpinner);
|
||||
sizeSpinner.onSpinCtrl = function(evt)
|
||||
{
|
||||
brush.size = evt.position;
|
||||
brush.send();
|
||||
};
|
||||
|
||||
brushSettingsSizer.add(new wxStaticText(window, -1, 'Strength'), wxAlignment.RIGHT);
|
||||
var strengthSpinner = new wxSpinCtrl(window, -1, 10, wxDefaultPosition, wxDefaultSize, wxSpinCtrl.ARROW_KEYS, 1, 100);
|
||||
brushSettingsSizer.add(strengthSpinner);
|
||||
strengthSpinner.onSpinCtrl = function(evt)
|
||||
{
|
||||
brush.strength = evt.position / 10;
|
||||
brush.send();
|
||||
};
|
||||
}
|
@ -26,61 +26,130 @@
|
||||
#include <boost/preprocessor/punctuation/comma_if.hpp>
|
||||
#include <boost/preprocessor/repetition/repeat.hpp>
|
||||
|
||||
#define FAIL(msg) do { JS_ReportError(cx, msg); return false; } while (false)
|
||||
|
||||
const int RUNTIME_SIZE = 1024*1024; // TODO: how much memory is needed?
|
||||
const int STACK_CHUNK_SIZE = 8192;
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
template<typename T> bool ScriptInterface::FromJSVal(JSContext* cx, jsval WXUNUSED(v), T& WXUNUSED(out))
|
||||
namespace
|
||||
{
|
||||
JS_ReportError(cx, "Unrecognised argument type");
|
||||
// TODO: SetPendingException turns the error into a JS-catchable exception,
|
||||
// but the error report doesn't say anything useful like the line number,
|
||||
// so I'm just using ReportError instead for now (and failures are uncatchable
|
||||
// and will terminate the whole script)
|
||||
//JS_SetPendingException(cx, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, "Unrecognised argument type")));
|
||||
return false;
|
||||
// Use templated structs instead of functions, so that we can use partial specialisation:
|
||||
|
||||
template<typename T> struct FromJSVal
|
||||
{
|
||||
static bool Convert(JSContext* cx, jsval WXUNUSED(v), T& WXUNUSED(out))
|
||||
{
|
||||
JS_ReportError(cx, "Unrecognised argument type");
|
||||
// TODO: SetPendingException turns the error into a JS-catchable exception,
|
||||
// but the error report doesn't say anything useful like the line number,
|
||||
// so I'm just using ReportError instead for now (and failures are uncatchable
|
||||
// and will terminate the whole script)
|
||||
//JS_SetPendingException(cx, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, "Unrecognised argument type")));
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct FromJSVal<bool>
|
||||
{
|
||||
static bool Convert(JSContext* cx, jsval v, bool& out)
|
||||
{
|
||||
JSBool ret;
|
||||
if (! JS_ValueToBoolean(cx, v, &ret)) return false;
|
||||
out = (ret ? true : false);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct FromJSVal<float>
|
||||
{
|
||||
static bool Convert(JSContext* cx, jsval v, float& out)
|
||||
{
|
||||
jsdouble ret;
|
||||
if (! JS_ValueToNumber(cx, v, &ret)) return false;
|
||||
out = ret;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct FromJSVal<int>
|
||||
{
|
||||
static bool Convert(JSContext* cx, jsval v, int& out)
|
||||
{
|
||||
int32 ret;
|
||||
if (! JS_ValueToInt32(cx, v, &ret)) return false;
|
||||
out = ret;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct FromJSVal<std::wstring>
|
||||
{
|
||||
static bool Convert(JSContext* cx, jsval v, std::wstring& out)
|
||||
{
|
||||
JSString* ret = JS_ValueToString(cx, v); // never returns NULL
|
||||
jschar* ch = JS_GetStringChars(ret);
|
||||
out = std::wstring(ch, ch+JS_GetStringLength(ret));
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct FromJSVal<std::string>
|
||||
{
|
||||
static bool Convert(JSContext* cx, jsval v, std::string& out)
|
||||
{
|
||||
JSString* ret = JS_ValueToString(cx, v); // never returns NULL
|
||||
char* ch = JS_GetStringBytes(ret);
|
||||
out = std::string(ch);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct FromJSVal<wxString>
|
||||
{
|
||||
static bool Convert(JSContext* cx, jsval v, wxString& out)
|
||||
{
|
||||
JSString* ret = JS_ValueToString(cx, v); // never returns NULL
|
||||
jschar* ch = JS_GetStringChars(ret);
|
||||
out = wxString((const char*)ch, wxMBConvUTF16(), JS_GetStringLength(ret)*2);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T> struct FromJSVal<std::vector<T> >
|
||||
{
|
||||
static bool Convert(JSContext* cx, jsval v, std::vector<T>& out)
|
||||
{
|
||||
JSObject* obj;
|
||||
if (! JS_ValueToObject(cx, v, &obj) || obj == NULL || !JS_IsArrayObject(cx, obj))
|
||||
FAIL("Argument must be an array");
|
||||
jsuint length;
|
||||
if (! JS_GetArrayLength(cx, obj, &length))
|
||||
FAIL("Failed to get array length");
|
||||
for (jsint i = 0; i < length; ++i)
|
||||
{
|
||||
jsval el;
|
||||
if (! JS_GetElement(cx, obj, i, &el))
|
||||
FAIL("Failed to read array element");
|
||||
T el2;
|
||||
if (! FromJSVal<T>::Convert(cx, el, el2))
|
||||
return false;
|
||||
out.push_back(el2);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template<> bool ScriptInterface::FromJSVal<bool>(JSContext* cx, jsval v, bool& out)
|
||||
template<typename T> bool ScriptInterface::FromJSVal(JSContext* cx, jsval v, T& out)
|
||||
{
|
||||
JSBool ret;
|
||||
if (! JS_ValueToBoolean(cx, v, &ret)) return false;
|
||||
out = (ret ? true : false);
|
||||
return true;
|
||||
return ::FromJSVal<T>::Convert(cx, v, out);
|
||||
}
|
||||
|
||||
template<> bool ScriptInterface::FromJSVal<float>(JSContext* cx, jsval v, float& out)
|
||||
{
|
||||
jsdouble ret;
|
||||
if (! JS_ValueToNumber(cx, v, &ret)) return false;
|
||||
out = ret;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<> bool ScriptInterface::FromJSVal<int>(JSContext* cx, jsval v, int& out)
|
||||
{
|
||||
int32 ret;
|
||||
if (! JS_ValueToInt32(cx, v, &ret)) return false;
|
||||
out = ret;
|
||||
return true;
|
||||
}
|
||||
|
||||
template<> bool ScriptInterface::FromJSVal<std::wstring>(JSContext* cx, jsval v, std::wstring& out)
|
||||
{
|
||||
JSString* ret = JS_ValueToString(cx, v); // never returns NULL
|
||||
jschar* ch = JS_GetStringChars(ret);
|
||||
out = std::wstring(ch, ch+JS_GetStringLength(ret));
|
||||
return true;
|
||||
}
|
||||
|
||||
template<> bool ScriptInterface::FromJSVal<std::string>(JSContext* cx, jsval v, std::string& out)
|
||||
{
|
||||
JSString* ret = JS_ValueToString(cx, v); // never returns NULL
|
||||
char* ch = JS_GetStringBytes(ret);
|
||||
out = std::string(ch);
|
||||
return true;
|
||||
}
|
||||
// Explicit instantiation of functions that would otherwise be unused in this file
|
||||
// but are required for linking with other files
|
||||
template bool ScriptInterface::FromJSVal<wxString>(JSContext*, jsval, wxString&);
|
||||
|
||||
|
||||
template<> jsval ScriptInterface::ToJSVal<float>(JSContext* cx, const float& val)
|
||||
@ -148,6 +217,14 @@ namespace
|
||||
wxPrintf(_T("wxJS %s: %s\n--------\n"), isWarning ? _T("warning") : _T("error"), logMessage.c_str());
|
||||
}
|
||||
|
||||
// Functions in the Atlas.* namespace:
|
||||
|
||||
JSBool ForceGC(JSContext* cx, JSObject* WXUNUSED(obj), uintN WXUNUSED(argc), jsval* WXUNUSED(argv), jsval* WXUNUSED(rval))
|
||||
{
|
||||
JS_GC(cx);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSBool LoadScript(JSContext* cx, JSObject* WXUNUSED(obj), uintN WXUNUSED(argc), jsval* argv, jsval* rval)
|
||||
{
|
||||
if (! ( JSVAL_IS_STRING(argv[0]) && JSVAL_IS_STRING(argv[1]) ))
|
||||
@ -160,12 +237,8 @@ namespace
|
||||
JS_GetStringChars(code), (uintN)JS_GetStringLength(code),
|
||||
JS_GetStringBytes(name), rval);
|
||||
}
|
||||
|
||||
JSBool ForceGC(JSContext* cx, JSObject* WXUNUSED(obj), uintN WXUNUSED(argc), jsval* WXUNUSED(argv), jsval* WXUNUSED(rval))
|
||||
{
|
||||
JS_GC(cx);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
// Functions in the global namespace:
|
||||
|
||||
JSBool print(JSContext* cx, JSObject* WXUNUSED(obj), uintN argc, jsval* argv, jsval* WXUNUSED(rval))
|
||||
{
|
||||
@ -212,8 +285,8 @@ ScriptInterface_impl::ScriptInterface_impl()
|
||||
JS_DefineFunction(m_cx, m_glob, "print", ::print, 0, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
|
||||
|
||||
m_atlas = JS_DefineObject(m_cx, m_glob, "Atlas", NULL, NULL, JSPROP_READONLY|JSPROP_PERMANENT);
|
||||
JS_DefineFunction(m_cx, m_atlas, "LoadScript", ::LoadScript, 2, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
|
||||
JS_DefineFunction(m_cx, m_atlas, "ForceGC", ::ForceGC, 0, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
|
||||
JS_DefineFunction(m_cx, m_atlas, "LoadScript", ::LoadScript, 2, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
|
||||
|
||||
RegisterMessages(m_atlas);
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ public:
|
||||
|
||||
// Defined elsewhere:
|
||||
// template <TR, T0..., TR (*fptr) (T0...)>
|
||||
// void RegisterFunction(const wxString& functionName);
|
||||
// void RegisterFunction(const char* functionName);
|
||||
// (NOTE: The return type must be defined as a ToJSVal<TR> specialisation
|
||||
// in ScriptInterface.cpp, else you'll end up with linker errors.)
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "AtlasScript/ScriptInterface.h"
|
||||
|
||||
#include "Tools/Common/Tools.h"
|
||||
#include "Tools/Common/Brushes.h"
|
||||
|
||||
static HighResTimer g_Timer;
|
||||
|
||||
@ -262,6 +263,21 @@ END_EVENT_TABLE()
|
||||
static AtlasWindowCommandProc g_CommandProc;
|
||||
AtlasWindowCommandProc& ScenarioEditor::GetCommandProc() { return g_CommandProc; }
|
||||
|
||||
namespace
|
||||
{
|
||||
// Wrapper function because SetCurrentTool takes an optional argument, which JS doesn't like
|
||||
void SetCurrentTool_script(wxString name)
|
||||
{
|
||||
SetCurrentTool(name);
|
||||
}
|
||||
|
||||
// TODO: see comment in terrain.js, and remove this when/if it's no longer necessary
|
||||
void SetBrushStrength(float strength)
|
||||
{
|
||||
g_Brush_Elevation.SetStrength(strength);
|
||||
}
|
||||
}
|
||||
|
||||
ScenarioEditor::ScenarioEditor(wxWindow* parent, ScriptInterface& scriptInterface)
|
||||
: wxFrame(parent, wxID_ANY, _T(""), wxDefaultPosition, wxSize(1024, 768))
|
||||
, m_FileHistory(_T("Scenario Editor")), m_ScriptInterface(scriptInterface)
|
||||
@ -281,6 +297,8 @@ ScenarioEditor::ScenarioEditor(wxWindow* parent, ScriptInterface& scriptInterfac
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Script interface functions
|
||||
GetScriptInterface().RegisterFunction<wxString, Datafile::GetDataDirectory>("GetDataDirectory");
|
||||
GetScriptInterface().RegisterFunction<void, wxString, SetCurrentTool_script>("SetCurrentTool");
|
||||
GetScriptInterface().RegisterFunction<void, float, SetBrushStrength>("SetBrushStrength");
|
||||
|
||||
{
|
||||
const wxString relativePath (_T("tools/atlas/scripts/main.js"));
|
||||
|
@ -287,7 +287,7 @@ void SectionLayout::Build(ScenarioEditor& scenarioEditor)
|
||||
m_PageMappings.insert(std::make_pair(name, (int)m_SidebarBook->GetPageCount()-1));
|
||||
|
||||
ADD_SIDEBAR_SCRIPT(_T("map"), _T("map.png"), _("Map"));
|
||||
//ADD_SIDEBAR_SCRIPT(_T("terrain"), _T("terrain.png"), _("Terrain"));
|
||||
ADD_SIDEBAR_SCRIPT(_T("terrain"), _T("terrain.png"), _("Terrain"));
|
||||
ADD_SIDEBAR(TerrainSidebar, _T("terrain.png"), _("Terrain"));
|
||||
ADD_SIDEBAR(ObjectSidebar, _T("object.png"), _("Object"));
|
||||
ADD_SIDEBAR(EnvironmentSidebar, _T("environment.png"), _("Environment"));
|
||||
|
@ -92,6 +92,11 @@ float Brush::GetStrength() const
|
||||
return m_Strength;
|
||||
}
|
||||
|
||||
void Brush::SetStrength(float strength)
|
||||
{
|
||||
m_Strength = strength;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class BrushShapeCtrl : public wxRadioBox
|
||||
|
@ -21,6 +21,7 @@ public:
|
||||
std::vector<float> GetData() const;
|
||||
|
||||
float GetStrength() const;
|
||||
void SetStrength(float strength);
|
||||
|
||||
void CreateUI(wxWindow* parent, wxSizer* sizer);
|
||||
|
||||
|
@ -40,9 +40,10 @@ public:
|
||||
float avg = (
|
||||
m_Brush->Get(i-i0, j-j0) + m_Brush->Get(i-i0+1, j-j0) +
|
||||
m_Brush->Get(i-i0, j-j0+1) + m_Brush->Get(i-i0+1, j-j0+1)
|
||||
) / 4.f;
|
||||
) / 4.f;
|
||||
RenderTile(CColor(0, 1, 0, avg*0.8f), false);
|
||||
RenderTileOutline(CColor(1, 1, 1, 0.4f), 1, true);
|
||||
if (avg > 0.1f)
|
||||
RenderTileOutline(CColor(1, 1, 1, std::min(0.4f, avg-0.1f)), 1, true);
|
||||
}
|
||||
|
||||
const AtlasMessage::Brush* m_Brush;
|
||||
@ -64,6 +65,8 @@ void Brush::SetData(int w, int h, const std::vector<float>& data)
|
||||
m_H = h;
|
||||
|
||||
m_Data = data;
|
||||
|
||||
debug_assert(data.size() == w*h);
|
||||
}
|
||||
|
||||
void Brush::GetCentre(int& x, int& y) const
|
||||
|
Loading…
Reference in New Issue
Block a user