diff --git a/src/core/prototypes/plot_segments/region_2d/polar.ts b/src/core/prototypes/plot_segments/region_2d/polar.ts index cf9d86de..9a6ca3f8 100644 --- a/src/core/prototypes/plot_segments/region_2d/polar.ts +++ b/src/core/prototypes/plot_segments/region_2d/polar.ts @@ -35,6 +35,7 @@ import { getSortDirection } from "../../.."; import { ChartStateManager } from "../.."; import { strings } from "../../../../strings"; import { AxisDataBinding } from "../../../specification/types"; +import { PolarPlotSegmentPlugin } from "../../../solver/plugins"; export type PolarAxisMode = "null" | "default" | "numerical" | "categorical"; @@ -61,6 +62,8 @@ export interface PolarAttributes extends Region2DAttributes { a2r1y: number; a2r2x: number; a2r2y: number; + + autoMargin: boolean; } export interface PolarState extends Specification.PlotSegmentState { @@ -73,6 +76,7 @@ export interface PolarProperties extends Region2DProperties { innerRatio: number; outerRatio: number; equalizeArea: boolean; + autoMargin: boolean; } export interface PolarObject extends Specification.PlotSegment { @@ -136,6 +140,7 @@ export class PolarPlotSegment extends PlotSegmentClass< endAngle: 360, innerRatio: 0.5, outerRatio: 0.9, + autoMarginTitle: false, }; public readonly state: PolarState; @@ -164,6 +169,7 @@ export class PolarPlotSegment extends PlotSegmentClass< "a2r1y", "a2r2x", "a2r2y", + "autoMargin", ]; public attributes: { [name: string]: AttributeDescription } = { x1: { @@ -258,6 +264,10 @@ export class PolarPlotSegment extends PlotSegmentClass< name: "a2r2y", type: Specification.AttributeType.Number, }, + autoMargin: { + name: "autoMargin", + type: Specification.AttributeType.Boolean, + }, }; public initializeState(): void { @@ -284,6 +294,7 @@ export class PolarPlotSegment extends PlotSegmentClass< attrs.a2r1y = 0; attrs.a2r2x = 0; attrs.a2r2y = 0; + attrs.autoMargin = false; } public createBuilder( @@ -397,7 +408,8 @@ export class PolarPlotSegment extends PlotSegmentClass< attrs, this.parent.object.constraints, this.object._id, - manager + manager, + this.object.properties ) ); } @@ -808,6 +820,13 @@ export class PolarPlotSegment extends PlotSegmentClass< headerLabel: strings.objects.plotSegment.equalizeArea, } ), + manager.inputBoolean( + { property: "autoMargin" }, + { + type: "checkbox", + label: strings.objects.plotSegment.autoMarginTitle, + } + ), ] ), ...builder.buildPanelWidgets(manager), diff --git a/src/core/solver/plugins/polar_plotsegment.ts b/src/core/solver/plugins/polar_plotsegment.ts index b235d9fb..6c46e107 100644 --- a/src/core/solver/plugins/polar_plotsegment.ts +++ b/src/core/solver/plugins/polar_plotsegment.ts @@ -2,7 +2,7 @@ // Licensed under the MIT license. import { ConstraintPlugin } from "../abstract"; -import { PolarAttributes } from "../../prototypes/plot_segments/region_2d/polar"; +import { PolarAttributes, PolarProperties, } from "../../prototypes/plot_segments/region_2d/polar"; import { Geometry } from "../../common"; import { Constraint } from "../../specification"; import { ChartStateManager } from "../../prototypes"; @@ -13,18 +13,174 @@ export class PolarPlotSegmentPlugin extends ConstraintPlugin { private attrs: PolarAttributes, private chartConstraints: Constraint[], private objectID: string, - private manager: ChartStateManager + private manager: ChartStateManager, + private properties: PolarProperties ) { super(); } + + // eslint-disable-next-line max-lines-per-function + public static getCenterByAngle(isAutoMargin: boolean, attrs: PolarAttributes) { + const {angle1, angle2, x1, y1, x2, y2, radial1, radial2} = attrs; + let cx; + let cy; + let radialRatio = 1; + if (isAutoMargin) { + //pos case + + const angleDelta = Math.abs(angle1 - angle2); + const startAngle = angle1 % 360; + + if (startAngle >= 0 && startAngle < 90) { + //startAngle - 1 quadrant + if (angleDelta <= 90 - startAngle) { + //endAngle - 1 quadrant => move left bottom corner + cx = x1; + cy = y1; + radialRatio = 2; + } else if (angleDelta <= 180 - startAngle) { + //endAngle - 4 quadrant => move left + cx = x1; + cy = (y2 + y1) / 2; + } else if (angleDelta + startAngle > 180) { + //endAngle - 3 and 4 quadrants + cx = (x2 + x1) / 2; + cy = (y2 + y1) / 2; + } + } else if (startAngle >= 90 && startAngle < 180) { + //startAngle - 4 quadrant + if (angleDelta <= 180 - startAngle) { + //endAngle - 4 quadrant => move left top corner + cx = x1; + cy = y2; + radialRatio = 2; + } else if (angleDelta <= 270 - startAngle) { + //endAngle - 3 quadrant => move top + cx = (x2 + x1) / 2; + cy = y2; + } else if (angleDelta + startAngle > 270) { + //endAngle - 2 and 1 quadrants + cx = (x2 + x1) / 2; + cy = (y2 + y1) / 2; + } + } else if (startAngle >= 180 && startAngle < 270) { + //startAngle - 3 quadrant + if (angleDelta <= 270 - startAngle) { + //endAngle - 3 quadrant => move right top corner + cx = x2; + cy = y2; + radialRatio = 2; + } else if (angleDelta <= 360 - startAngle) { + //endAngle - 1 quadrant => move right + cx = x2; + cy = (y2 + y1) / 2; + } else if (angleDelta + startAngle > 360) { + //endAngle - 2 and 1 quadrants + cx = (x2 + x1) / 2; + cy = (y2 + y1) / 2; + } + } else if (startAngle >= 270 && startAngle < 360) { + //startAngle - 2 quadrant + if (angleDelta <= 360 - startAngle) { + //endAngle - 2 quadrant => move right bottom corner + cx = x2; + cy = y1; + radialRatio = 2; + } else if (angleDelta <= 450 - startAngle) { + //endAngle - 1 quadrant => move bottom + cx = (x2 + x1) / 2; + cy = y1; + } else if (angleDelta + startAngle > 450) { + //endAngle - 2 and 1 quadrants + cx = (x2 + x1) / 2; + cy = (y2 + y1) / 2; + } + } + + //neg case + if (startAngle < 0 && startAngle >= -90) { + //startAngle - 3 quadrant + if (angleDelta <= 90 - (90 + startAngle)) { + //endAngle - 3 quadrant => move right bottom corner + cx = x2; + cy = y1; + radialRatio = 2; + } else if (angleDelta <= 180 - (90 + startAngle)) { + //endAngle - 1 quadrant => move bottom + cx = (x1 + x2) / 2; + cy = y1; + } else if (angleDelta - startAngle >= 180) { + //endAngle - 2 and 3 quadrants + cx = (x2 + x1) / 2; + cy = (y2 + y1) / 2; + } + } else if (startAngle <= -90 && startAngle >= -180) { + //startAngle - 3 quadrant + if (angleDelta <= 90 - (180 + startAngle)) { + //endAngle - 3 quadrant => move right + cx = x2; + cy = y2; + radialRatio = 2; + } else if (angleDelta <= 180 - (180 + startAngle)) { + //endAngle - 2 quadrant => move top + cx = x2; + cy = (y2 + y1) / 2; + } else if (angleDelta - startAngle >= -270) { + //endAngle - 1 and 4 quadrants + cx = (x2 + x1) / 2; + cy = (y2 + y1) / 2; + } + } else if (startAngle <= -180 && startAngle >= -270) { + //startAngle - 4 quadrant + if (angleDelta <= 90 - (270 + startAngle)) { + //endAngle - 4 quadrant => move left top corner + cx = x1; + cy = y2; + radialRatio = 2; + } else if (angleDelta <= 180 - (270 + startAngle)) { + //endAngle - 3 quadrant => move top + cx = (x2 + x1) / 2; + cy = y2; + } else if (angleDelta - startAngle >= -360) { + //endAngle - 2 and 1 quadrants + cx = (x2 + x1) / 2; + cy = (y2 + y1) / 2; + } + } else if (startAngle <= -270 && startAngle > -360) { + //startAngle - 1 quadrant + if (angleDelta <= 90 - (360 + startAngle)) { + //endAngle - 1 quadrant => move left bottom corner + cx = x1; + cy = y1; + radialRatio = 2; + } else if (angleDelta <= 180 - (360 + startAngle)) { + //endAngle - 4 quadrant => move right + cx = x1; + cy = (y2 + y1) / 2; + } else if (angleDelta - startAngle >= -450) { + //endAngle - 2 and 3 quadrants + cx = (x2 + x1) / 2; + cy = (y2 + y1) / 2; + } + } + } else { + cx = (x2 + x1) / 2; + cy = (y2 + y1) / 2; + } + return {cx, cy, ratio: radialRatio}; + } + public apply(): boolean { - const { attrs } = this; - const { angle1, angle2, radial1, radial2, x1, y1, x2, y2 } = attrs; + const {attrs} = this; + const {angle1, angle2, radial1, radial2} = attrs; - attrs.cx = (x2 + x1) / 2; - attrs.cy = (y2 + y1) / 2; + const isAutoMargin = this.properties.autoMargin; - const { cx, cy } = attrs; + const center = PolarPlotSegmentPlugin.getCenterByAngle(isAutoMargin, attrs); + attrs.cx = center.cx; + attrs.cy = center.cy; + + const {cx, cy} = attrs; const toPoint = (radius: number, angle: number) => { const radians = Geometry.degreesToRadians(angle); diff --git a/src/strings.ts b/src/strings.ts index fac5b289..0ade028a 100644 --- a/src/strings.ts +++ b/src/strings.ts @@ -415,6 +415,7 @@ export const strings = { polarCoordinates: "Polar Coordinates", heightToArea: "Height to Area", equalizeArea: "Equalize area", + autoMarginTitle: "Plot auto alignment", inner: "Inner:", outer: "Outer:", radius: "Radius",