Conversation
mauriciopoppe
left a comment
There was a problem hiding this comment.
Please change the base branch to 1.x, the reason is that this is a feature so I'd need to create the branch release-1.26.
|
|
||
| [`Check the available options in the docs`](https://mauriciopoppe.github.io/function-plot/docs/functions/default-1.html) | ||
|
|
||
| Polar functions use Cartesian coordinates by default. To display a polar grid, |
There was a problem hiding this comment.
All the examples are in site/js/site.js, please move this example alongside a comment above it, the dev server running in localhost:8080 should display it.
| throw Error('axis type ' + axis.type + ' unsupported') | ||
| })(this.options.yAxis)) | ||
|
|
||
| if (this.options.coordinateSystem === 'polar') { |
There was a problem hiding this comment.
It feels a little weird to have two locations where xDomain & yDomain are set (the block above this one and this one).
I'm thinking we could create a function that returns the xDomain and yDomain, and within that function call getPolarDomains if needed or fallback to the other cartesian domain logic.
function getDomains(
options: FunctionPlotOptions,
width: number,
height: number
): {
xDomain: number[]
yDomain: number[]
} {
const computeYDomainSize = (xDomain: number[]) => {
const xSize = xDomain[1] - xDomain[0]
return (height * xSize) / width
}
const radius =
options.coordinateSystem === 'polar'
? options.polar?.radiusDomain?.[1]
: undefined
const radiusLimit = radius === undefined ? undefined : radius * 1.2
const xDomain = (() => {
if (options.xAxis.domain) {
return options.xAxis.domain
}
if (options.xAxis.type === 'log') {
return [1, 10]
}
if (options.xAxis.type === 'linear') {
return radiusLimit === undefined
? [-6, 6]
: [-radiusLimit, radiusLimit]
}
throw new Error(`axis type ${options.xAxis.type} unsupported`)
})()
const yDomain = (() => {
if (options.yAxis.domain) {
return options.yAxis.domain
}
if (options.yAxis.type === 'log') {
return [1, 10]
}
if (options.yAxis.type === 'linear') {
if (radiusLimit !== undefined) {
return [-radiusLimit, radiusLimit]
}
const ySize = computeYDomainSize(xDomain)
return [-ySize / 2, ySize / 2]
}
throw new Error(`axis type ${options.yAxis.type} unsupported`)
})()
if (options.coordinateSystem === 'polar') {
return getPolarDomains(xDomain, yDomain, width, height)
}
return { xDomain, yDomain }
}
Caller:
const { xDomain, yDomain } = getDomains(
this.options,
this.meta.width,
this.meta.height
)
this.meta.xDomain = xDomain
this.meta.yDomain = yDomain
| .selectAll('text.x.axis-label') | ||
| .data(function (d: FunctionPlotOptions) { | ||
| return [d.xAxis.label].filter(Boolean) | ||
| return d.coordinateSystem === 'polar' ? [] : [d.xAxis.label].filter(Boolean) |
There was a problem hiding this comment.
self.isPolarCoordinateSystem() instead (where self = outer scope).
| .selectAll('text.y.axis-label') | ||
| .data(function (d: FunctionPlotOptions) { | ||
| return [d.yAxis.label].filter(Boolean) | ||
| return d.coordinateSystem === 'polar' ? [] : [d.yAxis.label].filter(Boolean) |
There was a problem hiding this comment.
self.isPolarCoordinateSystem() instead (where self = outer scope).
| .attr('stroke', 'black') | ||
| .attr('opacity', 0.2) | ||
| yOrigin.merge(yOriginEnter).attr('d', this.line) | ||
| yOrigin.exit().remove() |
| const polar = options.polar || {} | ||
| const xScale = meta.xScale | ||
| const yScale = meta.yScale | ||
| const visibleDomain = getPolarRadiusDomain(xScale.domain(), yScale.domain()) |
There was a problem hiding this comment.
Some of these variables are used within functions e.g. radius is only used by getRayGeometry, please limit the scope of a variable to the smallest scope possible, review this for all the consts you defined here.
| const TWO_PI = 2 * Math.PI | ||
| const DEFAULT_POLAR_ANGLE_UNIT = 'radians' | ||
|
|
||
| export function validatePolarOptions(options: FunctionPlotOptions) { |
There was a problem hiding this comment.
Please add a docstring in all the functions, if possible add some explanations with ascii graphs about the math.
| position?: 'sticky' | 'left' | 'bottom' | ||
| } | ||
|
|
||
| export interface PolarOptions { |
There was a problem hiding this comment.
API wise looks clean, I explored the following but I think a similar version it would be part of the v2 (main) branch instead.
interface AxisOptions {
domain?: [number, number]
label?: string
}
interface CartesianAxisOptions extends AxisOptions {
type?: 'linear' | 'log'
invert?: boolean
position?: 'sticky' | 'left' | 'bottom'
}
interface PolarRadiusAxisOptions extends AxisOptions {
ticks?: number | number[]
tickFormat?: (radius: number) => string
}
interface PolarAngleAxisOptions extends AxisOptions {
ticks?: number | number[]
unit?: 'radians' | 'degrees'
tickFormat?: (angle: number) => string
labels?: boolean
}
interface PolarOptions {
grid?: boolean
radiusAxis?: PolarRadiusAxisOptions
angleAxis?: PolarAngleAxisOptions
}
| @@ -0,0 +1,187 @@ | |||
| const puppeteer = require('puppeteer') | |||
There was a problem hiding this comment.
Please add a test to test/e2e/graphs.test.js too to see a screenshot
| await browser?.close() | ||
| }) | ||
|
|
||
| it('renders a true polar grid while preserving Cartesian defaults', async () => { |
There was a problem hiding this comment.
These are good e2e tests, in this codebase I just did screenshot tests and that was enough but checking dom properties seems like a good idea.
|
Thank you very much for the detailed review and helpful suggestions. |



What this changes
polar.angularLabels: true), using radians by default.Open design question
When angular labels are enabled, the current implementation selects a

visible radial ring independently for each angle. After panning or zooming,
labels may therefore appear on different rings.
I think we should select one shared ring, favor an outer ring with enough
visible space for labels, and hide labels that do not fit (I learn it from Desmos). I would appreciate
your thoughts on that rule.
Validation