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

fixes-336: Extend the Ticker interface to allow passing a format function #362

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

DavidGamba
Copy link

@DavidGamba DavidGamba commented May 25, 2017

Fixes #336 This allows for easy wrappers around the plot.DefaultTicks struct with
custom format functions:

    type myCustomFormatTicks struct{}

    var _ plot.Ticker = myCustomFormatTicks{}

    func (myCustomFormatTicks) Ticks(min, max float64, format func(v float64, prec int) string) (ticks []plot.Tick) {
      return plot.DefaultTicks{}.Ticks(min, max, myFormatFloatTick)
    }

    func myFormatFloatTick(v float64, prec int) string {
           return strconv.FormatFloat(floats.Round(v, prec), 'g', 8, 64)
    }

    p.Y.Tick.Marker = myCustomFormatTicks{}

…tion

This allows for easy wrappers around the plot.DefaultTicks struct with
custom format functions:

    type myCustomFormatTicks struct{}

    var _ plot.Ticker = myCustomFormatTicks{}

    func (myCustomFormatTicks) Ticks(min, max float64, format func(v float64, prec int) string) (ticks []plot.Tick) {
      return plot.DefaultTicks{}.Ticks(min, max, myFormatFloatTick)
    }

    func myFormatFloatTick(v float64, prec int) string {
           return strconv.FormatFloat(floats.Round(v, prec), 'g', 8, 64)
    }

    p.Y.Tick.Marker = myCustomFormatTicks{}
gob/gob_test.go Outdated
func (commaTicks) Ticks(min, max float64) []plot.Tick {
tks := plot.DefaultTicks{}.Ticks(min, max)
func (commaTicks) Ticks(min, max float64, format func(v float64, prec int) string) []plot.Tick {
tks := plot.DefaultTicks{}.Ticks(min, max, nil)
Copy link
Member

Choose a reason for hiding this comment

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

you should probably weave the format arg through the call of DefaultTicks, instead of dropping it on the floor

Copy link
Member

Choose a reason for hiding this comment

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

actually, I don't understand why weaving format through all the calls to .Ticks(...) isn't done.

axis.go Outdated
@@ -21,7 +21,7 @@ const displayPrecision = 4
// Ticker creates Ticks in a specified range
type Ticker interface {
// Ticks returns Ticks in a specified range
Copy link
Member

Choose a reason for hiding this comment

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

if the interface is modified, the new argument should also be documented.

axis_test.go Outdated
@@ -42,7 +42,7 @@ func TestAxisSmallTick(t *testing.T) {
want: []string{"0.0001", "0.00011", "0.00012", "0.00013", "0.00014"},
},
} {
ticks := d.Ticks(test.min, test.max)
ticks := d.Ticks(test.min, test.max, nil)
Copy link
Member

Choose a reason for hiding this comment

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

you should also add a test with the new format argument

…tion

* Document the extra argument in the Ticker interface.
* Weave the format arg through the call to DefaultTicks when applicable.
* Add an extra test with the additional argument.
@DavidGamba
Copy link
Author

@sbinet please take another look.

axis.go Outdated
@@ -200,7 +202,7 @@ func (a *horizontalAxis) size() (h vg.Length) {
h -= a.Label.Font.Extents().Descent
h += a.Label.Height(a.Label.Text)
}
if marks := a.Tick.Marker.Ticks(a.Min, a.Max); len(marks) > 0 {
if marks := a.Tick.Marker.Ticks(a.Min, a.Max, nil); len(marks) > 0 {
Copy link
Member

Choose a reason for hiding this comment

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

ok. so how would I use my own format here?

shouldn't Axis grow another field that could be passed there?
something like:

type Axis struct {
   // ...
   Tick struct {
      // ...
      // Marker returns the tick marks.  Any tick marks
      // returned by the Marker function that are not in
      // range of the axis are not drawn.
      Marker Ticker

      // <doc for Format>
      Format func(v float64, prec int) string
   }
}

and then:

if marks := a.Tick.Marker.Ticks(a.Min, a.Max, a.Tick.Format); len(marks) > 0 {

Copy link
Author

Choose a reason for hiding this comment

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

I'll give it a shot 😃 Thanks for the feedback!

@sbinet
Copy link
Member

sbinet commented Jun 20, 2017

@kortschak @eaburns any feedback about this change?

Allows to define a custom Tick format function.
@DavidGamba
Copy link
Author

@sbinet please take another look. I apologize for the delay...
With this, to provide a format to the default ticks, all I have to do is:

...
	p, err := plot.New()
	if err != nil {
		return err
	}
	p.X.Tick.Format = myAxisTickFormat
	p.Y.Tick.Format = myAxisTickFormat
...

func myAxisTickFormat(v float64, prec int) string {
	return strconv.FormatFloat(floats.Round(v, prec), 'f', 0, 64)
}

axis.go Outdated
const SuggestedTicks = 3
if max < min {
panic("illegal range")
}
if format == nil {
Copy link
Member

Choose a reason for hiding this comment

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

instead of sprinkling DefaultTicks and LogTicks with these if format == nil checks, perhaps we could instead put this default inside makeAxis ?
Like we do for Axis.Tick.Marker ?

and expose formatFloatTick as DefaultTickFormatter ?

Copy link
Author

Choose a reason for hiding this comment

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

Do you mean:

  1. Creating an interface:
type TickFormatter interface {
    Format func(v float64, prec int) string
}

And having Axis.Format be of type TickFormatter and have a DefaultTickFormat struct implementing the interface and assigning that in makeAxis?

or

  1. Simply create a DefaultTickFormat function that is exposed and gets assigned to Axis.Format (of type func(v float64, prec int) string) in makeAxis?

Copy link
Member

Choose a reason for hiding this comment

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

(apologies for not being very clear)

the latter. this makes this new field (and its handling in the code) much more inline with the others.

And set it by default on axis.makeAxis
@DavidGamba
Copy link
Author

@sbinet PTAL

sbinet
sbinet previously approved these changes Sep 10, 2017
Copy link
Member

@sbinet sbinet left a comment

Choose a reason for hiding this comment

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

LGTM.

@kortschak and/or @eaburns should definitely comment, though :)

axis.go Outdated
@@ -74,6 +76,10 @@ type Axis struct {
// returned by the Marker function that are not in
// range of the axis are not drawn.
Marker Ticker

// Format function used to format the Axis Ticks.
// When no format is provided a sane default is used.
Copy link
Member

Choose a reason for hiding this comment

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

// When format is nil DefaultTickFormat is used.

axis.go Outdated
Ticks(min, max float64) []Tick
// Ticks returns Ticks in a specified range and formatted according to the
// given format function.
// When no format is provided (nil) a sane default is used.
Copy link
Member

Choose a reason for hiding this comment

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

// When format is nil DefaultTickFormat is used.

Sanity is subjective. We can claim this in talks, blogs and wiki articles, but I don't think it's appropriate in documentation.

"testing"
)

func TestAxisSmallTick(t *testing.T) {
d := DefaultTicks{}
for _, test := range []struct {
min, max float64
format func(v float64, prec int) string
Copy link
Member

Choose a reason for hiding this comment

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

There should be a test where format is nil.

Copy link
Author

Choose a reason for hiding this comment

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

This is where my lack of knowledge of the codebase comes in. Initially I added this check:

       if format == nil {
               format = formatFloatTick
       }

In DefaultTicks.Ticks and LogTicks.Ticks. However, @sbinet told me that the check wasn't required if I applied the DefaultTickFormat in makeAxis ( #362 (comment) ).
Right now, if you call DefaultTicks.Ticks directly without a format you get a panic in the test. I am assuming this will never happen but I don't have enough insight into it.

@sbinet and @kortschak please let me know what course to take on this one.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants