Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added a new 'snap' function to SimpleNumber that quantizes values #3160

Merged
merged 28 commits into from
Sep 20, 2017

Conversation

cianoc
Copy link
Contributor

@cianoc cianoc commented Sep 4, 2017

See:
#2826

Added a new snap function which 'quantizes' values. It's really just the existing quantize function, but which does the expected thing.

As part of this PR I added help for snap, added some clarification to quantize and also added a unit test for this new function.

I welcome discussion on the best name for this function, as well as what to do about the old 'quantize' function.

@telephon
Copy link
Member

telephon commented Sep 4, 2017

It would be good to add a comparison between quantize and snap, showing how both can be useful.

I could also imagine a quantize function that, if passed a negative quantum, would result in the snap behavior. This would be a typical way to formulate such symmetric distinctions.

@cianoc
Copy link
Contributor Author

cianoc commented Sep 4, 2017

Well quantize will change any values bigger than (quantum+-tolerance) while snap will change any value smaller than (quantum+-tolerance). So I can certainly add that. There was talk of deprecating quantize just because it's horribly named (my proposed function 'snap' is what quantize should have been). Of course in an ideal world my new function would be 'quantize', and the current 'quantize' function would probably be called 'snap'. Backwards compatibility is annoying sometimes.

I'd also like to change the parameter name for quantize. I proposed 'grid' for snap, as it relates to the idea of a musical grid of beats (e.g. a sequencer/DAW). I really don't love that, but quantum is quite mathy and scary to non-math people (which is probably most users of SuperCollider, and certainly the ones most intimidated by it in the first place).

@mossheim
Copy link
Contributor

mossheim commented Sep 4, 2017

Quantize should be deprecated, for the reasons given in #2826:

  • quantize does not do what most people will expect; in other words, its name is confusing
  • what it does do is not a needed function for a vast majority of users
  • it appears that quantize was incorrectly written, but needs to maintain backwards compatibility
  • we should strongly encourage users to use a correctly behaving function rather than an incorrectly behaving function, and the best way to do that is through deprecation of the incorrect function

A possible alternative is to deprecate quantize and add a second function which does the same thing but is more appropriately named. However, I think you'll have a hard time coming up with a name for it, or even an example showing its utility. IMO there is none. I would rather just deprecate it.

I proposed 'grid' for snap

I think grid is fine, although it isn't immediately intuitive to me. Technically speaking the value isn't a grid, it's the resolution of the grid; EnvelopeView has the same naming problem. I offer 'resolution' for consideration. A longer name is OK here since it's very unlikely someone would type out the parameter name when calling it - in other words, write 3.snap(resolution: 4) instead of just 3.snap(4).

I'd also like to change the parameter name for quantize.

Since quantize will be deprecated, changing the parameter name doesn't do much. I think it should be left as-is.

@telephon
Copy link
Member

telephon commented Sep 4, 2017

If we can imagine another way for a number to snap, the name probably should be snapToGrid. But otherwise I'm fine with snap. I hope nobody complains that a float snapped at him.

@muellmusik
Copy link
Contributor

muellmusik commented Sep 4, 2017 via email

@mossheim
Copy link
Contributor

mossheim commented Sep 4, 2017

I don't have a strong opinion on the name, but let's try to suggest concrete alternatives so the discussion doesn't derail.

@muellmusik
Copy link
Contributor

muellmusik commented Sep 4, 2017 via email

@telephon
Copy link
Member

telephon commented Sep 5, 2017

we have roundUp and roundDown. Maybe something analog? Like roundToward?

On the other hand, snap isn't a bad name for what it does.

I can imagine many other rounding to grid functions, but they can almost always better be written directly as a function.

@patrickdupuis
Copy link
Contributor

roundAbout ?

... sorry couldn't help myself

@telephon
Copy link
Member

telephon commented Sep 5, 2017

(I also needed to hold myself back!)

@jamshark70
Copy link
Contributor

BTW, I never published this extension, but I use it all the time for FM modulator ratios:

SnapControlSpec : ControlSpec {
	var <>snap = 0.05;
	map { arg value;
		// maps a value from [0..1] to spec range
		var out = warp.map(value.clip(0.0, 1.0)),
		rounded = out.round(step);
		if((out absdif: rounded) <= snap) { ^rounded } { ^out }
	}
	unmap { arg value;
		// maps a value from spec range to [0..1]
		var rounded = value.round(step);
		if((value absdif: rounded) <= snap) { value = rounded };
		^warp.unmap(value.clip(clipLo, clipHi));
	}
	constrain { arg value;
		var rounded;
		value = value.asFloat.clip(clipLo, clipHi);
		rounded = value.round(step);
		if((value absdif: rounded) <= snap) { ^rounded } { ^value }
	}
}

SnapControlSpec(0.5, 20, \lin, 0.5).snap_(0.05), e.g.

Or a panner with an indent in the center: SnapControlSpec(-1, 1, \lin, 1).snap_(0.05)

@cianoc
Copy link
Contributor Author

cianoc commented Sep 5, 2017

I offer 'resolution' for consideration. A longer name is OK here since it's very unlikely someone would type out the parameter name when calling it - in other words, write 3.snap(resolution: 4) instead of just 3.snap(4).

This seems like a no-brainer. Resolution it is. Nice one.

@cianoc
Copy link
Contributor Author

cianoc commented Sep 5, 2017

roundToward isn't bad. roundWithTolerance would be perfect if it wasn't so long. gradualRound? There are two hard problems in Computer Science - naming things and cache invalidation.

I'd be inclined if we went down that route to replicate the old quantize function as snap (with better named parameters and help).

@jamshark70
Copy link
Contributor

I won't hold my breath for graphics programs to start to roundToward grid lines instead of snapping to them...

@mossheim
Copy link
Contributor

mossheim commented Sep 5, 2017

my thought after seeing these suggestions is softRound

@cianoc
Copy link
Contributor Author

cianoc commented Sep 5, 2017

I like softRound.

How about I create a copy of quantize called 'snap' which has the behavior that James described (snap to grid lines). And my function is softRound. And I'll add better documentation for 'snap' than the current quantize function.

And quantize is deprecated.

@telephon
Copy link
Member

telephon commented Sep 5, 2017

roundSoftly would be found more easily in autocomplete.

@mossheim
Copy link
Contributor

mossheim commented Sep 5, 2017

naming shouldn't be justified by current autocomplete functionality imho. i'd rather have code that makes sense and fits with naming conventions.

@cianoc
Copy link
Contributor Author

cianoc commented Sep 5, 2017

Well there's currently a round and a roundUp (though not a roundDown - what's 'up' with that). So I guess it would fit into an already existing scheme if it was roundSoftly.

@telephon
Copy link
Member

telephon commented Sep 6, 2017

yes, that's what I actually wanted to say. But I'm fine with most variants.

@cianoc
Copy link
Contributor Author

cianoc commented Sep 6, 2017

So do we think we can achieve some kind of consensus on naming?

I'd like to call it roundSoft, or roundSoftly, just because it would fit into the already existing naming convention for round. And use snap for 'snap' in the sense that James referenced it above. But I'm open to persuasion.

And I'll start a new pull request to add roundDown... Seriously, why's that not in there?

@mossheim
Copy link
Contributor

mossheim commented Sep 6, 2017

if you're looking for a convention, the other things in SC that have "soft" in their names are:

  • PV_SoftWipe
  • softPut
  • softSet
  • softVol_
  • softclip

similarly, there are

  • hardFreeAll
  • hardRun
  • hardStop

@cianoc
Copy link
Contributor Author

cianoc commented Sep 6, 2017

So you had to bring facts to this discussion @brianlheim...

Okay I'll rename the function to softRound, move the help up so that it's in the round section. And I'll create a copy of quantize called snap. And I'll update help appropriately.

Is that cool?

Edited because I wrote the wrong function name (duh).

@cianoc
Copy link
Contributor Author

cianoc commented Sep 6, 2017

new function name softRound, not roundSoftly. Dammit.

@nhthn nhthn added the comp: class library SC class library label Sep 8, 2017
@cianoc
Copy link
Contributor Author

cianoc commented Sep 10, 2017

I still need to rewrite the tests. Everything else should be done.

snap { arg resolution = 1.0, margin = 0.05, strength = 1.0;
var round = round(this, resolution);
var diff = round - this;
if ( abs(diff) < margin ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why are there suddenly spaces in the parens now? also FWIW i think our semiformal style is to not add a space after if since it is also a method and not a true control structure as in many other languages:

if(abs(diff) < margin)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no idea how those spaces got there. Wasn't deliberate.

this.assertEquals(testF.(1,0.2,0.5), [ 0, 0.2, 0.2, 0.8, 0.8, 1, 1.2, 1.2, 1.8, 1.8, 2 ], "Snap test 5 failed.");

}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing an EOL here. we should have a low-priority issue that ensures the IDE always inserts a \n after the last line in the editor, we have way too many faulty commits due to this.

@cianoc
Copy link
Contributor Author

cianoc commented Sep 11, 2017

I think this should address all the issues.

The data now covers all the edge cases (plus a little extra for my OCD paranoia), including negative values. Also have tests for values that should never be entered.

Apparently I don't find user tests soothing :-/

Copy link
Contributor

@mossheim mossheim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks for all the tests, there are still issues though


}

test_Snap {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be lowercase "snap"

var testF = {|vals, g, t, s| vals.collect({|num| num.softRound(g, t, s)})};

val = [-1.3, -1, -0.7, -0.5, -0.3, 0, 0.2, 0.5, 0.7, 1, 1.2, 1.8, 2];
this.assertEquals(testF.(val, 1,0,1), [ -1, -1, -1, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2 ], "softRound test 1 failed.");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

keep in mind that message is posted even on a successful verbose run. The message should describe the test; these currently read like error messages

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and again, testing way too many values IMO, it's very hard to tell what's going on in this method

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, sorry used to unit test libraries that only show the message when it fails. I will modify. I see why people complain about the verbosity of the messages.

Each of the values is testing a very specific edge case where a bug (imo) could creep in. I could split these onto multiple lines, but I'm hesitant to reduce coverage.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you!


// Testing weird edge cases that are probably not typical use cases for backwards compatability purposes.
val = [-1, -0.5, 0, 1, 0.5, 1];
this.assertEquals(testF.(val,0,0,1), val, "softRound test 5 failed."); // resolution value of 0 leave the original array unchanged.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you please put these end of line comments on the line above instead?

Copy link
Contributor

@mossheim mossheim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think this looks great, thank you so much for putting in the effort!

@mossheim
Copy link
Contributor

as an aside, this has definitely given me some good ideas for what to include in a unit testing guide. i'm sorry that you didn't have anything to go off of for this PR, that shouldn't happen in the future.

Copy link
Contributor

@mossheim mossheim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops, didn't notice this before, but you've still got a submodule link at external_libraries/link. Just remove that and we're good to merge.

@cianoc
Copy link
Contributor Author

cianoc commented Sep 18, 2017

That submodule keeps creeping back in. This should be fixed now.

Copy link
Contributor

@mossheim mossheim left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@mossheim
Copy link
Contributor

NB - I realized at the last moment that this PR hadn't actually deprecated quantize yet, so I went ahead and pushed that change to your branch, @cianoc

@mossheim mossheim merged commit ff3ca4f into supercollider:3.9 Sep 20, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
comp: class library SC class library
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants