备注:
显示的数据,代码中是从后端获取的,参考者可以自行造数据。
HTML
<div>
<div>
<label style="margin-left: 80px" nz-checkbox [(ngModel)]="isHideOffset" (ngModelChange)="isHideChecked($event)">隐藏偏移量</label>
<canvas #headCanvas width="800px;" height="50px"></canvas>
</div>
<div>
<div >
<canvas #LATCanvas id="LATCanvasId" width="2000px" height="130px" ></canvas>
</div>
<div>
<canvas #LNTCanvas width="2000px" height="130px" id="LNTCanvasId"></canvas>
</div>
<div>
<canvas #VRTCanvas width="2000px" height="130px" id="VRTCanvasId"></canvas>
</div>
<canvas #buttomCanvas width="2000px" height="130px"></canvas>
</div>
</div>
TS
import { Component, OnInit, ElementRef, ViewChild, Input, OnChanges } from '@angular/core';
import { PV2BeamViewComponent } from '@app/uView/view-component/pv2-beam-view/pv2-beam-view.component';
import { THIS_EXPR } from '@angular/compiler/src/output/output_ast';
import * as moment from 'moment';
import { ImageBeamInfoServiceProxy, SetupOffsetRecordDto, ImageBeamTypes } from '@shared/service-proxies/service-proxies';
@Component({
selector: 'offset-display',
templateUrl: './offset-display.component.html',
styleUrls: ['./offset-display.component.less']
})
export class OffsetDisplayComponent implements OnInit, OnChanges {
ngOnChanges(changes: import("@angular/core").SimpleChanges): void {
console.log("OffsetDisplayComponent:" + this.beamGroupId);
this.constInterval = 25;
this.displayModelList = [];
this.displayModelList.push("Show All");
this.displayModelList.push("Show LAT");
this.displayModelList.push("Show LNG");
this.displayModelList.push("Show VRT");
this.selectedMode = "Show All";
this.showAll = true;
this.showLAT = true;
this.showLNG = true;
this.showVRT = true;
this.isHideOffset = false;
this.getOffsetSessions();
this.initialHead();
}
@Input() beamGroupId: number;
@ViewChild('LATCanvas') LATCanvas: ElementRef;
@ViewChild('headCanvas') headCanvas: ElementRef;
@ViewChild('LNTCanvas') LNTCanvas: ElementRef;
@ViewChild('VRTCanvas') VRTCanvas: ElementRef;
@ViewChild('buttomCanvas') buttomCanvas: ElementRef;
public latcontext: any;
public headContext: any;
public lntContext: any;
public vrtContext: any;
public buttomContext: any;
public constInterval: number;
public offsetSessions: Array<OffsetPosition>;
public displayDates: Array<string>;
public dateMapX: Dictionary;
public isHideOffset: boolean;
public displayModelList: string[] = [];
public selectedMode: string;
public showAll: boolean;
public showLAT: boolean = true;
public showLNG: boolean;
public showVRT: boolean;
public fractionMapDate: Dictionary;
public totalFx: string;
public setupOffsetRecords: Array<SetupOffsetRecordDto>;
public dates:Array<string>;
ngOnInit(): void {
}
constructor(private _imageBeamService: ImageBeamInfoServiceProxy) {
}
displayModeChanged() {
switch (this.selectedMode) {
case "Show All":
this.showAll = true;
this.showLAT = true;
this.showLNG = true;
this.showVRT = true;
break;
case "Show LAT":
this.showAll = false;
this.showLAT = true;
this.showLNG = false;
this.showVRT = false;
break;
case "Show LNG":
this.showAll = false;
this.showLAT = false;
this.showLNG = true;
this.showVRT = false;
break;
case "Show VRT":
this.showAll = false;
this.showLAT = false;
this.showLNG = false;
this.showVRT = true;
break;
default:
break;
}
this.initialOffsetDisplay();
}
private drawLine(context: any, type: TableType) {
for (let index = 0; index <= this.dates.length; index++) {
context.beginPath();
context.strokeStyle = "white";
context.setLineDash([]);
context.moveTo(200 + index * 40, this.constInterval);
context.lineTo(200 + index * 40, this.constInterval * 5)
context.lineWidth = 0.3;
context.stroke();
context.save();
}
context.beginPath();
context.strokeStyle = "white";
context.lineWidth = 0.5;
context.moveTo(200, this.constInterval);
context.lineTo(200 + this.dates.length * 40, this.constInterval);
context.moveTo(200, this.constInterval * 5);
context.lineTo(200 + this.dates.length * 40, this.constInterval * 5);
context.stroke();
context.save();
//画0刻度线
context.beginPath();
context.moveTo(200, this.constInterval * 3);
context.lineTo(200 + this.dates.length * 40, this.constInterval * 3);
context.strokeStyle = "white";
context.lineWidth = 3;
context.stroke();
context.font = "10px 黑体";
context.fillStyle = "white";
context.textAlign = "right";
context.textBaseline = "middle";
context.fillText("0", 190, this.constInterval * 3);
context.fillText("1", 190, this.constInterval * 2);
context.fillText("-1", 190, this.constInterval * 4);
context.font = "14px bold 黑体";
context.textBaseline = "middle";
switch (type) {
case TableType.LAT:
context.fillText("治疗床 LAT", 150, this.constInterval * 3);
break;
case TableType.LNG:
context.fillText("治疗床 LNG", 150, this.constInterval * 3);
break;
case TableType.VRT:
context.fillText("治疗床 VRT", 150, this.constInterval * 3);
break;
default:
break;
}
context.save();
//画虚线
context.beginPath();
context.setLineDash([5]);
context.lineWidth = 0.5;
context.moveTo(200, this.constInterval * 2);
context.lineTo(200 + this.dates.length * 40, this.constInterval * 2);
context.stroke();
context.moveTo(200, this.constInterval * 4);
context.lineTo(200 + this.dates.length * 40, this.constInterval * 4);
context.stroke();
context.save();
}
private setIfShow(element: any, isShow: boolean) {
if (element != null) {
if (isShow == false) {
element.style.display = "none";
}
else {
element.style.display = "block"
}
}
}
private initialOffsetDisplay() {
this.latcontext = (<HTMLCanvasElement>this.LATCanvas.nativeElement).getContext('2d');
this.latcontext.clearRect(0, 0, 2000, 200);
this.lntContext = (<HTMLCanvasElement>this.LNTCanvas.nativeElement).getContext('2d');
this.lntContext.clearRect(0, 0, 2000, 200);
this.vrtContext = (<HTMLCanvasElement>this.VRTCanvas.nativeElement).getContext('2d');
this.vrtContext.clearRect(0, 0, 2000, 200);
let latelement = document.getElementById("LATCanvasId");
this.setIfShow(latelement, this.showLAT);
let lntelement = document.getElementById("LNTCanvasId");
this.setIfShow(lntelement, this.showLNG);
let vrtelement = document.getElementById("VRTCanvasId");
this.setIfShow(vrtelement, this.showVRT);
this.buttomContext = (<HTMLCanvasElement>this.buttomCanvas.nativeElement).getContext('2d');
this.buttomContext.clearRect(0, 0, 2000, 200);
this.initDates();
for (let index = 0; index <= this.dates.length; index++) {
this.buttomContext.beginPath();
this.buttomContext.lineWidth = 0.3;
this.buttomContext.strokeStyle = "white";
this.buttomContext.setLineDash([]);
this.buttomContext.moveTo(200 + index * 40, this.constInterval * 1);
this.buttomContext.lineTo(200 + index * 40, this.constInterval * 2)
this.buttomContext.stroke();
this.buttomContext.save();
}
this.drawLine(this.latcontext, TableType.LAT);
this.drawLine(this.lntContext, TableType.LNG);
this.drawLine(this.vrtContext, TableType.VRT);
//IGRT Date
this.buttomContext.beginPath();
this.buttomContext.strokeStyle = "white";
this.buttomContext.setLineDash([]);
this.buttomContext.lineWidth = 0.5;
this.buttomContext.moveTo(200, this.constInterval );
this.buttomContext.lineTo(200 + this.dates.length * 40, this.constInterval );
this.buttomContext.moveTo(200, this.constInterval * 2);
this.buttomContext.lineTo(200 + this.dates.length * 40, this.constInterval * 2);
this.buttomContext.stroke();
this.buttomContext.font = "14px 黑体";
this.buttomContext.textBaseline = "middle";
this.buttomContext.fillStyle = "white";
this.buttomContext.textAlign = "right";
this.buttomContext.fillText("IGRT 成像日期(MM-dd)", 150, this.constInterval * 1.5);
this.buttomContext.save();
//画坐标点
this.initOffsetPosition();
}
private initDates() {
this.displayDates = new Array<string>();
this.dateMapX = new Dictionary();
this.fractionMapDate = new Dictionary();
var maxFxIndex = this.offsetSessions[0].FractionNumber;
this.dates = new Array<string>();
this.offsetSessions.forEach(element => {
var day = moment(element.PlanDate).format('MM-DD');
if(this.dates.indexOf(day) == -1){
this.dates.push(day);
}
});
for (let index = 0; index < this.dates.length; index++) {
this.buttomContext.beginPath();
this.buttomContext.lineWidth = 0.5;
this.buttomContext.font = "10px 黑体";
this.buttomContext.fillStyle = "white";
this.buttomContext.textBaseline = "middle";
this.buttomContext.fillText(this.dates[index], 210 + index * 40, this.constInterval * 1.5);
this.buttomContext.save();
this.displayDates.push(this.dates[index]);
this.dateMapX.set(this.dates[index], 220 + index * 40);
}
}
formatDate(time: any) {
const Dates = new Date(time);
const year: number = Dates.getFullYear();
const month: any = (Dates.getMonth() + 1) < 10 ? '0' + (Dates.getMonth() + 1) : (Dates.getMonth() + 1);
const day: any = Dates.getDate() < 10 ? '0' + Dates.getDate() : Dates.getDate();
return month + '-' + day;
}
private initialHead() {
this.headContext = (<HTMLCanvasElement>this.headCanvas.nativeElement).getContext('2d');
this.headContext.clearRect(0, 0, 800, 50);
//画圆形
this.headContext.beginPath();
this.headContext.arc(300, 30, 5, 0, Math.PI * 2, true);
this.headContext.closePath();
this.headContext.fillStyle = 'green';
this.headContext.fill();
this.headContext.save();
this.headContext.beginPath();
this.headContext.fillStyle = 'white';
this.headContext.strokeStyle = "white";
this.headContext.font = "14px bold 黑体";
this.headContext.textBaseline = "middle";
this.headContext.fillText("FBCT", 315, 30);
this.headContext.save();
//画矩形
this.headContext.beginPath();
this.headContext.fillStyle = 'green';
this.headContext.fillRect(400, 25, 10, 10);
this.headContext.fillStyle = 'green';
this.headContext.save();
this.headContext.beginPath();
this.headContext.fillStyle = 'white';
this.headContext.strokeStyle = "white";
this.headContext.font = "14px bold 黑体";
this.headContext.textBaseline = "middle";
this.headContext.fillText("PV", 415, 30);
this.headContext.save();
//画三角形
this.headContext.beginPath();
this.headContext.fillStyle = 'green';
var height = 10 * Math.sin(Math.PI / 3);//计算等边三角形的高
this.headContext.moveTo(505, 25); //从A(345,25)开始
this.headContext.lineTo(500, 25 + height);//从A(345,25)开始,画到B (340,25+height)结束
this.headContext.lineTo(510, 25 + height); //B(340,25+height)-C(430,25+height)
this.headContext.fill();
this.headContext.save();
this.headContext.beginPath();
this.headContext.fillStyle = 'white';
this.headContext.strokeStyle = "white";
this.headContext.font = "14px bold 黑体";
this.headContext.textBaseline = "middle";
this.headContext.fillText("CBCT", 515, 30);
this.headContext.save();
}
private initOffsetPosition() {
if (this.offsetSessions != null) {
this.offsetSessions.forEach(point => {
switch (point.SessionType.id) {
case SessionType.FBCT:
this.drawFbctPosition(point);
break;
case SessionType.CBCT:
this.drawCbctPosition(point);
break;
case SessionType.PV:
this.drawPvPosition(point);
break;
default:
break;
}
});
}
}
private drawFbctPosition(point: OffsetPosition) {
var x = this.dateMapX.get(moment(point.PlanDate).format('MM-DD'));
//1像素代表多少值
var per = 2 / (2 * this.constInterval);
//LAT
if(point.LAT > 1){
var xheight = (point.LAT - 1)/per;
var y = this.constInterval*2 -xheight;
this.drawPoint(SessionType.FBCT, x, y, this.latcontext);
this.drawPositionLabel(point.LAT, x + 5, y - 10, this.latcontext);//Lat label
}
else{
var xheight = point.LAT / per;
var y = this.constInterval * 3 - xheight;
this.drawPoint(SessionType.FBCT, x, y, this.latcontext);
this.drawPositionLabel(point.LAT, x + 5, y - 10, this.latcontext);//Lat label
}
//LNT
if(point.LNT > 1){
var yheight = (point.LNT - 1)/per;
var yLNT = this.constInterval*2 -yheight;
this.drawPoint(SessionType.FBCT, x, yLNT, this.lntContext);
this.drawPositionLabel(point.LNT, x + 5, yLNT - 10, this.lntContext); //LNT label
}
else{
var yheight = point.LNT / per;
var yLNT = this.constInterval * 3 - yheight;
this.drawPoint(SessionType.FBCT, x, yLNT, this.lntContext);
this.drawPositionLabel(point.LNT, x + 5, yLNT - 10, this.lntContext); //LNT label
}
//VRT
if(point.VRT > 1){
var zheight = (point.VRT-1) / per;
var zVRT = this.constInterval*2 - zheight;
this.drawPoint(SessionType.FBCT, x, zVRT, this.vrtContext);
//VRT标坐标
this.drawPositionLabel(point.VRT, x + 5, zVRT - 10, this.vrtContext);
}
else{
var zheight = point.VRT / per;
var zVRT = this.constInterval * 3 - zheight;
this.drawPoint(SessionType.FBCT, x, zVRT, this.vrtContext);
//VRT标坐标
this.drawPositionLabel(point.VRT, x + 5, zVRT - 10, this.vrtContext);
}
}
private drawCbctPosition(point: OffsetPosition) {
var x = this.dateMapX.get(moment(point.PlanDate).format('MM-DD')) - 5;
//1像素代表多少值
var per = 2 / (2 * this.constInterval);
//LAT
if(point.LAT > 1){
var xheight = (point.LAT - 1)/per;
var y = this.constInterval*2 -xheight;
this.drawPoint(SessionType.CBCT, x, y, this.latcontext);
this.drawPositionLabel(point.LAT, x + 5, y - 10, this.latcontext); //LAT标坐标
}
else{
var xheight = point.LAT / per;
var y = this.constInterval * 3 - xheight;
this.drawPoint(SessionType.CBCT, x, y, this.latcontext);
this.drawPositionLabel(point.LAT, x + 5, y - 10, this.latcontext); //LAT标坐标
}
//LNT
if(point.LNT > 1){
var yheight = (point.LNT - 1)/per;
var yLNT = this.constInterval*2 -yheight;
this.drawPoint(SessionType.CBCT, x, yLNT, this.lntContext);
this.drawPositionLabel(point.LNT, x + 5, yLNT - 10, this.lntContext); //LNT标坐标
}
else{
var yheight = point.LNT / per;
var yLNT = this.constInterval * 3 - yheight;
this.drawPoint(SessionType.CBCT, x, yLNT, this.lntContext);
this.drawPositionLabel(point.LNT, x + 5, yLNT - 10, this.lntContext); //LNT标坐标
}
//VRT
if(point.VRT > 1){
var zHeight = (point.VRT- 1 )/per;
var zVRT = this.constInterval*2 - zHeight;
this.drawPoint(SessionType.CBCT, x, zVRT, this.vrtContext);
this.drawPositionLabel(point.VRT, x + 5, zVRT - 10, this.vrtContext); //VRT标坐标
}
else{
var zHeight = point.VRT /per;
var zVRT = this.constInterval * 3 - zHeight;
this.drawPoint(SessionType.CBCT, x, zVRT, this.vrtContext);
this.drawPositionLabel(point.VRT, x + 5, zVRT - 10, this.vrtContext); //VRT标坐标
}
}
private drawPvPosition(point: OffsetPosition) {
var x = this.dateMapX.get(moment(point.PlanDate).format('MM-DD')) - 5;
//1像素代表多少值
//LAT标坐标
var per = 2 / (2 * this.constInterval);
if(point.LAT > 1){
var height = (point.LAT -1)/per;
var y = this.constInterval - height;
this.drawPoint(SessionType.PV, x - 5, y - 5, this.latcontext);
this.drawPositionLabel(point.LAT, x + 5, y - 10, this.latcontext);
}
else{
var height = point.LAT / per;
var y = this.constInterval * 3 - height;
this.drawPoint(SessionType.PV, x - 5, y - 5, this.latcontext);
this.drawPositionLabel(point.LAT, x + 5, y - 10, this.latcontext);
}
//LNT
if(point.LNT > 1){
var height = (point.LNT - 1)/per;
var yLNT = this.constInterval - height;
this.drawPoint(SessionType.PV, x, yLNT, this.lntContext);
this.drawPositionLabel(point.LNT, x + 5, yLNT - 10, this.lntContext); //LNT标坐标
}
else{
var yLNT = this.constInterval * 3 - height;
this.drawPoint(SessionType.PV, x, yLNT, this.lntContext);
this.drawPositionLabel(point.LNT, x + 5, yLNT - 10, this.lntContext); //LNT标坐标
}
//VRT
if(point.VRT > 1){
var height = (point.VRT - 1)/per;
var zVRT = this.constInterval - height;
this.drawPoint(SessionType.PV, x, zVRT, this.vrtContext);
this.drawPositionLabel(point.VRT, x + 5, zVRT - 10, this.vrtContext); //VRT标坐标
}
else{
var zVRT = this.constInterval * 3 - height;
this.drawPoint(SessionType.PV, x, zVRT, this.vrtContext);
this.drawPositionLabel(point.VRT, x + 5, zVRT - 10, this.vrtContext); //VRT标坐标
}
}
private drawPositionLabel(label: number, x: number, y: number, context: any) {
if (this.isHideOffset == false || this.isHideOffset == undefined) {
context.beginPath();
context.fillStyle = 'white';
context.strokeStyle = "white";
context.font = "10px bold 黑体";
context.textBaseline = "middle";
context.fillText(Number(label).toFixed(2), x, y);
context.save();
}
else {
}
}
private drawPoint(sessionType: SessionType, x: number, y: number, context: any) {
switch (sessionType) {
case SessionType.FBCT:
context.beginPath();
context.arc(x, y, 5, 0, Math.PI * 2, true);
context.closePath();
context.fillStyle = 'green';
context.fill();
context.save();
break;
case SessionType.CBCT:
context.beginPath();
var trangleheight = 10 * Math.sin(Math.PI / 3);//计算等边三角形的高
context.moveTo(x, y - trangleheight / 2); //从A顶点开始
context.lineTo(x - 5, y + trangleheight / 2);//从A开始,画到B 左下角结束
context.lineTo(x + 5, y + trangleheight / 2); //B-C右下角
context.fillStyle = 'green';
context.fill();
context.save();
break;
case SessionType.PV:
context.beginPath();
context.fillStyle = 'green';
context.fillRect(x, y, 10, 10);
context.save();
break;
default:
break;
}
}
private getOffsetSessions() {
if (this.beamGroupId == null || this.beamGroupId == 0) {
console.log("get offset session:beamgroup is " + this.beamGroupId);
return;
}
console.log("get setupOffsetRecord:" + this.beamGroupId);
//数据可以自己造
this._imageBeamService.getSetupOffsetRecords(this.beamGroupId).subscribe(
(result: Array<SetupOffsetRecordDto>) => {
this.setupOffsetRecords = result;
this.offsetSessions = new Array<OffsetPosition>();
if (this.setupOffsetRecords != null) {
var index = 1;
this.setupOffsetRecords.forEach(record => {
var p = new OffsetPosition();
p.LAT = record.xOffset;
p.LNT = record.yOffset;
p.VRT = record.zOffset;
p.FractionNumber = index;
p.PlanDate = record.applyDateTime;
p.SessionType = record.imageBeamType;
this.offsetSessions.push(p);
index++;
});
this.initialOffsetDisplay();
}
});
}
getRandomNumInt(min: number, max: number) {
var Range = max - min;
var Rand = Math.random();
return (min + Math.round(Rand * Range));
}
public isHideChecked(event: boolean) {
if (event == undefined) {
this.isHideOffset = false;
}
this.isHideOffset = event;
this.initialOffsetDisplay();
}
}
class OffsetPosition {
public LAT: number;
public LNT: number;
public VRT: number;
public SessionType: ImageBeamTypes;
public PlanDate: moment.Moment | undefined;;
public FractionNumber: number;
}
export class Dictionary {
items: object;
constructor() {
this.items = {};
}
has(key: any): boolean {
return this.items.hasOwnProperty(key);
}
set(key: any, val: any) {
this.items[key] = val;
}
delete(key: any): boolean {
if (this.has(key)) {
delete this.items[key];
}
return false;
}
get(key: any): any {
return this.has(key) ? this.items[key] : undefined;
}
values(): any[] {
let values: any[] = [];
for (let k in this.items) {
if (this.has(k)) {
values.push(this.items[k]);
}
}
return values;
}
}
enum SessionType {
FBCT = 2,
PV = 3,
CBCT = 1,
}
enum TableType {
LAT = 0,
LNG = 1,
VRT = 2,
}