@itwin/core-frontend
Version:
iTwin.js frontend components
310 lines • 16.1 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
* @module Views
*/
import { JsonUtils } from "@itwin/core-bentley";
import { Angle, AngleSweep, Arc3d, Matrix3d, Point2d, Point3d, Transform, Vector3d, YawPitchRollAngles } from "@itwin/core-geometry";
import { BisCodeSpec, Code, ColorDef, IModel, LinePixels, Npc, } from "@itwin/core-common";
import { ElementState } from "./EntityState";
import { CoordSystem } from "./CoordSystem";
import { GraphicType } from "./common/render/GraphicType";
/**
* @public
* @extensions
*/
export var ACSType;
(function (ACSType) {
ACSType[ACSType["None"] = 0] = "None";
ACSType[ACSType["Rectangular"] = 1] = "Rectangular";
ACSType[ACSType["Cylindrical"] = 2] = "Cylindrical";
ACSType[ACSType["Spherical"] = 3] = "Spherical";
})(ACSType || (ACSType = {}));
/**
* @public
* @extensions
*/
export var ACSDisplayOptions;
(function (ACSDisplayOptions) {
ACSDisplayOptions[ACSDisplayOptions["None"] = 0] = "None";
ACSDisplayOptions[ACSDisplayOptions["Active"] = 1] = "Active";
ACSDisplayOptions[ACSDisplayOptions["Deemphasized"] = 2] = "Deemphasized";
ACSDisplayOptions[ACSDisplayOptions["Hilite"] = 4] = "Hilite";
ACSDisplayOptions[ACSDisplayOptions["CheckVisible"] = 8] = "CheckVisible";
ACSDisplayOptions[ACSDisplayOptions["Dynamics"] = 16] = "Dynamics";
})(ACSDisplayOptions || (ACSDisplayOptions = {}));
/** The state of an AuxCoordSystem element in the frontend
* @public
* @extensions
*/
export class AuxCoordSystemState extends ElementState {
static get className() { return "AuxCoordSystem"; }
type;
description;
static fromProps(props, iModel) {
const name = props.classFullName.toLowerCase();
if (name.endsWith("system2d"))
return new AuxCoordSystem2dState(props, iModel);
if (name.endsWith("system3d"))
return new AuxCoordSystem3dState(props, iModel);
return new AuxCoordSystemSpatialState(props, iModel);
}
/** Create a new AuxCoordSystemState.
* @param acsName the name for the new AuxCoordSystem
* @param iModel the iModel for which the ACS applies.
* @note call this method with the appropriate subclass (e.g. AuxCoordSystemSpatialState, AuxCoordSystem2dState, etc), not on AuxCoordSystemState directly
*/
static createNew(acsName, iModel) {
const myCode = new Code({ spec: BisCodeSpec.auxCoordSystemSpatial, scope: IModel.dictionaryId.toString(), value: acsName });
return new AuxCoordSystemSpatialState({ model: IModel.dictionaryId, code: myCode, classFullName: this.classFullName }, iModel);
}
constructor(props, iModel) {
super(props, iModel);
this.type = JsonUtils.asInt(props.type, ACSType.None);
this.description = props.description;
}
toJSON() {
const val = super.toJSON();
val.type = this.type;
val.description = this.description;
return val;
}
isValidForView(view) {
if (view.isSpatialView())
return this instanceof AuxCoordSystemSpatialState;
return (view.is3d() === this.is3d);
}
get is3d() { return this instanceof AuxCoordSystem3dState; }
drawGrid(context) {
// Called for active ACS when grid orientation is GridOrientationType::ACS.
const view = context.viewport.view;
const fixedRepsAuto = Point2d.create(); // limit grid to project extents
context.drawStandardGrid(this.getOrigin(), this.getRotation(), view.getGridSpacing(), view.getGridsPerRef(), false, fixedRepsAuto);
}
/** Returns the value, clamped to the supplied range. */
static limitRange(min, max, val) { return Math.max(min, Math.min(max, val)); }
/**
* Given an origin point, returns whether the point falls within the view or not. If adjustOrigin is set to true, a point outside
* the view will be modified to fall within the appropriate range.
*/
static isOriginInView(drawOrigin, viewport, adjustOrigin) {
const testPtView = viewport.worldToView(drawOrigin);
const frustum = viewport.getFrustum(CoordSystem.View);
const screenRange = Point3d.create();
screenRange.x = frustum.points[Npc._000].distance(frustum.points[Npc._100]);
screenRange.y = frustum.points[Npc._000].distance(frustum.points[Npc._010]);
screenRange.z = frustum.points[Npc._000].distance(frustum.points[Npc._001]);
// Check if current acs origin is outside view...
const inView = (!((testPtView.x < 0 || testPtView.x > screenRange.x) || (testPtView.y < 0 || testPtView.y > screenRange.y)));
if (!adjustOrigin)
return inView;
if (!inView) {
const offset = viewport.pixelsFromInches(0.6 /* ACSDisplaySizes.TriadSizeInches */);
testPtView.x = AuxCoordSystemState.limitRange(offset, screenRange.x - offset, testPtView.x);
testPtView.y = AuxCoordSystemState.limitRange(offset, screenRange.y - offset, testPtView.y);
}
// Limit point to NPC box to prevent triad from being clipped from display...
const originPtNpc = viewport.viewToNpc(testPtView);
originPtNpc.x = AuxCoordSystemState.limitRange(0, 1, originPtNpc.x);
originPtNpc.y = AuxCoordSystemState.limitRange(0, 1, originPtNpc.y);
originPtNpc.z = AuxCoordSystemState.limitRange(0, 1, originPtNpc.z);
viewport.npcToView(originPtNpc, testPtView);
viewport.viewToWorld(testPtView, drawOrigin);
return inView;
}
getAdjustedColor(inColor, isFill, viewport, options) {
let color;
if ((options & ACSDisplayOptions.Hilite) !== ACSDisplayOptions.None) {
color = viewport.hilite.color;
}
else if ((options & ACSDisplayOptions.Active) !== ACSDisplayOptions.None) {
color = inColor.equals(ColorDef.white) ? viewport.getContrastToBackgroundColor() : inColor;
}
else {
color = ColorDef.from(150, 150, 150, 0);
}
color = color.adjustedForContrast(viewport.view.backgroundColor);
if (isFill)
color = color.withTransparency((options & (ACSDisplayOptions.Deemphasized | ACSDisplayOptions.Dynamics)) !== ACSDisplayOptions.None ? 225 : 200);
else
color = color.withTransparency((options & ACSDisplayOptions.Deemphasized) !== ACSDisplayOptions.None ? 150 : 75);
return color;
}
addAxisLabel(builder, axis, options, vp) {
const color = ColorDef.white;
const lineColor = this.getAdjustedColor(color, false, vp, options);
builder.setSymbology(lineColor, lineColor, 2);
const linePts1 = [];
if (0 === axis) {
linePts1[0] = Point3d.create(0.4 /* ACSDisplaySizes.LabelStart */, -0.15 /* ACSDisplaySizes.LabelWidth */);
linePts1[1] = Point3d.create(0.8 /* ACSDisplaySizes.LabelEnd */, 0.15 /* ACSDisplaySizes.LabelWidth */);
}
else {
linePts1[0] = Point3d.create(0.0, 0.4 /* ACSDisplaySizes.LabelStart */);
linePts1[1] = Point3d.create(0.0, (0.4 /* ACSDisplaySizes.LabelStart */ + 0.8 /* ACSDisplaySizes.LabelEnd */) * 0.5);
}
builder.addLineString(linePts1);
const linePts2 = []; // NOTE: Don't use same point array, addPointString/addLineString don't deep copy...
if (0 === axis) {
linePts2[0] = Point3d.create(0.4 /* ACSDisplaySizes.LabelStart */, 0.15 /* ACSDisplaySizes.LabelWidth */);
linePts2[1] = Point3d.create(0.8 /* ACSDisplaySizes.LabelEnd */, -0.15 /* ACSDisplaySizes.LabelWidth */);
}
else {
linePts2[0] = Point3d.create(0.15 /* ACSDisplaySizes.LabelWidth */, 0.8 /* ACSDisplaySizes.LabelEnd */);
linePts2[1] = Point3d.create(0.0, (0.4 /* ACSDisplaySizes.LabelStart */ + 0.8 /* ACSDisplaySizes.LabelEnd */) * 0.5);
linePts2[2] = Point3d.create(-0.15 /* ACSDisplaySizes.LabelWidth */, 0.8 /* ACSDisplaySizes.LabelEnd */);
}
builder.addLineString(linePts2);
}
addAxis(builder, axis, options, vp) {
const color = (0 === axis ? ColorDef.red : (1 === axis ? ColorDef.green : ColorDef.blue));
const lineColor = this.getAdjustedColor(color, false, vp, options);
const fillColor = this.getAdjustedColor(color, true, vp, options);
if (axis === 2) {
builder.setSymbology(lineColor, lineColor, 6);
builder.addPointString([Point3d.create(0.0, 0.0, 0.65 /* ACSDisplaySizes.ZAxisLength */)]); // NOTE: ACS origin point will be drawn separately as a pickable world decoration...
const linePts2 = [Point3d.create(), Point3d.create()]; // NOTE: Don't use same point array, addPointString/addLineString don't deep copy...
linePts2[1].z = 0.65 /* ACSDisplaySizes.ZAxisLength */;
builder.setSymbology(lineColor, lineColor, 1, (options & ACSDisplayOptions.Dynamics) === ACSDisplayOptions.None ? LinePixels.Solid : LinePixels.Code2);
builder.addLineString(linePts2);
const scale = 0.4 /* ACSDisplaySizes.ArrowTipWidth */ / 2;
const center = Point3d.create();
const viewRMatrix = vp.rotation;
const xVec = viewRMatrix.getRow(0);
const yVec = viewRMatrix.getRow(1);
builder.placement.matrix.multiplyTransposeVectorInPlace(xVec);
builder.placement.matrix.multiplyTransposeVectorInPlace(yVec);
xVec.normalize(xVec);
yVec.normalize(yVec);
const ellipse = Arc3d.createScaledXYColumns(center, Matrix3d.createColumns(xVec, yVec, Vector3d.create()), scale, scale, AngleSweep.createStartEnd(Angle.createRadians(0), Angle.createRadians(Math.PI * 2)));
builder.addArc(ellipse, false, false);
builder.setBlankingFill(fillColor);
builder.addArc(ellipse, true, true);
return;
}
const shapePts = [];
shapePts[0] = Point3d.create(1.25 /* ACSDisplaySizes.ArrowTipEnd */, 0.0);
shapePts[1] = Point3d.create(0.75 /* ACSDisplaySizes.ArrowTipFlange */, 0.4 /* ACSDisplaySizes.ArrowTipWidth */);
shapePts[2] = Point3d.create(0.85 /* ACSDisplaySizes.ArrowTipStart */, 0.2 /* ACSDisplaySizes.ArrowBaseWidth */);
shapePts[3] = Point3d.create(0.3 /* ACSDisplaySizes.ArrowBaseStart */, 0.2 /* ACSDisplaySizes.ArrowBaseWidth */);
shapePts[4] = Point3d.create(0.3 /* ACSDisplaySizes.ArrowBaseStart */, -0.2 /* ACSDisplaySizes.ArrowBaseWidth */);
shapePts[5] = Point3d.create(0.85 /* ACSDisplaySizes.ArrowTipStart */, -0.2 /* ACSDisplaySizes.ArrowBaseWidth */);
shapePts[6] = Point3d.create(0.75 /* ACSDisplaySizes.ArrowTipFlange */, -0.4 /* ACSDisplaySizes.ArrowTipWidth */);
shapePts[7] = shapePts[0].clone();
if (1 === axis)
shapePts.forEach((tmpPt) => tmpPt.set(tmpPt.y, tmpPt.x));
builder.setSymbology(lineColor, lineColor, 1, (options & ACSDisplayOptions.Dynamics) === ACSDisplayOptions.None ? LinePixels.Solid : LinePixels.Code2);
builder.addLineString(shapePts);
this.addAxisLabel(builder, axis, options, vp);
builder.setBlankingFill(fillColor);
builder.addShape(shapePts);
}
/** Returns a GraphicBuilder for this AuxCoordSystemState. */
createGraphicBuilder(context, options) {
const checkOutOfView = (options & ACSDisplayOptions.CheckVisible) !== ACSDisplayOptions.None;
const drawOrigin = this.getOrigin();
if (checkOutOfView && !AuxCoordSystemState.isOriginInView(drawOrigin, context.viewport, true))
options = options | ACSDisplayOptions.Deemphasized;
let pixelSize = context.viewport.pixelsFromInches(0.6 /* ACSDisplaySizes.TriadSizeInches */);
if ((options & ACSDisplayOptions.Deemphasized) !== ACSDisplayOptions.None)
pixelSize *= 0.8;
else if ((options & ACSDisplayOptions.Active) !== ACSDisplayOptions.None)
pixelSize *= 0.9;
const exaggerate = context.viewport.view.getAspectRatioSkew();
const scale = context.getPixelSizeAtPoint(drawOrigin) * pixelSize;
const rMatrix = this.getRotation();
rMatrix.transposeInPlace();
rMatrix.scaleColumns(scale, scale / exaggerate, scale, rMatrix);
const transform = Transform.createRefs(drawOrigin, rMatrix);
const builder = context.createGraphicBuilder(GraphicType.WorldOverlay, transform);
const vp = context.viewport;
this.addAxis(builder, 0, options, vp);
this.addAxis(builder, 1, options, vp);
this.addAxis(builder, 2, options, vp);
return builder;
}
display(context, options) {
const builder = this.createGraphicBuilder(context, options);
if (undefined !== builder)
context.addDecorationFromBuilder(builder);
}
}
/** The state of an AuxCoordSystem2d element in the frontend
* @public
* @extensions
*/
export class AuxCoordSystem2dState extends AuxCoordSystemState {
static get className() { return "AuxCoordSystem2d"; }
origin;
angle; // in degrees
_rMatrix;
constructor(props, iModel) {
super(props, iModel);
this.origin = Point2d.fromJSON(props.origin);
this.angle = JsonUtils.asDouble(props.angle);
this._rMatrix = Matrix3d.createRotationAroundVector(Vector3d.unitZ(), Angle.createDegrees(this.angle)) ?? Matrix3d.createIdentity();
}
toJSON() {
const val = super.toJSON();
val.origin = this.origin;
val.angle = this.angle;
return val;
}
getOrigin(result) { return Point3d.createFrom(this.origin, result); }
setOrigin(val) { this.origin.setFrom(val); }
getRotation(result) { return this._rMatrix.clone(result); }
setRotation(val) {
this._rMatrix.setFrom(val);
const angle = YawPitchRollAngles.createFromMatrix3d(val);
this.angle = (undefined !== angle ? angle.yaw.degrees : 0.0);
}
}
/** The state of an AuxCoordSystem3d element in the frontend
* @public
* @extensions
*/
export class AuxCoordSystem3dState extends AuxCoordSystemState {
static get className() { return "AuxCoordSystem3d"; }
origin;
yaw; // in degrees
pitch; // in degrees
roll; // in degrees
_rMatrix;
constructor(props, iModel) {
super(props, iModel);
this.origin = Point3d.fromJSON(props.origin);
this.yaw = JsonUtils.asDouble(props.yaw);
this.pitch = JsonUtils.asDouble(props.pitch);
this.roll = JsonUtils.asDouble(props.roll);
const angles = new YawPitchRollAngles(Angle.createDegrees(this.yaw), Angle.createDegrees(this.pitch), Angle.createDegrees(this.roll));
this._rMatrix = angles.toMatrix3d();
}
toJSON() {
const val = super.toJSON();
val.origin = this.origin;
val.yaw = this.yaw;
val.pitch = this.pitch;
val.roll = this.roll;
return val;
}
getOrigin(result) { return Point3d.createFrom(this.origin, result); }
setOrigin(val) { this.origin.setFrom(val); }
getRotation(result) { return this._rMatrix.clone(result); }
setRotation(rMatrix) {
this._rMatrix.setFrom(rMatrix);
const angles = YawPitchRollAngles.createFromMatrix3d(rMatrix);
this.yaw = (undefined !== angles ? angles.yaw.degrees : 0.0);
this.pitch = (undefined !== angles ? angles.pitch.degrees : 0.0);
this.roll = (undefined !== angles ? angles.roll.degrees : 0.0);
}
}
/** The state of an AuxCoordSystemSpatial element in the frontend
* @public
* @extensions
*/
export class AuxCoordSystemSpatialState extends AuxCoordSystem3dState {
static get className() { return "AuxCoordSystemSpatial"; }
}
//# sourceMappingURL=AuxCoordSys.js.map