what can we do with mxs arrays? (SDK)


#1

there is a weak link in many my scripts. it’s a using of arrays. it leaks badly.

what could replace the mxs array and don’t leak in memory? any ideas?

actually i’m thinking about writing a custom class which will not leak.


#2

my guess is it’s not the array as such that leaks but the value class it inherits from and the handle it uses to store the data, so if your own array class is going to work like the existing mxs class and use an array of value pointers so it can store any mxs type then it will still leak. The alternative is dedicated array classes for floats/integers/references/point3/matrix3 etc which then become a nightmare to maintain but might not leak as much. As for writing your own classes then the BigMatrix Value in the mxsagni project is a good place to start (ExtClass.cpp & ExtClass.h), again it looks like a nightmare of getting your includes in the correct order!


#3

If I had a better knowledge of the SDK I would try to help, but I can see what Klvnk said about having dedicated arrays for each class being a nightmare to maintain.

Just out of curiosity, do you have a short sample code to test?


#4

the idea with custom class works… here is a simple template for FloatTab class:

/******************************************* HEADER (.h) *********************************************/

applyable_class_debug_ok (FloatTabValue)

class FloatTabValue : public Value
{
private: 
	Value* _parameter;
public:
	Tab<float> tab;

	FloatTabValue();
	FloatTabValue(Tab<float> t);
	FloatTabValue(int count);
	
	ValueMetaClass* local_base_class() { return class_tag(FloatTabValue); } // local base class in this class's plug-in
	classof_methods(FloatTabValue, Value);

	Array* AsArray()
	{
		Array* arr = new Array(0);
		for (int k=0; k < tab.Count(); k++) arr->append(Float::intern(tab[k]));
		return arr;
	}

	void		collect() { delete this; }
	void		sprin1(CharStream* s);
	void		gc_trace();
#	define		is_floattab(v) ((DbgVerify(!is_sourcepositionwrapper(v)), (v))->tag == class_tag(FloatTabValue))
	Tab<float>	to_floattab() { return tab; }

	Value* operator[](const int i) const { return Float::intern(tab[i]); }

	def_prop_getter (count);

	def_generic	(get,			"get");
	def_generic	(put,			"put");
};

#endif //_FLT_TAB_MXS_CLASS


/******************************************* SOURCE (.cpp) *********************************************/

visible_class_instance (FloatTabValue, "FloatTab")
BOOL is_floatTabValue(Value* val)
{
	return (val->is_kind_of(class_tag(FloatTabValue))) ;
}

Value*
FloatTabValueClass::apply(Value** arg_list, int count, CallContext* cc)
{
	switch (count)
	{
		case 0: 
		{
			return new FloatTabValue();
		}
		case 1: 
		{
			Value* v0 = arg_list[0]->eval()->get_heap_ptr();

			if (is_array(v0)) 
			{
				Array* arr = (Array*)v0;

				Tab<float> tab;
				tab.SetCount(arr->size);
				for (int k=0; k < arr->size; k++) tab[k] = arr->data[k]->to_float();
				return new FloatTabValue(tab);
			}
			if (is_number(v0)) 
			{
				return new FloatTabValue(v0->to_int());
			}
			break;
		}
		default: 
			check_arg_count(FloatTabValue, 1, count);
			break;
	}
	return &undefined;
}

// -------------------- FloatTabValue methods -------------------------------- 

FloatTabValue::FloatTabValue()
{
	tag = class_tag(FloatTabValue);
}
FloatTabValue::FloatTabValue(Tab<float> tab)
{
	tag = class_tag(FloatTabValue);
	this->tab = tab;
}
FloatTabValue::FloatTabValue(int count)
{
	tag = class_tag(FloatTabValue);
	this->tab.SetCount(count, 1);
	for (int k=0; k < count; k++) this->tab[k] = 0.0f;
}

void
FloatTabValue::sprin1(CharStream* s)
{
	s->puts(_M("FloatTab["));
	Value* count = Integer::intern(this->tab.Count());
	Array* arr = this->AsArray();
	count->sprin1(s);
	s->puts(_M("] "));	
	arr->sprin1(s);
	//s->puts(_M(""));	
}

Value*
FloatTabValue::get_vf(Value** arg_list, int count)
{
	check_arg_count(get, 2, count + 1);
	Value* arg = arg_list[0];
	if (is_number(arg)) 
	{
		int	index = arg->to_int() - 1;
		if (index > -1 && index < this->tab.Count())
		{
			return Float::intern(this->tab[index]);
		}
	}
	return &undefined;  // array indexes are 1-based !!!
}
Value*
FloatTabValue::put_vf(Value** arg_list, int count)
{
	check_arg_count(get, 3, count + 1);
	if (is_number(arg_list[0]) && is_number(arg_list[1])) 
	{
		int index = arg_list[0]->to_int() - 1;
		float val = arg_list[1]->to_float();
		if (index > -1)
		{
			if (index >= this->tab.Count()) this->tab.SetCount(index+1);
			this->tab[index] = val;
			return &ok; //Float::intern(val);
		}
	}
	return &undefined;  // array indexes are 1-based !!!
}

void
FloatTabValue::gc_trace()
{
	Value::gc_trace();
}

Value*
FloatTabValue::get_count(Value** arg_list, int count)
{
	return Integer::intern((this->tab).Count());
}

it doesn’t leak at all!

a = floattab 1000000
for k=1 to a.count do a[k] = k
--time:562 memory:0L 

a = #()
a.count = 1000000
for k=1 to a.count do a[k] = k
--time:760 memory:55992776L

#5

nice, though I don’t like the built in sdk tab, IIRC it pre allocates to a 100 items and always seems a bit “awkward” and limited so I’ve taken to using stl with a custom max heap allocator.


#6

i will stay with sdk tabs. i need a solution right now for Point2(3,4) and float.


#7

you may want to add the following to your class

//in the class def
 
 def_prop_setter(count);
 use_generic(free, "free");
 
 // in the cpp
 
 Value* FloatTabValue::free_vf(Value** arg_list, int count)
 {
 	check_arg_count(free, 1, count + 1);
 	tab.ZeroCount(); 
 	return &ok;  
 }
 
 Value* FloatTabValue::set_count(Value** arg_list, int count)
 {
 	check_arg_count(get, 3, count + 1);
 	int newtabcount = arg_list[0]->to_int();
 	if (newtabcount < 0)
 		throw RuntimeError("Positive values only", arg_list[0]);
 	tab.SetCount(newtabcount);
 	return &ok;
 }

that way you can clean up memory without waiting for the floattab to go out of scope.


 a = floattab 1000000 
for k=1 to a.count do a[k] = k
 free a;

though using free on your standard array would probably cure your leak :wink:


#8

i was just experimenting with FloatTabValue… the goal is Point3TabValue.
for fast and leaking free point3 operations:

collect mesh verts positions (Point3TabValue(Mesh* mesh))

transform buffer

append if unique (with tolerance)

make unique (with tolerance)

search a value (with tolerance)

round (with tolerance)

sort by component

sort by distance to specified point

union

intersect

everything is almost done. it works very fast with zero memory use.


#9

I failed to find the correct includes to compile the above example so I have a question.

Can the above class be extended to have public methods? So it could be used in mxs like this:

ptTab = point3Tab()
ptTab.fromMeshVerts (snapshotAsMesh selection[1])
ptTab.Add [0,0,0]
avg = ptTab.Average()
minmax = ptTab.MinMax()

Any little example of a class with one property and a method would be just great.
I created a class visible to mxs using Klvnk’s kd3tree class with working property getters and setters, but how to define a single public method is still a problem for me.


#10

in my class it’s already implemented as:


pp = point3tab <mesh or node> local:bool transform:matrix3

-- or

pp = point3tab()
append pp <mesh>

append pp <point3 or (array of point3) or point3tab or mesh> start:int count:int

pp.center -- geometry center
pp.min -- minimum for all three compoments
pp.max -- maximum for all three compoments

pp.pivot -- average

coerce:
pp as mesh --(needs faces list as parameter)
pp as array -- (or pp.value)
pp as floattab
pp as bbox3
-- and more

#11

Ok. Guess I wasn’t clear enough.
As I understand, your example is still about class instance property getters/setters and not about a class instance methods.

And that’s the way I’d like my class to function… very similar to RayMeshgridIntersect

obj = MyClass()
obj.AddNode $teapot001
obj.SetGridSize 10 20 10
obj.GetNearest [10,20,30]
...

ps. I’ve seen that RayMeshgridIntersect is derived from FPMixinInterface, but is it possible to have methods accessible from mxs in a class that derive from Value?


#12

the only way to organize and publish class methods in mxs is to use the mixin interfaces, but calling these methods in the “simple” mxs syntax results in a loss of performance and memory:

for example:

delete objects
b = converttopoly (box())
gc()

-- 'plain' syntax:
(
	t0 = timestamp()
	h0 = heapfree

	for k=1 to 1000 do
	(
		b.selectHardEdges()
	)

	format "#1 time:% heap:%\n" (timestamp() - t0) (h0 - heapfree)
)
-- the very well known trick:
(
	t0 = timestamp()
	h0 = heapfree

	selectHardEdges = b.selectHardEdges
	for k=1 to 1000 do
	(
		selectHardEdges()
	)

	format "#2 time:% heap:%\n" (timestamp() - t0) (h0 - heapfree)
)

that’s why I don’t like to use interfaces


#13

in SDK of course I have class methods for this mxs classes, and use them. but I don’t publish them for MXS.
if I wanted to publish them, I would have to add an interface to code that I absolutely do not need in relation to the SDK


#14

Thanks a lot.
Now it’s at least clear why I couldn’t make my methods exposed to mxs.


#15

Worth mentioning there’s no need for that trick as you can always use

Editable_Poly.selectHardEdges b

#16

I finally made it using the example by Rodrigue Cloutier provided in ancient Descreet Sparks chm.

sparks_archive.chm (3.3 MB) source

ps. Why there’s still no sticky thread for c++ faq ?


#17

… solved