▶ 実 行
▶ 実行
クリア
タートルグラフィクス(Promise版) - 非同期待機+スムース行動
by てぃふと@うぇいく
/** * Turtle Graphics for Web browser (nadesiko3) * plugin_turtl_promisee.js */ const turtleImage = 'https://n3s.nadesi.com/image.php?f=64.png' const elephantImage = '' const pandaImage = '' const PluginTurtlePromise = { '初期化': { type: 'func', josi: [], pure: true, fn: function (sys) { /* istanbul ignore if */ if (sys._turtlePromise) {return} sys._turtlePromise = { turtles: [], target: -1, ctx: null, canvas: null, lastStart: 0, fid: -1, canvas_r: {left: 0, top: 0, width: 640, height: 400, visible: true}, clearAll: function () { const me = this console.log('[TURTLE] clearAll') for (let i = 0; i < me.turtles.length; i++) { const tt = me.turtles[i] // 未実行の全JOBに対してresolve(1)を呼び出す。 for (let j = 0; j > tt.jobs.length; j++) { const resolve = tt.jobs[j][0][0] if (resolve != null) { resolve(1) } } tt.jobs = [] // ジョブをクリア document.body.removeChild(tt.canvas) } me.turtles = [] if (me.canvas != null) {me.ctx.clearRect(0, 0, me.canvas.width, me.canvas.height)} me.target = -1 me.flagSetTimer = false if (me.fid !== -1) { cancelAnimationFrame(me.fid) } me.lastStart = 0 me.fid = -1 }, drawTurtle: function (id) { const tt = this.turtles[id] if (!tt) {return} const cr = this.canvas_r if (!cr.visible) {return} // カメの位置を移動 tt.canvas.style.left = (cr.left + tt.x - tt.cx) + 'px' tt.canvas.style.top = (cr.top + tt.y - tt.cx) + 'px' if (!tt.f_update) {return} /* istanbul ignore if */ if (!tt.flagLoaded) {return} tt.f_update = false tt.ctx.clearRect(0, 0, tt.canvas.width, tt.canvas.height) if (!tt.f_visible) {return} if (tt.dir !== 270) { const rad = (tt.dir + 90) * 0.017453292519943295 tt.ctx.save() tt.ctx.translate(tt.cx, tt.cy) tt.ctx.rotate(rad) tt.ctx.translate(-(tt.img.width/2), -(tt.img.height/2)) tt.ctx.drawImage(tt.img, 0, 0) tt.ctx.restore() } else {tt.ctx.drawImage(tt.img, tt.cx - (tt.img.width/2), tt.cy - (tt.img.height/2))} }, getCur: function () { if (this.turtles.length === 0) {throw Error('最初に『カメ作成』命令を呼び出してください。')} return this.turtles[this.target] }, flagSetTimer: false, setTimer: function () { if (this.flagSetTimer) {return} this.flagSetTimer = true this.play(0) }, line: function (tt, x1, y1, x2, y2) { /* istanbul ignore else */ if (tt) {if (!tt.flagDown) {return}} const ctx = this.ctx if (tt.flagBegeinPath) { ctx.lineTo(x2, y2) } else { ctx.beginPath() ctx.lineWidth = tt.lineWidth ctx.strokeStyle = tt.color ctx.moveTo(x1, y1) ctx.lineTo(x2, y2) ctx.stroke() } }, clearTemporaryLine: function (tt) { }, queCommand: function (tt, command) { return new Promise(function (resolve, reject) { const cmd = [[resolve, reject], command] tt.jobs.push(cmd) }) }, doJob: function (tt, time, defaultWait, waitForTurteImage) { const me = this if (!tt.flagLoaded && waitForTurteImage) { //console.log('[TURTLE] waiting ...') return true } const immediateRun = time === 0 while ((time > 0 || immediateRun) && (tt.jobs.length > 0 || tt.jobType !== '')) { if (tt.jobType === 'substep') { if (tt.substepType === '') { if (tt.substeps.length > 0) { const sa = tt.substeps.shift() const subcmd = (sa !== undefined) ? sa[0] : '' switch (subcmd) { case 'turn': { tt.substepType = 'turn' tt.substepBase = tt.dir tt.substepModifier = 0 tt.substepTarget = sa[1] tt.substepRemain = Math.abs(sa[1]) break } case 'move': { tt.substepType = 'move' tt.substepBase = [ tt.x, tt.y ] tt.substepModifier = sa[2] tt.substepTarget = sa[1] tt.substepRemain = Math.abs(sa[1]) break } } } else { tt.jobType = 'step' tt.stepWait = 0 } } else { switch (tt.substepType) { case 'turn': { if (tt.spdRotate <= 0 || time * tt.spdRotate >= tt.substepRemain) { if (tt.spdRotate > 0) { time -= Math.floor(tt.substepRemain / tt.spdRotate) } tt.substepRemain = 0 tt.dir = (tt.substepBase + ( tt.substepTarget % 360 ) + 360) % 360 tt.substepType = '' } else { const delta = time * tt.spdRotate const direction = tt.substepTarget > 0 ? 1 : -1 tt.substepRemain -= delta tt.dir = (tt.dir + ( delta * direction % 360 ) + 360) % 360 time = 0 } tt.f_update = true break } case 'move': { if (tt.spdMove <= 0 || time * tt.spdMove >= tt.substepRemain) { if (tt.spdMove > 0) { time -= Math.floor(tt.substepRemain / tt.spdMove) } tt.substepRemain = 0 const deg = ( tt.dir + tt.substepModifier ) % 360 const rad = deg * 0.017453292519943295 const vp = tt.substepTarget const x2 = tt.substepBase[0] + Math.cos(rad) * vp const y2 = tt.substepBase[1] + Math.sin(rad) * vp me.line(tt, tt.x, tt.y, x2, y2) tt.x = x2 tt.y = y2 tt.substepType = '' } else { const delta = time * tt.spdMove const direction = tt.substepTarget > 0 ? 1 : -1 tt.substepRemain -= delta const deg = ( tt.dir + tt.substepModifier ) % 360 const rad = deg * 0.017453292519943295 const vp = delta * direction const x2 = tt.x + Math.cos(rad) * vp const y2 = tt.y + Math.sin(rad) * vp me.line(tt, tt.x, tt.y, x2, y2) tt.x = x2 tt.y = y2 time = 0 } tt.f_update = true break } } } } else if (tt.jobType === 'step') { if (time >= tt.stepWait || immediateRun) { time -= tt.stepWait tt.stepWait = 0 tt.step() tt.jobType = '' tt.step = null tt.substep = null if (tt.flagLoaded) {sys._turtlePromise.drawTurtle(tt.id)} if (tt.jobResolve != null) { tt.jobResolve(0) } } else { tt.stepWait -= time time = 0 } } else if (tt.jobType === '') { const ma = tt.jobs.shift() const resolve = ma[0][0] const reject = ma[0][1] const m = ma[1] const cmd = (m !== undefined) ? m[0] : '' switch (cmd) { case 'xy': // 起点を移動する tt.jobType = 'step' tt.stepWait = defaultWait tt.jobResolve = resolve tt.step = (function (tt, x, y) { return function () { tt.x = x tt.y = y }})(tt, m[1], m[2]) break case 'begin': // 描画を明示的に開始する tt.jobType = 'step' tt.stepWait = defaultWait tt.jobResolve = resolve tt.step = (function (tt, ctx) { return function () { ctx.beginPath() ctx.moveTo(tt.x, tt.y) tt.flagBegeinPath = true }})(tt, this.ctx) break case 'close': // パスを閉じる tt.jobType = 'step' tt.stepWait = defaultWait tt.jobResolve = resolve tt.step = (function (tt, ctx) { return function () { ctx.closePath() tt.flagBegeinPath = false }})(tt, this.ctx) break case 'fill': tt.jobType = 'step' tt.stepWait = defaultWait tt.jobResolve = resolve tt.step = (function (tt, ctx) { return function () { if (tt.flagBegeinPath) { ctx.closePath() tt.flagBegeinPath = false } ctx.fill() }})(tt, this.ctx) break case 'stroke': tt.jobType = 'step' tt.stepWait = defaultWait tt.jobResolve = resolve tt.step = (function (tt, ctx) { return function () { if (tt.flagBegeinPath) { ctx.closePath() tt.flagBegeinPath = false } ctx.stroke() }})(tt, this.ctx) break case 'text': tt.jobType = 'step' tt.stepWait = defaultWait tt.jobResolve = resolve tt.step = (function (tt, ctx, text) { return function () { ctx.fillText(text, tt.x, tt.y) }})(tt, this.ctx, m[1]) break case 'textset': tt.jobType = 'step' tt.stepWait = defaultWait tt.jobResolve = resolve tt.step = (function (ctx, font) { return function () { ctx.font = m[1] }})(this.ctx, m[1]) break case 'fillStyle': tt.jobType = 'step' tt.stepWait = defaultWait tt.jobResolve = resolve tt.step = (function (ctx, style) { return function () { ctx.fillStyle = style }})(this.ctx, m[1]) break case 'mv': { tt.jobType = 'substep' // 引数を取り出す const x2 = m[1] const y2 = m[2] // カメの角度を算出 const dx = x2 - tt.x const dy = y2 - tt.y const angleRad = Math.atan2(dy, dx) const angle = angleRad * 57.29577951308232 const targetdir = (angle + 360 ) % 360 let deg = (targetdir - tt.dir + 360 ) % 360 if (deg > 180) { deg = deg - 360 } // カメの移動距離を算出 const fdv = Math.sqrt(dx*dx+dy*dy) if (tt.spdRotate > 0 || tt.spdMove > 0) { tt.jobType = 'substep' tt.substeps.push(['turn', deg]) tt.substeps.push(['move', fdv, 0]) } else { tt.jobType = 'step' tt.stepWait = defaultWait } tt.jobResolve = resolve tt.step = ((tt, dir, x1, y1, deg, x2, y2) => { return () => { // 線を引く me.line(tt, tt.x, tt.y, x2, y2) // カメの角度を変更 tt.dir = (dir + deg + 360) % 360 // 実際に位置を移動 tt.x = x2 tt.y = y2 tt.f_update = true }})(tt, tt.dir, tt.x, tt.y, deg, x2, y2) break } case 'directmv': { tt.jobType = 'step' tt.stepWait = defaultWait tt.jobResolve = resolve tt.step = function () { // 線を引く me.line(tt, tt.x, tt.y, m[1], m[2]) // カメの角度を変更 const mvRad = Math.atan2(m[2] - tt.y, m[1] - tt.x) tt.dir = mvRad * 57.29577951308232 tt.f_update = true // 実際に位置を移動 tt.x = m[1] tt.y = m[2] } break } case 'fd': { const fdv = m[1] * m[2] const rad = tt.dir * 0.017453292519943295 const x2 = tt.x + Math.cos(rad) * fdv const y2 = tt.y + Math.sin(rad) * fdv if (tt.spdMove > 0) { tt.jobType = 'substep' tt.substeps.push(['move', fdv, 0]) } else { tt.jobType = 'step' tt.stepWait = defaultWait } tt.jobResolve = resolve tt.step = (function (tt, x1, y1, x2, y2) { return function () { me.clearTemporaryLine() me.line(tt, tt.x, tt.y, x2, y2) tt.x = x2 tt.y = y2 }})(tt, tt.x, tt.y, x2, y2) break } case 'angle': { tt.jobType = 'substep' const angle = m[1] const targetdir = (((angle - 90) % 360) + 360 ) % 360 let deg = (targetdir - tt.dir + 360 ) % 360 if (deg > 180) { deg = deg - 360 } if (tt.spdRotate > 0) { tt.jobType = 'substep' tt.substeps.push(['turn', deg]) } else { tt.jobType = 'step' tt.stepWait = defaultWait } tt.jobResolve = resolve tt.step = (function (tt, dir, deg) { return function () { tt.dir = (dir + ( deg % 360 ) + 360) % 360 tt.f_update = true }})(tt, tt.dir, deg) break } case 'directangle': { tt.jobType = 'step' tt.stepWait = defaultWait tt.jobResolve = resolve tt.step = (function (tt, angle) { return function () { tt.dir = ((angle - 90 % 360 ) + 360) % 360 tt.f_update = true }})(tt, m[1]) break } case 'rot': { let deg = m[1] * m[2] if (tt.spdRotate > 0) { tt.jobType = 'substep' tt.substeps.push(['turn', deg]) } else { tt.jobType = 'step' tt.stepWait = defaultWait } tt.jobResolve = resolve tt.step = (function (tt, dir, deg) { return function () { tt.dir = (dir + ( deg % 360 ) + 360) % 360 tt.f_update = true }})(tt, tt.dir, deg) break } case 'color': tt.jobType = 'step' tt.stepWait = defaultWait tt.jobResolve = resolve tt.step = (function (tt, ctx, c) { return function () { tt.color = c ctx.strokeStyle = tt.color }})(tt, this.ctx, m[1]) break case 'size': tt.jobType = 'step' tt.stepWait = defaultWait tt.jobResolve = resolve tt.step = (function (tt, ctx, w) { return function () { ctx.lineWidth = tt.lineWidth tt.lineWidth = w }})(tt, this.ctx, m[1]) break case 'penOn': tt.jobType = 'step' tt.stepWait = defaultWait tt.jobResolve = resolve tt.step = (function (tt, pen) { return function () { tt.flagDown = pen }})(tt, m[1]) break case 'spdR': tt.jobType = 'step' tt.stepWait = defaultWait tt.jobResolve = resolve tt.step = (function (tt, spd) { return function () { tt.spdRotate = spd }})(tt, m[1]) break case 'spdM': tt.jobType = 'step' tt.stepWait = defaultWait tt.jobResolve = resolve tt.step = (function (tt, spd) { return function () { tt.spdMove = spd }})(tt, m[1]) break case 'visible': tt.jobType = 'step' tt.stepWait = defaultWait tt.jobResolve = resolve tt.step = (function (tt, v) { return function () { tt.f_visible = v tt.f_update = true }})(tt, m[1]) break case 'changeImage': tt.flagLoaded = false tt.img.src = m[1] tt.jobType = 'step' tt.stepWait = defaultWait tt.jobResolve = resolve tt.step = function () {} break } } if (tt.flagLoaded) {sys._turtlePromise.drawTurtle(tt.id)} } return (tt.jobs.length > 0 || tt.jobType !== '') }, doJobAll: function (time, defaultWait, waitForTurteImage) { let hasNext = false for (let i = 0; i < sys._turtlePromise.turtles.length; i++) { const tt = sys._turtlePromise.turtles[i] hasNext = this.doJob(tt, time, defaultWait, waitForTurteImage) || hasNext } return hasNext }, play: function (timestamp) { const me = this if (this.lastStart == 0) { this.lastStart = timestamp this.fid = requestAnimationFrame(function (timestamp) { return me.play(timestamp) }) return } let time = timestamp - this.lastStart if (time <= 0) { time = 1 } this.lastStart = timestamp const wait = sys.__v0['カメ速度'] if (wait <= 0) { console.log('[TURTLE] run all jobs') let hasNext = true while (hasNext) { hasNext = this.doJobAll(0, wait, false) } } else { const waitForTurteImage = wait > 0 let hasNext = this.doJobAll(time, wait, waitForTurteImage) if (hasNext) { this.fid = requestAnimationFrame(function (timestamp) { return me.play(timestamp) }) return } } console.log('[TURTLE] finished.') me.flagSetTimer = false me.lastStart = 0 }, setupCanvas: function (sys) { // 描画先をセットする let canvasId = sys.__v0['カメ描画先'] if (typeof canvasId === 'string') { canvasId = document.getElementById(canvasId) || document.querySelector(canvasId) sys.__v0['カメ描画先'] = canvasId } console.log('カメ描画先=', canvasId) const cv = sys._turtlePromise.canvas = canvasId if (!cv) { console.log('[ERROR] カメ描画先が見当たりません。' + canvasId) throw Error('カメ描画先が見当たりません。') } const ctx = sys._turtlePromise.ctx = sys._turtlePromise.canvas.getContext('2d') ctx.lineWidth = 4 ctx.strokeStyle = 'black' ctx.lineCap = 'round' sys._turtlePromise.resizeCanvas(sys) }, resizeCanvas: function (sys) { console.log('[TURTLE] resize canvas') const cv = sys._turtlePromise.canvas let rect try { rect = cv.getBoundingClientRect() } catch (e) { rect = {left:0, top:0, width:0, height: 0} } const rx = rect.left + window.pageXOffset const ry = rect.top + window.pageYOffset const v = (rect.width !== 0 && rect.height !== 0) sys._turtlePromise.canvas_r = { visible: v, 'left': rx, 'top': ry, width: v ? rect.width : cv.width, height: v ? rect.height : cv.height } }, createTurtle: function (imageUrl, sys) { // キャンバス情報は毎回参照する (#734) sys._turtlePromise.setupCanvas(sys) const cv = sys._turtlePromise.canvas // カメの情報を sys._turtlePromise リストに追加 const id = sys._turtlePromise.turtles.length const tt = { id: id, img: null, canvas: null, ctx: null, dir: 270, // 上向き cx: 32, cy: 32, x: 0, y: 0, color: 'black', lineWidth: 4, spdRotate: 15/100, spdMove: 10/100, flagDown: true, flagBegeinPath: false, f_update: true, flagLoaded: false, f_visible: true, jobType: '', jobs: [], substeps: [], substepType: '' } sys._turtlePromise.turtles.push(tt) sys._turtlePromise.target = id // 画像を読み込む tt.img = document.createElement('img') tt.canvas = document.createElement('canvas') tt.ctx = tt.canvas.getContext('2d') tt.canvas.id = id tt.img.onload = function () { tt.canvas.width = tt.img.width * 1.5 tt.canvas.height = tt.img.height * 1.5 tt.cx = tt.canvas.width / 2 tt.cy = tt.canvas.height / 2 tt.flagLoaded = true tt.f_update = true sys._turtlePromise.drawTurtle(tt.id) console.log('turtle.onload') } tt.img.onerror = function () { console.log('カメの読み込みに失敗') tt.flagLoaded = true tt.f_visible = false tt.f_update = true sys._turtlePromise.drawTurtle(tt.id) } tt.img.src = imageUrl tt.canvas.style.position = 'absolute' document.body.appendChild(tt.canvas) // デフォルト位置の設定 tt.x = sys._turtlePromise.canvas_r.width / 2 tt.y = sys._turtlePromise.canvas_r.height / 2 return id } } } }, '!クリア': { type: 'func', josi: [], pure: true, fn: function (sys) { sys._turtlePromise.clearAll() } }, // @タートルグラフィックス・カメ描画 'カメ作成': { // @タートルグラフィックスを開始してカメのIDを返す // @かめさくせい type: 'func', josi: [], pure: true, fn: function (sys) { const imageUrl = sys.__v0['カメ画像URL'] return sys._turtlePromise.createTurtle(imageUrl, sys) } }, 'ゾウ作成': { // @ゾウの画像でタートルグラフィックスを開始してIDを返す // @ぞうさくせい type: 'func', josi: [], pure: true, fn: function (sys) { const imageUrl = elephantImage return sys._turtlePromise.createTurtle(imageUrl, sys) } }, 'パンダ作成': { // @パンダの画像でタートルグラフィックスを開始してIDを返す // @ぱんださくせい type: 'func', josi: [], pure: true, fn: function (sys) { const imageUrl = pandaImage return sys._turtlePromise.createTurtle(imageUrl, sys) } }, 'カメ複製': { // @タートルグラフィックスを開始してカメのIDを返す // @かめふくせい type: 'func', josi: [['の','から']], pure: true, fn: function (t, sys) { const imageUrl = sys.__v0['カメ画像URL'] let tt if (typeof sys === 'undefined') { sys = t t = null } if (t == null) { tt = sys._turtlePromise.getCur() } else { tt = sys._turtlePromise.turtles[t] } const tid = sys._turtlePromise.createTurtle(imageUrl, sys) sys._turtlePromise.turtles[tid].x = tt.x sys._turtlePromise.turtles[tid].y = tt.y sys._turtlePromise.turtles[tid].dir = tt.dir return tid } }, 'カメ操作対象設定': { // @IDを指定して操作対象となるカメを変更する // @かめそうさたいしょうせってい type: 'func', josi: [['に', 'へ', 'の']], pure: true, fn: function (id, sys) { sys._turtlePromise.target = id }, return_none: true }, 'カメ数取得': { // @カメの数を取得する // @かめすうしゅとく type: 'func', josi: [], pure: true, fn: function (sys) { return sys._turtlePromise.turtles.length } }, 'カメ描画先': {type: 'var', value: 'turtle_cv'}, // @かめびょうがさき 'カメ画像URL': {type: 'var', value: turtleImage}, // @かめがぞうURL 'カメ画像変更': { // @カメの画像をURLに変更する // @かめがぞうへんこう type: 'func', josi: [['に', 'へ']], pure: true, fn: function (url, sys) { const tt = sys._turtlePromise.getCur() const p = sys._turtlePromise.queCommand(tt, ['changeImage', url]) sys._turtlePromise.setTimer() if (sys.__genMode === '非同期モード') { sys.async = true p.finally(function () {return sys.nextAsync(sys)}) } return p } }, 'カメ速度': {type: 'const', value: 100}, // @かめそくど 'カメ速度設定': { // @カメの動作速度vに設定(大きいほど遅い) // @かめそくどせってい type: 'func', josi: [['に', 'へ']], pure: true, fn: function (v, sys) { sys.__v0['カメ速度'] = v } }, 'カメ移動': { // @カメの位置を[x,y]へ移動する // @かめいどう type: 'func', josi: [['に', 'へ']], pure: true, fn: function (xy, sys) { const tt = sys._turtlePromise.getCur() const p = sys._turtlePromise.queCommand(tt, ['mv', xy[0], xy[1]]) sys._turtlePromise.setTimer() if (sys.__genMode === '非同期モード') { sys.async = true p.finally(function () {return sys.nextAsync(sys)}) } return p } }, 'カメ直接移動': { // @カメの位置を[x,y]へ移動する // @かめちょくせついどう type: 'func', josi: [['に', 'へ']], pure: true, fn: function (xy, sys) { const tt = sys._turtlePromise.getCur() const p = sys._turtlePromise.queCommand(tt, ['directmv', xy[0], xy[1]]) sys._turtlePromise.setTimer() if (sys.__genMode === '非同期モード') { sys.async = true p.finally(function () {return sys.nextAsync(sys)}) } return p } }, 'カメ起点移動': { // @カメの描画起点位置を[x,y]へ移動する // @かめきてんいどう type: 'func', josi: [['に', 'へ']], pure: true, fn: function (xy, sys) { console.log('called') console.log(xy) console.log(sys) const tt = sys._turtlePromise.getCur() const p = sys._turtlePromise.queCommand(tt, ['xy', xy[0], xy[1]]) sys._turtlePromise.setTimer() if (sys.__genMode === '非同期モード') { sys.async = true p.finally(function () {return sys.nextAsync(sys)}) } return p } }, 'カメ進': { // @カメの位置をVだけ進める // @かめすすむ type: 'func', josi: [['だけ']], pure: true, fn: function (v, sys) { const tt = sys._turtlePromise.getCur() const p = sys._turtlePromise.queCommand(tt, ['fd', v, 1]) sys._turtlePromise.setTimer() if (sys.__genMode === '非同期モード') { sys.async = true p.finally(function () {return sys.nextAsync(sys)}) } return p } }, 'カメ戻': { // @カメの位置をVだけ戻す // @かめもどる type: 'func', josi: [['だけ']], pure: true, fn: function (v, sys) { const tt = sys._turtlePromise.getCur() const p = sys._turtlePromise.queCommand(tt, ['fd', v, -1]) sys._turtlePromise.setTimer() if (sys.__genMode === '非同期モード') { sys.async = true p.finally(function () {return sys.nextAsync(sys)}) } return p } }, 'カメ位置取得': { // @カメの位置([X,Y])を取得する // @かめいちしゅとく type: 'func', josi: [['の','から']], pure: true, fn: function (t, sys) { let tt if (typeof sys === 'undefined') { sys = t t = null } if (t == null) { tt = sys._turtlePromise.getCur() } else { tt = sys._turtlePromise.turtles[t] } return [tt.x, tt.y] } }, 'カメ角度取得': { // @カメの向き(度)を取得する // @かめかくどしゅとく type: 'func', josi: [['の','から']], pure: true, fn: function (t, sys) { let tt if (typeof sys === 'undefined') { sys = t t = null } if (t == null) { tt = sys._turtlePromise.getCur() } else { tt = sys._turtlePromise.turtles[t] } return tt.dir } }, 'カメ角度設定': { // @カメの向きをDEGに設定する // @かめかくどせってい type: 'func', josi: [['に', 'へ', 'の']], pure: true, fn: function (v, sys) { const tt = sys._turtlePromise.getCur() const p = sys._turtlePromise.queCommand(tt, ['angle', parseFloat(v)]) sys._turtlePromise.setTimer() if (sys.__genMode === '非同期モード') { sys.async = true p.finally(function () {return sys.nextAsync(sys)}) } return p } }, 'カメ直接角度設定': { // @カメの向きをDEGに設定する // @かめちょくせつかくどせってい type: 'func', josi: [['に', 'へ', 'の']], pure: true, fn: function (v, sys) { const tt = sys._turtlePromise.getCur() const p = sys._turtlePromise.queCommand(tt, ['directangle', parseFloat(v)]) sys._turtlePromise.setTimer() if (sys.__genMode === '非同期モード') { sys.async = true p.finally(function () {return sys.nextAsync(sys)}) } return p } }, 'カメ右回転': { // @カメの向きをDEGだけ右に向ける // @かめみぎかいてん type: 'func', josi: [['だけ']], pure: true, fn: function (v, sys) { const tt = sys._turtlePromise.getCur() const p = sys._turtlePromise.queCommand(tt, ['rot', v, 1]) sys._turtlePromise.setTimer() if (sys.__genMode === '非同期モード') { sys.async = true p.finally(function () {return sys.nextAsync(sys)}) } return p } }, 'カメ左回転': { // @カメの向きをDEGだけ左に向ける // @かめひだりかいてん type: 'func', josi: [['だけ']], pure: true, fn: function (v, sys) { const tt = sys._turtlePromise.getCur() const p = sys._turtlePromise.queCommand(tt, ['rot', v, -1]) sys._turtlePromise.setTimer() if (sys.__genMode === '非同期モード') { sys.async = true p.finally(function () {return sys.nextAsync(sys)}) } return p } }, 'カメペン色設定': { // @カメのペン描画色をCに設定する // @かめぺんいろせってい type: 'func', josi: [['に', 'へ']], pure: true, fn: function (c, sys) { const tt = sys._turtlePromise.getCur() const p = sys._turtlePromise.queCommand(tt, ['color', c]) sys._turtlePromise.setTimer() if (sys.__genMode === '非同期モード') { sys.async = true p.finally(function () {return sys.nextAsync(sys)}) } return p } }, 'カメペンサイズ設定': { // @カメペンのサイズをWに設定する // @かめぺんさいずせってい type: 'func', josi: [['に', 'へ']], pure: true, fn: function (w, sys) { const tt = sys._turtlePromise.getCur() const p = sys._turtlePromise.queCommand(tt, ['size', w]) sys._turtlePromise.setTimer() if (sys.__genMode === '非同期モード') { sys.async = true p.finally(function () {return sys.nextAsync(sys)}) } return p } }, 'カメペン設定': { // @カメペンを使うかどうかをV(オン/オフ)に設定する // @かめぺんせってい type: 'func', josi: [['に', 'へ']], pure: true, fn: function (w, sys) { const tt = sys._turtlePromise.getCur() const p = sys._turtlePromise.queCommand(tt, ['penOn', w]) sys._turtlePromise.setTimer() if (sys.__genMode === '非同期モード') { sys.async = true p.finally(function () {return sys.nextAsync(sys)}) } return p } }, 'カメ移動速度設定': { // @カメペンが移動する際の速さをV(px/ミリ秒)に設定する // @かめいどうそくどせってい type: 'func', josi: [['に', 'へ']], pure: true, fn: function (w, sys) { const tt = sys._turtlePromise.getCur() const p = sys._turtlePromise.queCommand(tt, ['spdM', w]) sys._turtlePromise.setTimer() if (sys.__genMode === '非同期モード') { sys.async = true p.finally(function () {return sys.nextAsync(sys)}) } return p } }, 'カメ回転速度設定': { // @カメペンが回転する際の速さをV(度/ミリ秒)に設定する // @かめかいてんそくどせってい type: 'func', josi: [['に', 'へ']], pure: true, fn: function (w, sys) { const tt = sys._turtlePromise.getCur() const p = sys._turtlePromise.queCommand(tt, ['spdR', w]) sys._turtlePromise.setTimer() if (sys.__genMode === '非同期モード') { sys.async = true p.finally(function () {return sys.nextAsync(sys)}) } return p } }, 'カメパス開始': { // @カメで明示的にパスの描画を開始する // @かめぱすかいし type: 'func', josi: [], pure: true, fn: function (sys) { const tt = sys._turtlePromise.getCur() const p = sys._turtlePromise.queCommand(tt, ['begin']) sys._turtlePromise.setTimer() if (sys.__genMode === '非同期モード') { sys.async = true p.finally(function () {return sys.nextAsync(sys)}) } return p } }, 'カメパス閉': { // @カメでパスを明示的に閉じる(省略可能) // @かめぱすとじる type: 'func', josi: [], pure: true, fn: function (sys) { const tt = sys._turtlePromise.getCur() const p = sys._turtlePromise.queCommand(tt, ['close']) sys._turtlePromise.setTimer() if (sys.__genMode === '非同期モード') { sys.async = true p.finally(function () {return sys.nextAsync(sys)}) } return p } }, 'カメパス線引': { // @カメでパスを閉じて、カメペン色設定で指定した色で枠線を引く // @かめぱすせんひく type: 'func', josi: [], pure: true, fn: function (sys) { const tt = sys._turtlePromise.getCur() const p = sys._turtlePromise.queCommand(tt, ['stroke']) sys._turtlePromise.setTimer() if (sys.__genMode === '非同期モード') { sys.async = true p.finally(function () {return sys.nextAsync(sys)}) } return p } }, 'カメパス塗': { // @カメでパスを閉じて、カメ塗り色設定で指定した色で塗りつぶす // @かめぱすぬる type: 'func', josi: [], pure: true, fn: function (sys) { const tt = sys._turtlePromise.getCur() const p = sys._turtlePromise.queCommand(tt, ['fill']) sys._turtlePromise.setTimer() if (sys.__genMode === '非同期モード') { sys.async = true p.finally(function () {return sys.nextAsync(sys)}) } return p } }, 'カメ文字描画': { // @カメの位置に文字Sを描画 // @かめもじびょうが type: 'func', josi: [['を', 'と', 'の']], pure: true, fn: function (s, sys) { const tt = sys._turtlePromise.getCur() const p = sys._turtlePromise.queCommand(tt, ['text', s]) sys._turtlePromise.setTimer() if (sys.__genMode === '非同期モード') { sys.async = true p.finally(function () {return sys.nextAsync(sys)}) } return p } }, 'カメ文字設定': { // @カメ文字描画で描画するテキストサイズやフォント(48px serif)などを設定 // @かめもじせってい type: 'func', josi: [['に', 'へ', 'で']], pure: true, fn: function (s, sys) { s = '' + s // 文字列に if (s.match(/^\d+$/)) { s = s + "px serif" } else if (s.match(/^\d+(px|em)$/)) { s = s + " serif" } const tt = sys._turtlePromise.getCur() const p = sys._turtlePromise.queCommand(tt, ['textset', s]) sys._turtlePromise.setTimer() if (sys.__genMode === '非同期モード') { sys.async = true p.finally(function () {return sys.nextAsync(sys)}) } return p } }, 'カメ塗色設定': { // @カメパスの塗り色をCに設定する // @かめぬりいろせってい type: 'func', josi: [['に', 'へ']], pure: true, fn: function (c, sys) { const tt = sys._turtlePromise.getCur() const p = sys._turtlePromise.queCommand(tt, ['fillStyle', c]) sys._turtlePromise.setTimer() if (sys.__genMode === '非同期モード') { sys.async = true p.finally(function () {return sys.nextAsync(sys)}) } return p } }, 'カメ全消去': { // @表示しているカメと描画内容を全部消去する // @かめぜんしょうきょ type: 'func', josi: [], pure: true, fn: function (sys) { sys._turtlePromise.clearAll() }, return_none: true }, 'カメコマンド実行': { // @カメにコマンドSを実行する。コマンドは改行か「;」で区切る。コマンドと引数は「=」で区切り引数はかカンマで区切る // @かめこまんどじっこう type: 'func', josi: [['の', 'を']], pure: true, fn: function (cmd, sys) { const tt = sys._turtlePromise.getCur() const a = cmd.split(/(\n|\;)/) let p for (let i = 0; i < a.length; i++) { let c = a[i] c = c.replace(/^([a-zA-Z_]+)\s*(\d+)/, '$1,$2') c = c.replace(/^([a-zA-Z_]+)\s*\=/, '$1,') ca = c.split(/\s*,\s*/) p = sys._turtlePromise.queCommand(tt, ca) } sys._turtlePromise.setTimer() if (p == null) { p = new Promise(function (r) { return r(0) }) } if (sys.__genMode === '非同期モード') { sys.async = true p.finally(function () {return sys.nextAsync(sys)}) } return p } }, 'カメ非表示': { // @カメの画像を非表示にする。描画に影響しない。 // @かめひひょうじ type: 'func', josi: [], pure: true, fn: function (sys) { const tt = sys._turtlePromise.getCur() const p = sys._turtlePromise.queCommand(tt, ['visible', false]) sys._turtlePromise.setTimer() if (sys.__genMode === '非同期モード') { sys.async = true p.finally(function () {return sys.nextAsync(sys)}) } return p } }, 'カメ表示': { // @非表示にしたカメを表示する。 // @かめひょうじ type: 'func', josi: [], pure: true, fn: function (sys) { const tt = sys._turtlePromise.getCur() const p = sys._turtlePromise.queCommand(tt, ['visible', true]) sys._turtlePromise.setTimer() if (sys.__genMode === '非同期モード') { sys.async = true p.finally(function () {return sys.nextAsync(sys)}) } return p } }, 'カメクリック時': { // @ 操作対象のカメをクリックした時のイベントを設定する // @かめくりっくしたとき type: 'func', josi: [['を']], pure: false, fn: function (func, sys) { func = sys.__findVar(func, null) // 文字列指定なら関数に変換 const tid = sys._turtlePromise.target const tt = sys._turtlePromise.turtles[tid] tt.canvas.onclick = function (e) { sys.__v0['対象'] = e.target return func(e, sys) } }, return_none: true } } if (typeof process !== 'undefined' && typeof module !== 'undefined') { module.exports = PluginTurtlePromise } // scriptタグで取り込んだ時、自動で登録する /* istanbul ignore else */ if (typeof (navigator) === 'object' && typeof (navigator.nako3)) {navigator.nako3.addPluginObject('PluginTurtlePromise', PluginTurtlePromise)}