-
Notifications
You must be signed in to change notification settings - Fork 12
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
Custom scales #55
Comments
Hey @flekschas-ozette, good to hear from you 🙂 I think exporting The |
I agree that the tick mark computation does not fit to the scale class. On the other hand, it'd be a little bit annoying having to re-implement Something like this: export type GetTicks = (tickStep: number, tickOrigin: number, axisLow: number, axisHigh: number) => number[];
const getLinearTicks: GetTicks = (tickStep, tickOrigin, axisLow, axisHigh) => {
const ticks = [];
let tickLocation =
tickOrigin +
tickStep * Math.floor((axisLow - tickOrigin) / tickStep) -
tickStep * 2;
while (tickLocation <= axisHigh + tickStep) {
const tick = tickLocation - axisLow;
ticks.push(tick);
tickLocation += tickStep;
}
return ticks;
}
const getLogTicks: GetTicks = (tickStep, tickOrigin, axisLow, axisHigh) => {
const ticks = [];
const tickPowerLow = Math.log(axisLow) / Math.log(scale.base);
const tickPowerHigh = Math.log(axisHigh) / Math.log(scale.base);
let tickPower =
tickOrigin + resolvedTickStep * Math.floor((tickPowerLow - tickOrigin) / tickStep) - tickStep;
while (tickPower <= tickPowerHigh + tickStep) {
const tickLocation = Math.pow(scale.base, tickPower);
const tick = tickLocation - axisLow;
ticks.push(tick);
tickPower += tickStep;
}
return ticks;
}
export interface OrthoAxisOptions extends AxisOptions {
/** The maximum value encompassed by this axis. */
axisHigh?: number;
/** The position on the opposing axis that this axis intercepts. */
axisIntercept?: number;
/** The minimum value encompassed by this axis. */
axisLow?: number;
/** The function to use to format the ticks. Default `(n: number) => n.toString()`. */
labelFormatter?: (n: number) => string;
/** The number of minor ticks between major ticks. None if undefined. Default undefined. */
minorTickCount?: number;
/** Used to anchor ticks to the axis. Using a value of 0.1 and a tickStep of
* 1.0 will result in ticks at `[... -1.9, -0.9, 0.1, 1.1 ... ]`. Default 0.*/
tickOrigin?: number;
/** The distance between ticks. Default 1. */
tickStep?: number;
/** The function to use to compute tick values. Default undefined. */
getTicks?: GetTicks;
}
export class OrthoAxis extends Composite {
public readonly computed: OrthoAxisComputed;
private axis: Renderable = [];
constructor(
cg: CandyGraph,
coords: CartesianCoordinateSystem,
axis: "x" | "y",
font: Font,
options: OrthoAxisOptions = {}
) {
super();
const opts = { ...DEFAULTS, ...options };
const { axisIntercept, axisLow, axisHigh, minorTickCount, tickOrigin, tickStep, labelFormatter } = opts;
if (tickStep === 0) {
throw new Error("tickStep must be non-zero.");
}
const resolvedTickStep = Math.abs(tickStep);
const isx = axis === "x";
const scale = isx ? coords.xscale : coords.yscale;
const otherScale = isx ? coords.yscale : coords.xscale;
const resolvedAxisIntercept = axisIntercept ?? otherScale.domain[0];
const resolvedAxisLow = axisLow ?? scale.domain[0];
const resolvedAxisHigh = axisHigh ?? scale.domain[1];
// Defaults to no ticks
let getTicks: GetTicks = () => [];
if (opts.getTicks) {
getTicks = opts.getTicks;
} else if (scale.kind === ScaleKind.Linear) {
getTicks = getLinearTicks;
} else if (scale.kind === ScaleKind.Log) {
getTicks = getLogTicks;
}
const ticks = getTicks(resolvedTickStep, tickOrigin, resolvedAxisLow, resolvedAxisHigh);
... This is just an early quick idea. I don't know if the function signature ( By the way, happy new year! 🎆 :) |
Yeah that seems fair to me, and your solution looks good. I think documenting that function might be the most difficult part. Happy new year! :) |
I'm wondering what the easiest way might be to implement custom scales for CandyGraph. From the top of my head, I would think to just implement a custom class that extends from
Scale
but if I'm not mistaken, theScale
class isn't exported by CandyGraph.I also wonder how to support tick marks for custom scales as it looks like
OrthoAxis
holds the implementation forticks
. Could it make sense to have the scale class implement a function calledticks()
that receives as input{ resolvedTickStep, resolvedAxisLow, resolvedAxisHigh, tickOrigin }
and returns an array of tick values? That way, the user wouldn't have to also implement a customOrthoAxis
class. (I assume a similar function would be needed forminorTicks
)Thanks for your help!
The text was updated successfully, but these errors were encountered: