123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667 |
- function H264Session() {
- let rtpTimeStamp = 0;
- let size1M = 1048576; //1024 * 1024
- let inputBuffer = new Uint8Array(size1M);
- let spsSegment = null;
- let ppsSegment = null;
- let SPSParser = null;
- let width = 0;
- let height = 0;
- let inputLength = 0;
- let initalSegmentFlag = true; //用于确定是否是initSegment
- let initalMediaFrameFlag = true;
- let frameRate = null; //根据SDP或者SPS设置
- let preSample = null; //上一个Sample
- let durationTimeCount = 0;
- let frameCount = 0;
- let inputSegBufferSub = null;
- //MSE使用的数据以及相关配置,顺序codecInfo -> initSegmentData -> mediaSample -> frameData
- //时间戳用于绘制人脸框
- let decodedData = {
- frameData: null, //视频数据
- timeStamp: null, //时间戳
- initSegmentData: null, //MP4配置,用于initsegment
- mediaSample: null, //使用duration控制每一帧的播放时间
- codecInfo: "", //MSE init时传入,用于创建mediasource
- };
- let FRAMETYPE = {
- 1: 'P',
- 5: 'I',
- 6: 'SEI',
- 7: 'I'
- };
- let frameType = '';
- let decodeMode = 'video';
- let outputSize = 0;
- let curSize = 0;
- const PREFIX = new Uint8Array(['0x00', '0x00', '0x00', '0x01']);
- let firstIframe = false;
- let SEIInfo = {
- ivs: null,
- timestamp: null,
- };
- let preWidth = null,
- preHeight = null;
- let resetTimeCount = 0;
- let lastTimeStamp = 0;
- //const RESETTIME = 162000000;
- const RESETTIME = 4320000;
- let SEIBuffer = null;
- let lastSEITime = 0;
- function constructor() {
- }
- constructor.prototype = {
- init() {
- SPSParser = new H264SPSParser();
- this.resolutionChangedCallback = () => {
- };
- },
- remuxRTPData(rtspInterleaved, rtpHeader, rtpPayload) {
- //console.log(rtspInterleaved)
- //console.log(rtpHeader)
- let PaddingSize = 0;
- let extensionHeaderLen = 0; //如果RtpHeader.X=1,则在RTP报头后跟有一个扩展报头
- let PAYLOAD = null;
- //console.log(rtpHeader)
- //console.log(rtspInterleaved, rtpHeader, rtpPayload.subarray(0,5))
- let RtpHeader = {
- V: rtpHeader[0] >>> 6,
- P: rtpHeader[0] & 0x20,
- X: rtpHeader[0] & 0x10,
- CC: rtpHeader[0] & 0x0F,
- M: (rtpHeader[1] & 0x80) >> 7,
- PT: rtpHeader[1] & 127,
- SN: (rtpHeader[2] << 8) + rtpHeader[3],
- timeStamp: (rtpHeader[4] << 24) + (rtpHeader[5] << 16) + (rtpHeader[6] << 8) + rtpHeader[7],
- SSRC: (rtpHeader[8] << 24) + (rtpHeader[9] << 16) + (rtpHeader[10] << 8) + rtpHeader[11],
- };
- if (RtpHeader.P) { //填充
- PaddingSize = rtpPayload[rtpPayload.length - 1];
- console.log("Padding - " + PaddingSize);
- }
- if (RtpHeader.X) { //扩展
- extensionHeaderLen = (((rtpPayload[2] << 8) | rtpPayload[3]) * 4) + 4;
- console.log('X: ' + rtpPayload[0])
- }
- //console.log('extensionHeaderLen: '+ extensionHeaderLen)
- PAYLOAD = rtpPayload.subarray(extensionHeaderLen, rtpPayload.length - PaddingSize);
- rtpTimeStamp = RtpHeader.timeStamp;
- //console.log(rtpTimeStamp, rtpHeader[4], rtpHeader[5], rtpHeader[6] , rtpHeader[7], PAYLOAD[0] & 0x1f)
- /* 载荷结构(https://blog.csdn.net/davebobo/article/details/52994596)
- +---------------+
- |0|1|2|3|4|5|6|7|
- +-+-+-+-+-+-+-+-+
- |F|NRI| Type |
- +---------------+
- Type = 1-23 单个NAL单元包
- Type = 24,25, 26, 27聚合包
- Type = 28,29, 分片单元
- */
- //console.log(rtspInterleaved,rtpHeader, PAYLOAD[0]);
- let nalType = (PAYLOAD[0] & 0x1f);
- //console.log(PAYLOAD[0] + ' nalType: ' + nalType);
- //console.log('rtpPayload.length: ' + rtpPayload.length)
- //console.log(nalType, PAYLOAD[0])
- //console.log('nalType: ' + nalType, RtpHeader.M)
- switch (nalType) {
- case 6: //SEI
- //console.log(PAYLOAD, String.fromCharCode.apply(null, PAYLOAD))
- if (SEIParse(PAYLOAD) === null) {
- return;
- }
- inputBuffer = setBuffer(inputBuffer, PREFIX);
- inputBuffer = setBuffer(inputBuffer, PAYLOAD);
- break;
- case 7: //SPS
- //console.log('SPS');
- SPSParser.parse(removeH264or5EmulationBytes(PAYLOAD));
- let sizeInfo = SPSParser.getSizeInfo();
- //console.log(SPSParser.getSpsMap())
- width = sizeInfo.width;
- height = sizeInfo.height;
- if (preWidth !== width || preHeight !== height) {
- console.log('resolution changed!');
- console.log('preWidth: ', preWidth, ' preHeight: ', preHeight, ' width: ', width, ' height: ', height);
- preWidth = width;
- preHeight = height;
- }
- inputBuffer = setBuffer(inputBuffer, PREFIX);
- inputBuffer = setBuffer(inputBuffer, PAYLOAD);
- spsSegment = PAYLOAD;
- //console.log('width: ',width, 'height: ', height)
- curSize = sizeInfo.decodeSize;
- firstIframe = true;
- //console.log(spsSegment)
- if (frameRate === null) {
- frameRate = SPSParser.getFPS();
- }
- break;
- case 8: //PPS
- //console.log('PPS')
- inputBuffer = setBuffer(inputBuffer, PREFIX);
- inputBuffer = setBuffer(inputBuffer, PAYLOAD);
- ppsSegment = PAYLOAD;
- //console.log(ppsSegment)
- break;
- case 28: //FU
- //console.log('FU');
- let startBit = ((PAYLOAD[1] & 0x80) === 0x80),
- endBit = ((PAYLOAD[1] & 0x40) === 0x40),
- fuType = PAYLOAD[1] & 0x1f,
- payloadStartIndex = 2;
- //console.log('startBit: ' + startBit + ' endBit: ' + endBit)
- //console.log('fuType: ' + fuType)
- if (startBit === true && endBit === false) {
- let newNalHeader = new Uint8Array(1);
- newNalHeader[0] = ((PAYLOAD[0] & 0xe0) | fuType);
- //console.log('newNalHeader: ', newNalHeader[0])
- //console.log('fuType: ' + fuType)
- //console.log((PAYLOAD[2] << 8) + PAYLOAD[3])
- //console.log(new Uint8Array(PAYLOAD.subarray(0, 100)));
- if (false) { //赛兰摄像头,SPS,PPS,I帧打在一个RTP包中
- PAYLOAD[1] = newNalHeader[0];
- SPSParser.parse(removeH264or5EmulationBytes(PAYLOAD.subarray(1, 27)));
- let sizeInfo = SPSParser.getSizeInfo();
- //console.log(sizeInfo, SPSParser.getSpsMap())
- //SPS
- width = sizeInfo.width;
- height = sizeInfo.height;
- inputBuffer = setBuffer(inputBuffer, PREFIX);
- inputBuffer = setBuffer(inputBuffer, PAYLOAD.subarray(1, 27));
- spsSegment = PAYLOAD.subarray(1, 27);
- //console.log('width: ',width, 'height: ', height)
- curSize = sizeInfo.decodeSize;
- firstIframe = true;
- //PPS
- inputBuffer = setBuffer(inputBuffer, PREFIX);
- inputBuffer = setBuffer(inputBuffer, PAYLOAD.subarray(30, 34));
- ppsSegment = PAYLOAD.subarray(30, 34);
- //I
- inputBuffer = setBuffer(inputBuffer, PREFIX);
- inputBuffer = setBuffer(inputBuffer, PAYLOAD.subarray(38, PAYLOAD.length));
- } else {
- //console.log(newNalHeader[0] & 0x1f)
- inputBuffer = setBuffer(inputBuffer, PREFIX);
- inputBuffer = setBuffer(inputBuffer, newNalHeader);
- inputBuffer = setBuffer(inputBuffer, PAYLOAD.subarray(payloadStartIndex, PAYLOAD.length));
- }
- } else {
- //console.log(startBit, endBit, 'endBit')
- inputBuffer = setBuffer(inputBuffer,
- PAYLOAD.subarray(payloadStartIndex, PAYLOAD.length));
- }
- //console.log(startBit,endBit)
- // if(endBit === true) {
- // end = true;
- // }
- break;
- case 1:
- inputBuffer = setBuffer(inputBuffer, PREFIX);
- inputBuffer = setBuffer(inputBuffer, PAYLOAD);
- break;
- default:
- //console.log('nalType: ' + nalType);
- //console.log(PAYLOAD)
- break;
- }
- //console.log('M: ' + RtpHeader.M + ' ' + (rtpHeader[1] & 0x80))
- //check marker bit
- if (RtpHeader.M) {
- if (!firstIframe) {
- inputLength = 0;
- return;
- }
- let inputBufferSub = inputBuffer.subarray(0, inputLength);
- frameType = FRAMETYPE[inputBufferSub[4] & 0x1f];
- //只根据视频帧计算resetTimeCount
- if (frameType !== 'SEI') {
- // rtp时间戳周期为RESETTIME,如果单向递增,设为0
- if (lastTimeStamp - rtpTimeStamp > (RESETTIME / 2)) { //判断lastTimeStamp远大于rtpTimeStamp,防止后一帧比前一帧先到的情况
- //console.log(lastTimeStamp - rtpTimeStamp)
- console.warn('时间戳重置', lastTimeStamp, rtpTimeStamp, frameType, new Date())
- resetTimeCount++;
- }
- rtpTimeStamp = rtpTimeStamp + RESETTIME * resetTimeCount;
- } else {
- //同一帧的SEI比视频发送慢时
- if (rtpTimeStamp - lastTimeStamp > (RESETTIME / 2)) {
- console.warn('SEI翻转', rtpTimeStamp, lastTimeStamp);
- rtpTimeStamp = rtpTimeStamp + RESETTIME * (resetTimeCount - 1);
- } else {
- rtpTimeStamp = rtpTimeStamp + RESETTIME * resetTimeCount;
- }
- //同一帧的SEI比视频发送快时
- // if(rtpTimeStamp > lastTimeStamp) {
- // rtpTimeStamp = rtpTimeStamp + RESETTIME * resetTimeCount;
- // } else {
- // rtpTimeStamp = rtpTimeStamp + RESETTIME * (resetTimeCount + 1);
- // }
- }
- //console.log('frameType: ', frameType, 'rtpTimeStamp: ', rtpTimeStamp)
- if (frameType === 'SEI') {
- //SEI被分片(nal === 28)时,分片包发送完后marker为1,不会和视频帧一起
- SEIBuffer = inputBuffer.subarray(4, inputLength);
- //console.log(SEIBuffer)
- inputBufferSub = new Uint8Array();
- }
- if (SEIBuffer) {
- let SEI = SEIParse(SEIBuffer);
- if (SEI) {
- SEIInfo.ivs = SEI;
- SEIInfo.timestamp = rtpTimeStamp;
- decodedData.SEIInfo = SEIInfo;
- if ((rtpTimeStamp - lastSEITime) !== (90000 / frameRate)) {
- //console.log('SEI 时间差:', (rtpTimeStamp - lastTime), rtpTimeStamp, lastTime)
- }
- lastSEITime = rtpTimeStamp;
- SEIInfo = {
- ivs: null,
- timestamp: 0,
- };
- }
- SEIBuffer = null;
- }
- if (!initalSegmentFlag) {
- decodedData.initSegmentData = null;
- decodedData.codecInfo = null;
- } else {
- initalSegmentFlag = false;
- const info = {
- id: 1,
- width: width,
- height: height,
- type: "video",
- profileIdc: SPSParser.getSpsValue("profile_idc"),
- profileCompatibility: 0,
- levelIdc: SPSParser.getSpsValue("level_idc"),
- sps: [spsSegment],
- pps: [ppsSegment],
- timescale: 1e3,
- fps: frameRate
- };
- decodedData.initSegmentData = info;
- decodedData.codecInfo = SPSParser.getCodecInfo();
- //console.log(info.pps)
- }
- if (frameType === 'I') {
- //console.log('ppsSegment: ', ppsSegment)
- let h264parameterLength = spsSegment.length + ppsSegment.length + 8;
- inputSegBufferSub = inputBufferSub.subarray(h264parameterLength, inputBufferSub.length);
- } else {
- inputSegBufferSub = inputBufferSub.subarray(0, inputBufferSub.length);
- }
- if (inputSegBufferSub.length) {
- let segSize = inputSegBufferSub.length - 4;
- //mp4 box头
- inputSegBufferSub[0] = (segSize & 0xFF000000) >>> 24;
- inputSegBufferSub[1] = (segSize & 0xFF0000) >>> 16;
- inputSegBufferSub[2] = (segSize & 0xFF00) >>> 8;
- inputSegBufferSub[3] = (segSize & 0xFF);
- decodedData.frameData = new Uint8Array(inputSegBufferSub);
- let sample = {
- duration: Math.round((1 / frameRate) * 1000),
- size: inputSegBufferSub.length,
- frame_time_stamp: null,
- frameDuration: null,
- };
- sample.frame_time_stamp = rtpTimeStamp; //Todo:暂时为null,通过帧率控制duration
- if (initalMediaFrameFlag) {
- sample.frameDuration = 0;
- initalMediaFrameFlag = false;
- } else {
- if (frameRate) {
- frameCount++;
- if (!(frameCount % frameRate)) { //每秒最后一帧时
- sample.frameDuration = 1000 - durationTimeCount;
- frameCount = 0;
- durationTimeCount = 0;
- } else {
- sample.frameDuration = Math.round(1000 / frameRate);
- durationTimeCount += Math.round(1000 / frameRate);
- }
- } else {
- sample.frameDuration = (sample.frame_time_stamp - preSample.frame_time_stamp) / 90; // 时钟频率90000,timescale=1000
- }
- //console.log(sample.frameDuration)
- }
- preSample = sample;
- decodedData.mediaSample = sample;
- decodedData.timeStamp = rtpTimeStamp;
- }
- this.handleDecodedData(decodedData);
- inputLength = 0;
- decodedData.SEIInfo = null;
- inputSegBufferSub = null;
- if (frameType !== 'SEI') {
- lastTimeStamp = RtpHeader.timeStamp;
- }
- frameType = '';
- }
- //console.log('xxxxxxxxxxxxxxxxxxxxxxxxx')
- },
- set rtpSessionCallback(func) {
- this.handleDecodedData = func;
- },
- setFrameRate(fps) {
- frameRate = fps;
- //console.log('frameRate: ', frameRate)
- },
- setResolutionChangedCallback(callback) {
- this.resolutionChangedCallback = callback;
- }
- }
- return new constructor();
- function setBuffer(buffer1, buffer2) {
- let bufferTemp = buffer1;
- if ((inputLength + buffer2.length) > buffer1.length) {
- bufferTemp = new Uint8Array(buffer1.length + size1M);
- }
- bufferTemp.set(buffer2, inputLength);
- inputLength += buffer2.length;
- return bufferTemp;
- }
- }
- /**
- * 去除SPS中的Emulation字节
- * @param data SPS源数据
- * @returns {Array} 去除后Emulation字节后的SPS
- */
- function removeH264or5EmulationBytes(data) {
- let toSize = 0;
- let i = 0;
- let to = [];
- let dataLength = data.length;
- while (i < dataLength) {
- if (i + 2 < dataLength && data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 3) {
- to[toSize] = to[toSize + 1] = 0;
- toSize += 2;
- i += 3;
- } else {
- to[toSize] = data[i];
- toSize += 1;
- i += 1;
- }
- }
- return to;
- }
- /**
- * 解析SEI信息
- * @param data
- * @return {Array}
- */
- function SEIParse(data) {
- //console.log(data)
- if ((data[0] & 0x1f) !== 6) {
- //非SEI
- return null;
- }
- if (data[1] !== 0x55 || data[2] !== 0x4C || data[3] !== 0x53 || data[4] !== 0x40) {
- //ULS@开头
- console.warn('unknown SEI type');
- return null;
- }
- let dataLength = data.length;
- let type = (data[5] << 8) + data[6];
- let checkSum = (data[7] << 8) + data[8];
- // if(dataLength !== (checkSum + 1 + 4 + 4)) {
- // console.log('SEI check fail!');
- // return null;
- // }
- let result;
- data = data.subarray(9);
- switch (type) {
- case 0:
- result = parseFace(data);
- break;
- case 1:
- //console.log(parseBody(data))
- result = parseBody(data);
- break;
- case 2:
- result = parseRegion(data);
- break;
- case 3:
- result = parseBodyEx(data);
- break;
- case 4:
- result = parseOverSpeed(data);
- break;
- default:
- result = null;
- break;
- }
- return result;
- }
- function parseFace(data) {
- //console.log(data)
- let dataLength = data.length;
- let contents = [];
- while (dataLength > 0) {
- //console.log('dataLength: ', dataLength)
- let x0 = ((data[4] & 0x7f) << 8) + data[5],
- y0 = ((data[6] & 0x7f) << 8) + data[7],
- width = ((data[8] & 0x7f) << 8) + data[9] - x0,
- height = ((data[10] & 0x7f) << 8) + data[11] - y0;
- let content = {
- type: 'rect',
- id: (data[2] << 8) + data[3],
- rect: [x0, y0, width, height],
- state: data[1] & 0x01,
- quality: (data[1] & 0x02) >> 1,
- };
- Array.prototype.push.apply(contents, [content]);
- data = data.subarray(12);
- dataLength = data.length;
- }
- return contents;
- }
- function parseBody(data) {
- let dataLength = data.length;
- let contents = [];
- while (dataLength > 0) {
- //console.log('dataLength: ', dataLength)
- let x0 = ((data[4] & 0x7f) << 8) + data[5],
- y0 = ((data[6] & 0x7f) << 8) + data[7],
- width = ((data[8] & 0x7f) << 8) + data[9] - x0,
- height = ((data[10] & 0x7f) << 8) + data[11] - y0,
- boxConfidence = ((data[12] & 0x7f) << 8) + data[13];
- let points = [];
- for (let i = 0; i < 17; i++) {
- let point = {
- x: ((data[16 + i * 8] & 0x7f) << 8) + data[17 + i * 8],
- y: ((data[18 + i * 8] & 0x7f) << 8) + data[19 + i * 8],
- confidence: ((data[20 + i * 8] & 0x7f) << 8) + data[21 + i * 8],
- // x: Math.random() * 8191,
- // y: Math.random() * 8191,
- // confidence: 1,
- };
- points.push(point);
- }
- let content = {
- type: 'coco-pose',
- id: (data[2] << 8) + data[3],
- handsUp: data[1] & 0x04,
- boundingBox: [x0, y0, width, height],
- boxConfidence,
- points: parseBodyToTree(points),
- state: data[1] & 0x01,
- };
- Array.prototype.push.apply(contents, [content]);
- data = data.subarray(152);
- dataLength = data.length;
- }
- return contents;
- }
- function parseRegion(data) {
- let dataLength = data.length;
- let contents = [];
- while (dataLength > 0) {
- let pointNum = (data[0] << 8) + data[1],
- state = data[3] & 0x03,
- area = [];
- for (let i = 0; i < pointNum; i++) {
- let point = {
- x: ((data[i * 4 + 4] & 0x7f) << 8) + data[i * 4 + 5],
- y: ((data[i * 4 + 6] & 0x7f) << 8) + data[i * 4 + 7],
- };
- area.push(point);
- }
- let content = {
- type: 'region-detect',
- state,
- area
- };
- Array.prototype.push.apply(contents, [content]);
- data = data.subarray(pointNum * 4 + 4);
- dataLength = data.length;
- }
- return contents;
- }
- function parseBodyEx(data) {
- let dataLength = data.length;
- let contents = [];
- while (dataLength > 0) {
- //console.log('dataLength: ', dataLength)
- let x0 = ((data[4] & 0x7f) << 8) + data[5],
- y0 = ((data[6] & 0x7f) << 8) + data[7],
- width = ((data[8] & 0x7f) << 8) + data[9] - x0,
- height = ((data[10] & 0x7f) << 8) + data[11] - y0,
- boxConfidence = ((data[12] & 0x7f) << 8) + data[13];
- let points = [];
- for (let i = 0; i < 17; i++) {
- let point = {
- x: ((data[16 + i * 8] & 0x7f) << 8) + data[17 + i * 8],
- y: ((data[18 + i * 8] & 0x7f) << 8) + data[19 + i * 8],
- confidence: ((data[20 + i * 8] & 0x7f) << 8) + data[21 + i * 8],
- };
- points.push(point);
- }
- let guides = [
- {
- x: ((data[16 + 17 * 8] & 0x7f) << 8) + data[17 + 17 * 8],
- y: ((data[18 + 17 * 8] & 0x7f) << 8) + data[19 + 17 * 8],
- },
- {
- x: ((data[20 + 17 * 8] & 0x7f) << 8) + data[21 + 17 * 8],
- y: ((data[22 + 17 * 8] & 0x7f) << 8) + data[23 + 17 * 8],
- }
- ];
- let content = {
- type: 'coco-poseex',
- id: (data[2] << 8) + data[3],
- Loiter: data[1] & 0x03,
- Standing: (data[1] & 0x04) >> 2,
- Alone: (data[1] & 0x08) >> 3,
- boundingBox: [x0, y0, width, height],
- boxConfidence,
- points: parseBodyToTree(points),
- guides,
- state: 1
- };
- Array.prototype.push.apply(contents, [content]);
- data = data.subarray(160);
- dataLength = data.length;
- }
- return contents;
- }
- function parseOverSpeed(data) {
- let dataLength = data.length;
- let contents = [];
- while (dataLength > 0) {
- //console.log('dataLength: ', dataLength)
- let speed = ((data[4] & 0x7f) << 8) + data[5],
- overSpeed = data[1] & 0x01;
- let content = {
- type: 'over-speed',
- id: (data[2] << 8) + data[3],
- speed,
- overSpeed,
- state: 1,
- };
- Array.prototype.push.apply(contents, [content]);
- data = data.subarray(8);
- dataLength = data.length;
- }
- return contents;
- }
- /**
- * 将智能帧中的人体姿态点转化为树结构(双亲表示法)
- * @param points
- */
- function parseBodyToTree(points) {
- let newPoints = [];
- newPoints[0] = {...points[0], parent: -1, pointColor: '#FF0002', lineColor: '#FF0002'};
- newPoints[1] = {...points[1], parent: 0, pointColor: '#FF0002', lineColor: '#FF0002'};
- newPoints[2] = {...points[2], parent: 0, pointColor: '#FF0002', lineColor: '#FF0002'};
- newPoints[3] = {...points[5], parent: 17, pointColor: '#D9E34F', lineColor: '#FF0002'};
- newPoints[4] = {...points[6], parent: 17, pointColor: '#D9E34F', lineColor: '#FF0002'};
- newPoints[5] = {...points[3], parent: 1, pointColor: '#00FF00', lineColor: '#FF0002'};
- newPoints[6] = {...points[4], parent: 2, pointColor: '#FFAC00', lineColor: '#FF0002'};
- newPoints[7] = {...points[7], parent: 3, pointColor: '#00FF45', lineColor: '#'};
- newPoints[8] = {...points[11], parent: 17, pointColor: '#EEC446', lineColor: '#'};
- newPoints[9] = {...points[8], parent: 4, pointColor: '#43D3AF', lineColor: '#'};
- newPoints[10] = {...points[12], parent: 17, pointColor: '#7A93E8', lineColor: '#'};
- newPoints[11] = {...points[9], parent: 7, pointColor: '#EFB842', lineColor: '#'};
- newPoints[12] = {...points[13], parent: 8, pointColor: '#E56C00', lineColor: '#'};
- newPoints[13] = {...points[10], parent: 9, pointColor: '#47CD43', lineColor: '#0096FF'};
- newPoints[14] = {...points[14], parent: 10, pointColor: '#3868D2', lineColor: '#00FF51'};
- newPoints[15] = {...points[15], parent: 12, pointColor: '#DF4D01', lineColor: '#5100FF'};
- newPoints[16] = {...points[16], parent: 14, pointColor: '#1E48D4', lineColor: '#00FFA0'};
- newPoints[17] = {
- x: (points[5].x + points[6].x) / 2,
- y: (points[5].y + points[6].y) / 2,
- parent: -1,
- pointColor: '#D9E34F',
- lineColor: '#00FFA0'
- };
- return newPoints;
- }
|