-
Notifications
You must be signed in to change notification settings - Fork 1k
Updating circle, ellipse, line-arc, and sector to use smarter approximations #2910
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
base: master
Are you sure you want to change the base?
Changes from 4 commits
554fadd
5a81a92
6ca84c9
60c4ab5
664f3dd
3a5da78
3673822
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,11 @@ | ||
| import { GeoJsonProperties, Feature, Point, Polygon } from "geojson"; | ||
| import { destination } from "@turf/destination"; | ||
| import { polygon, Units } from "@turf/helpers"; | ||
| import { | ||
| calculateNumberOfRegularPolygonSidesToBestApproximateEqualAreaCircle, | ||
| calculatePolygonCircumRadiusToBestApproximateEqualAreaCircle, | ||
| polygon, | ||
| Units, | ||
| } from "@turf/helpers"; | ||
|
|
||
| /** | ||
| * Takes a {@link Point} and calculates the circle polygon given a radius in {@link https://turfjs.org/docs/api/types/Units Units}; and steps for precision. | ||
|
|
@@ -10,6 +15,7 @@ import { polygon, Units } from "@turf/helpers"; | |
| * @param {number} radius radius of the circle | ||
| * @param {Object} [options={}] Optional parameters | ||
| * @param {number} [options.steps=64] number of steps | ||
| * @param {number} [options.maximumRimDeviation] if provided, will ignore steps and use a number of steps such that the rim of returned approximate regular polygon is at most this far away from the true circle | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Its not clear what units the rim deviation is measured in, and having this just override the steps argument seems like a bit of a force to maintain back compatability. I'm trying to release as many fixes from the PR backlog as 7.4 in the near term, but then I'm going to move to work on v8 and merge a bunch of the breaking changes that have been sitting around. Would you make this interface differently if you could make a breaking change? I'm on the fence on whether I want to add options.method (one of "inscribed" | "circumscribed" | "equiareal"), or just yoink everyone onto the newer more accurate version. Even in the world where we just have this only this implementation, there may still be a desire to bound the number of steps so you don't wind up with a polygon with way more points than expected and something downstream becomes less performant. So There's a similar break I'm going to be making in @turf/buffer when I move us to the new clipper2 implementation in v8. It currently uses
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Regarding the Regarding the /** Precision controlled by fixed step count */
type StepsPrecision = {
/** Number of steps/sides for the generated polygon (default: 64) */
steps?: number;
maximumRimDeviation?: never;
};
/** Precision controlled by maximum deviation/tolerance threshold */
type DeviationPrecision = {
steps?: never;
/** Maximum allowable distance error between the polygon rim and the true circle */
maximumRimDeviation: number;
};
/** Optional configuration for generating circle polygons */
export type CircleOptions<P extends GeoJsonProperties = GeoJsonProperties> = {
/** Turf distance unit for radius and tolerance calculations */
units?: Units;
/** Custom GeoJSON properties to attach to the resulting Feature */
properties?: P;
} & (StepsPrecision | DeviationPrecision);Please let me know what you would like to do. |
||
| * @param {Units} [options.units='kilometers'] Supports all valid Turf {@link https://turfjs.org/docs/api/types/Units Units} | ||
| * @param {Object} [options.properties={}] properties | ||
| * @returns {Feature<Polygon>} circle polygon | ||
|
|
@@ -27,12 +33,23 @@ function circle<P extends GeoJsonProperties = GeoJsonProperties>( | |
| radius: number, | ||
| options: { | ||
| steps?: number; | ||
| maximumRimDeviation?: number; | ||
| units?: Units; | ||
| properties?: P; | ||
| } = {} | ||
| ): Feature<Polygon, P> { | ||
| // default params | ||
| const steps = options.steps || 64; | ||
| let steps = options.steps || 64; | ||
| if (options.maximumRimDeviation && options.maximumRimDeviation > 0) { | ||
| steps = | ||
| calculateNumberOfRegularPolygonSidesToBestApproximateEqualAreaCircle( | ||
| radius, | ||
| options.maximumRimDeviation | ||
| ); | ||
| } | ||
|
|
||
| const circumRadius = | ||
| calculatePolygonCircumRadiusToBestApproximateEqualAreaCircle(radius, steps); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd have to do a quick benchmark to reason about any performance changes here. Since its just in the initialization I expect this to be mostly in the same ballpark speed wise.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah I'm not suggesting optimizing now, but before merging I just want to make sure we don't introduce some large performance regression that anyone will actually notice. We transitioned union and a few other packages to polyclip-ts for correctness, but it uses BigDecimal underneath for that correctness which turned out to be very impactful performance wise. So its something I try to keep an eye on as we make changes. (I'm willing to introduce performance hits when it meaningfully impacts correctness, but I try to keep the impact to a minimum where possible)
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This makes perfect sense! I agree with that.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have optimized the |
||
| const properties: any = options.properties | ||
| ? options.properties | ||
| : !Array.isArray(center) && center.type === "Feature" && center.properties | ||
|
|
@@ -43,7 +60,7 @@ function circle<P extends GeoJsonProperties = GeoJsonProperties>( | |
| const coordinates = []; | ||
| for (let i = 0; i < steps; i++) { | ||
| coordinates.push( | ||
| destination(center, radius, (i * -360) / steps, options).geometry | ||
| destination(center, circumRadius, (i * -360) / steps, options).geometry | ||
| .coordinates | ||
| ); | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These feel too internal to be exposed through @turf/helpers, but so far we haven't built anything to share code across packages and keep it all in sync. I'm not sure if I want to try some sort of code duplication mechanism in the monorepo or start adding very small support packages for code like this so that it will still be deduplicated in bundlers.
I think my first instinct would be to make some
@turf/internal-*packages which we don't publish API docs for, but we treat as public API from a versioning standpoint. I still don't like it, but I think that's a better outcome than trying to share code and having it wind up duplicated across packages, even if the implementation itself isn't very large. I considered simlinks, but that sounds like it requires some config on Windows to make Git work, and another config in TypeScript to make that work. I don't really want to add a file-copy step to the repo initialization on a fresh checkout.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree about the visibility of these methods. I wasn't sure where else they should go with the current architecture. Whatever you decide here is good with me, just let me know how you want to proceed and where an appropriate place for this code might be.