console.log( "subSvc.js is loading.." ); /* settingType 1 単体実行 アニメーション 2 index.html内で実行 クリックで動く 名称: 呼び名 プログラム名 すべての図形 図形 shape 図形と図形を結ぶ意味を持つ図形 コネクタ connector コネクタに連なる図形 svc svc コネクタとsvc svc各要素 svcElements 親のないコネクタで始まるツリー */ var app = new App( "svcTest", document.getElementById( "svcTest" ) ); // usage: var app = App( "appID" ); //appIDをidとするcanvas要素が用意済み // usage: var app = App( "appID" ); //appIDをidとするdiv要素等が用意済み // usage: var app = App( "appID", canvasEL ); // usage: var app = App( "appID", parentEL ); if( 1 ) { //preloadの利用 //1. bodyで onload="if( typeof preload !== "undefined" ) preload(); else onloadx();" とする。 app.preload = function( event ) { console.log( "preload", this.id ); if( ! event ) { //初期設定部分 this.preload.srcs = []; //2. ここに相対アドレスで指定 for( var name in shapes ) { //check. if( name.indexOf( ">" ) > -1 ) continue; this.preload.srcs.push( base + name + ".png" ); } this.preload.srcs.push( base + "りんかく2.png" ); this.preload.srcs.push( base + "mess1.png" ); this.preload.srcs.push( base + "mess2.png" ); this.preload.srcs.push( base + "mess3.png" ); this.preload.max = this.preload.srcs.length; this.preload.cnt = 0; with( this.preload.element = document.body.appendChild( document.createElement( "div" ) ) ) { id = innerHTML = "preloading.."; style.backgroundColor = "white"; style.position = "fixed"; style.border = "solid 1px black"; style.boxShadow = "0em 0em .5em gray inset"; style.padding = "0.5em"; style.right = 4 + "px"; style.bottom = 4 + "px"; style.backgroundImage = "url(/preloading.png)"; style.backgroundRepeat = "no-repeat"; style.backgroundPosition = "right bottom"; style.backgroundSize = "100% 100%"; } this.images = new Object(); for( var i = 0; i < this.preload.srcs.length; i++ ) { var image = new Image(); image.onload = this.preload.bind( this ); image.src = this.preload.srcs[ i ]; var imageSrcSub = this.preload.srcs[ i ].replace( base, "" ); this.images[ imageSrcSub.split( "." )[ 0 ].replace( "/", "_" ) ] = image; //3. "sub/pic1.png" と指定したものは -> images.sub_pic1 でアクセスできる } } else { //onloadごとの部分 this.preload.cnt ++; var text = "Image " + this.preload.cnt + " of " + this.preload.max + " loaded."; this.preload.element.innerHTML = text; //画面表示 console.log( text ); //コンソール表示 if( this.preload.cnt == this.preload.max ) { setTimeout( ( function() { document.body.removeChild( this.preload.element ) } ).bind( this ), 5000 ); this.init(); this.ready(); } } }//preload() }//preloadの利用 app.init = function() { if( 1 ) { //canvasの設置 // this.cc = document.createElement( "canvas" ).getContext( "2d" ); // if( parentDiv = document.getElementById( "testdiv" ) ) { // parentDiv.appendChild( this.cc.canvas ); // } else { // document.body.appendChild( this.cc.canvas ); // } with( this.cc.canvas ) { width = 480; height = 480; style.border = "solid 4px pink"; style.display = "block"; style.margin = "auto"; style.borderRadius = "1em"; } this.cc.clear = function() { this.clearRect( 0, 0, this.canvas.width, this.canvas.height ); } //~circleなど this.cc.circle = function( x, y, r, fillStyle, strokeStyle ) { //check. if( ! strokeStyle && ! fillStyle ) { strokeStyle = this.strokeStyle; } this.beginPath(); this.arc( x, y, r, 0, 6.28, false ); this.closePath(); if( fillStyle ) { this.fillStyle = fillStyle; this.fill(); } if( strokeStyle ) { this.strokeStyle = strokeStyle; this.stroke(); } } this.cc.line = function( x1, y1, x2, y2, strokeStyle ) { this.beginPath(); this.moveTo( x1, y1 ); this.lineTo( x2, y2 ); this.closePath(); if( strokeStyle ) this.strokeStyle = strokeStyle; this.stroke(); } this.cc.rect = function( x, y, w, h, fillStyle, strokeStyle ) { //check. if( ! strokeStyle && ! fillStyle ) { strokeStyle = this.strokeStyle; } if( fillStyle ) { this.fillStyle = fillStyle; this.fillRect( x, y, w, h ); } if( strokeStyle ) { this.strokeStyle = strokeStyle; this.strokeRect( x, y, w, h ); } } this.cc.cross = function() { this.beginPath(); this.moveTo( 0, this.canvas.height / 2 ); this.lineTo( this.canvas.width, this.canvas.height / 2 ); this.closePath(); this.stroke(); this.beginPath(); this.moveTo( this.canvas.width / 2, 0 ); this.lineTo( this.canvas.width / 2, this.canvas.height ); this.closePath(); this.stroke(); } this.cc.centerX = function() { return this.canvas.width / 2; } this.cc.centerY = function() { return this.canvas.height / 2; } } // this.cc.globalAlpha = 0.5; //---Svc作成 svcm = new Svcm(); tops = new Array(); //Excelのshape情報からsvcを作成 //~svcを作成している場所 for( var name in shapes ) { //check. コネクタを対象とする if( name.indexOf( ">" ) > -1 ) continue; var shape = shapes[ name ]; //画像は回転0の状態。画像の左上座標を回転する。 var xlCenterX = shape.left + shape.Width / 2; //図形の中心 var xlCenterY = shape.top + shape.Height / 2; //図形の中心 var imgX = shape.left - shape.diffLeft; var imgY = shape.top - shape.diffTop; var rad = shape.Rotation * ( Math.PI / 180 ); var kaitenImg = Svc.utlKaitenC( xlCenterX, xlCenterY, imgX, imgY, rad ); //~LTAX初期値決めてる場所 var svc = new Svc( { name : name, LTAX : kaitenImg.X, //画像左上座標は回転状態を初期値とする LTAY : kaitenImg.Y, width : shape.saveWidth, height : shape.saveHeight, image : this.images[ name ], thetaA : rad, zIndex : shape.ZOrderPosition, limitTheta : 0, } ); svcm.svcs[ name ] = svc; svcm.zIndex.push( svc ); } svcm.zIndex.sort( function( a, b ) { return ( a.zIndex > b.zIndex ) - ( a.zIndex < b.zIndex ); } ); debug_useGirl = typeof svcm.svcs[ "red" ] === "undefined"; if( debug_useGirl ) { svcm.top = svcm.svcs[ "りんかく" ]; svcm.offsetObject = svcm.svcs[ "りんかく" ]; svcm.offsetXMember = "LTAX"; svcm.offsetYMember = "LTAY"; } else { svcm.top = svcm.svcs[ "red" ]; } //connector for( var name in shapes ) { //check. if( name.indexOf( ">" ) == -1 ) continue; var tokens = name.split( ">" ); var parentName = tokens[ 0 ]; var childName = tokens[ 1 ]; var parent = svcm.svcs[ parentName ]; var child = svcm.svcs[ childName ]; if( parentName == "" ) { //top svc tops.push( child ); var connector = child.addConnector( name ); child.connectorC = connector; child.isTop = true; console.log( "top svc", shapes[ name ].left, shapes[ name ].top ); } else if( childName == "" ) { //hand svc console.log( "hand svc" ); } else { //normal svc var parentC = parent.addConnector( name ); var childC = child.addConnector( name ); Svc.connect( parentC, childC ); console.log( "normal svc", name ); } }//for //~コネクタの座標を決めている場所 for( var svcName in svcm.svcs ) { var svc = svcm.svcs[ svcName ]; for( var connectorName in svc.connectors ) { var connectorShape = shapes[ connectorName ]; var connectorCenterX = connectorShape.left + connectorShape.Width / 2; var connectorCenterY = connectorShape.top + connectorShape.Height / 2; //回転している状態のrx, ry var kaitenRx = connectorCenterX - svc.LTAX; var kaitenRy = connectorCenterY - svc.LTAY; //逆回転し回転0にする var k = Svc.utlKaiten( kaitenRx, kaitenRy, -svc.thetaA ); var connector = svc.connectors[ connectorName ]; connector.rx = k.X; connector.ry = k.Y; //回転0の状態で設定 } } svcm.top.calcByA(); //~poses設定 if( debug_useGirl ) { //~limitTheta svcm.svcs[ "りんかく" ].limitTheta = 321 * ( Math.PI / 180 ); svcm.svcs[ "首" ].limitTheta = 312 * ( Math.PI / 180 ); svcm.svcs[ "右上腕" ].limitTheta = 215 * ( Math.PI / 180 ); svcm.svcs[ "左上腕" ].limitTheta = 110 * ( Math.PI / 180 ); svcm.svcs[ "右下腕" ].limitTheta = 303 * ( Math.PI / 180 ); svcm.svcs[ "左下腕" ].limitTheta = 303 * ( Math.PI / 180 ); svcm.svcs[ "右手" ].limitTheta = 175 * ( Math.PI / 180 ); svcm.svcs[ "右すね" ].limitTheta = 269 * ( Math.PI / 180 ); svcm.svcs[ "右つまさき" ].limitTheta = 232 * ( Math.PI / 180 ); svcm.svcs[ "左つまさき" ].limitTheta = 232 * ( Math.PI / 180 ); svcm.svcs[ "左かかと" ].limitTheta = 257 * ( Math.PI / 180 ); svcm.svcs[ "腹" ].limitTheta = 100 * ( Math.PI / 180 ); svcm.svcs[ "右翼上" ].limitTheta = 253 * ( Math.PI / 180 ); svcm.svcs[ "左翼上" ].limitTheta = 253 * ( Math.PI / 180 ); svcm.svcs[ "右翼下" ].limitTheta = 190 * ( Math.PI / 180 ); svcm.svcs[ "左翼下" ].limitTheta = 190 * ( Math.PI / 180 ); poses = { "直立" : { name : "直立", tm : 1000, data : [ [ svcm.svcs[ "右つまさき" ], "thetaR", 0, true ], [ svcm.svcs[ "りんかく" ], "thetaR", 6.15, true ], [ svcm.svcs[ "首" ], "thetaR", -6.4, true ], [ svcm.svcs[ "胸" ], "thetaR", 0, true ], [ svcm.svcs[ "腹" ], "thetaR", 6.4, true ], [ svcm.svcs[ "腰" ], "thetaR", .55, true ], [ svcm.svcs[ "腰2" ], "thetaR", -.5, true ], [ svcm.svcs[ "右上腕" ], "thetaR", 1.9, true ], [ svcm.svcs[ "右下腕" ], "thetaR", -1.9, true ], [ svcm.svcs[ "左上腕" ], "thetaR", 0, true ], [ svcm.svcs[ "左下腕" ], "thetaR", 0, true ], [ svcm.svcs[ "右手" ], "thetaR", -.2, true ], [ svcm.svcs[ "左手" ], "thetaR", 3.14, true ], [ svcm.svcs[ "右もも" ], "thetaR", -.6, true ], [ svcm.svcs[ "右すね" ], "thetaR", .15, true ], [ svcm.svcs[ "左もも" ], "thetaR", -.8, true ], [ svcm.svcs[ "左すね" ], "thetaR", .4, true ], [ svcm.svcs[ "右翼上" ], "thetaR", 5.5, true ], [ svcm.svcs[ "右翼下" ], "thetaR", -3, true ], [ svcm.svcs[ "右翼先" ], "thetaR", -3.5, true ], [ svcm.svcs[ "左翼上" ], "thetaR", -1, true ], [ svcm.svcs[ "左翼下" ], "thetaR", 3, true ], [ svcm.svcs[ "左翼先" ], "thetaR", -3, true ], [ svcm.svcs[ "右かかと" ], "thetaR", -0.05, true ], [ svcm.svcs[ "左かかと" ], "thetaR", -0.05, true ], [ svcm.svcs[ "左つまさき" ], "thetaR", 0, true ], ], nextFunc : ( function() { //直立終了し、おっとへ遷移 svcm.svcs[ "りんかく" ].image = this.images[ "りんかく2" ]; if( settingType == 1 ) { anms.addWithPose( poses[ "しゃがみ" ] ); } } ).bind( this ), },//poses element "おっと" : { name : "おっと", tm : 1000, data : [ [ svcm.svcs[ "りんかく" ], "thetaR", 6.1, true ], [ svcm.svcs[ "首" ], "thetaR", -6.09, true ], [ svcm.svcs[ "胸" ], "thetaR", 0.08, true ], [ svcm.svcs[ "左翼上" ], "thetaR", 0.5, true ], [ svcm.svcs[ "左翼下" ], "thetaR", 1.82, true ], [ svcm.svcs[ "左翼先" ], "thetaR", -1.83, true ], [ svcm.svcs[ "左上腕" ], "thetaR", 0.35, true ], [ svcm.svcs[ "左下腕" ], "thetaR", 0.3, true ], [ svcm.svcs[ "左手" ], "thetaR", 4.36, true ], [ svcm.svcs[ "腹" ], "thetaR", 6, true ], [ svcm.svcs[ "腰" ], "thetaR", -6.11, true ], [ svcm.svcs[ "左もも" ], "thetaR", 5.08, true ], [ svcm.svcs[ "左すね" ], "thetaR", 0.35, true ], [ svcm.svcs[ "左かかと" ], "thetaR", -0.28, true ], [ svcm.svcs[ "左つまさき" ], "thetaR", -0.11, true ], [ svcm.svcs[ "右もも" ], "thetaR", 5.3, true ], [ svcm.svcs[ "右すね" ], "thetaR", 0.07, true ], [ svcm.svcs[ "右かかと" ], "thetaR", -0.3, true ], [ svcm.svcs[ "右つまさき" ], "thetaR", -0.06, true ], [ svcm.svcs[ "腰2" ], "thetaR", 5.68, true ], [ svcm.svcs[ "右上腕" ], "thetaR", 2.19, true ], [ svcm.svcs[ "右下腕" ], "thetaR", -1.42, true ], [ svcm.svcs[ "右手" ], "thetaR", 1.38, true ], [ svcm.svcs[ "右翼上" ], "thetaR", 5.94, true ], [ svcm.svcs[ "右翼下" ], "thetaR", -4.05, true ], [ svcm.svcs[ "右翼先" ], "thetaR", -1.83, true ], [ svcm.svcs[ "帽子" ], "thetaR", -0.25, true ], ], nextFunc : ( function() { //おっと終了し、直立へ遷移 svcm.svcs[ "りんかく" ].image = this.images[ "りんかく" ]; if( settingType == 1 ) { anms.addWithPose( poses[ "直立" ] ); } else { this.drawMess(); } } ).bind( this ), },//poses element "しゃがみ" : { name : "しゃがみ", tm : 1000, data : [ [ svcm.svcs[ "右つまさき" ], "thetaR", -0.45, true ], [ svcm.svcs[ "りんかく" ], "thetaR", 0, true ], [ svcm.svcs[ "首" ], "thetaR", 0.59, true ], [ svcm.svcs[ "胸" ], "thetaR", 0.08, true ], [ svcm.svcs[ "左翼上" ], "thetaR", -0.46, true ], [ svcm.svcs[ "左翼下" ], "thetaR", 2.12, true ], [ svcm.svcs[ "左翼先" ], "thetaR", 3.4, true ], [ svcm.svcs[ "左上腕" ], "thetaR", 3.47, true ], [ svcm.svcs[ "左下腕" ], "thetaR", -0.08, true ], [ svcm.svcs[ "左手" ], "thetaR", -3.57, true ], [ svcm.svcs[ "腹" ], "thetaR", -0.41, true ], [ svcm.svcs[ "腰" ], "thetaR", 0.12, true ], [ svcm.svcs[ "左もも" ], "thetaR", 4.4, true ], [ svcm.svcs[ "左すね" ], "thetaR", -2.89, true ], [ svcm.svcs[ "左かかと" ], "thetaR", 0.05, true ], [ svcm.svcs[ "左つまさき" ], "thetaR", -1.55, true ], [ svcm.svcs[ "右もも" ], "thetaR", 3.92, true ], [ svcm.svcs[ "右すね" ], "thetaR", -3.69, true ], [ svcm.svcs[ "右かかと" ], "thetaR", -0.16, true ], [ svcm.svcs[ "腰2" ], "thetaR", 4.12, true ], [ svcm.svcs[ "右上腕" ], "thetaR", 1.05, true ], [ svcm.svcs[ "右下腕" ], "thetaR", 2.74, true ], [ svcm.svcs[ "右手" ], "thetaR", 0.37, true ], [ svcm.svcs[ "右翼上" ], "thetaR", 5.02, true ], [ svcm.svcs[ "右翼下" ], "thetaR", -3.51, true ], [ svcm.svcs[ "右翼先" ], "thetaR", 3.72, true ], [ svcm.svcs[ "帽子" ], "thetaR", 6.03, true ], ], nextFunc : ( function() { //おっと終了し、直立へ遷移 svcm.svcs[ "りんかく" ].image = this.images[ "りんかく" ]; if( settingType == 1 ) { anms.addWithPose( poses[ "直立" ] ); } else { this.drawMess(); } } ).bind( this ), },//poses element };//poses //check. console.log( "checking poses..." ); for( var name in poses ) { var pose = poses[ name ]; for( var i = 0; i< pose.data.length; i++ ) { console.log( name, i, pose.data[ i ][ 0 ] ); } } console.log( "done" ); //~frame this.setFps( 10 ); anms = new Anms( this.timerMS ); switch( settingType ) { case 1: anms.addWithPose( poses[ "しゃがみ" ] ); break; case 2: break; } if( 1 ) { svcm.offsetObject = svcm.svcs[ "右つまさき" ]; // svcm.poseBy( poses[ [ "直立", "おっと", "しゃがみ" ][ 0 ] ] ); // timerID = setInterval( this.run.bind( this ), timerMS ); } else { svcm.offsetObject = svcm.svcs[ "右つまさき" ]; svcm.offsetXMember = "LTAX"; svcm.offsetYMember = "LTAY"; svcm.poseBy( poses[ [ "直立", "おっと", "しゃがみ" ][ 2 ] ] ); // svcm.output(); svcm.top.calc(); this.draw(); } } else { svcs[ "red" ].calc(); this.draw(); onkeydown = frame; } messIdx = 0; }//svc_onloadx app.run = function() { var drawFlg = false; if( debug_useGirl ) { //アニメを処理 for( var i = anms.array.length - 1; i >= 0; i-- ) { var anm = anms.array[ i ]; //check. アニメは終了した if( anm.endFlg ) { anms.array.splice( i, 1 ); anm.pose.endCount++; //check. ポーズは終了した if( anm.pose.endCount == anm.pose.data.length ) { anm.pose.nextFunc() } continue; } //アニメのframeを実行 anm.frame(); drawFlg = true; } //描画する場合 if( drawFlg ) { //ツリー全体を計算 svcm.top.calc(); //描画 this.draw(); } } else { svcs[ "red" ].thetaR += .1; svcs[ "green" ].thetaR += .1; svcs[ "red" ].calc(); this.draw(); } } app.draw = function() { this.cc.clear(); this.cc.strokeStyle = "cyan"; this.cc.cross(); this.cc.save(); this.cc.translate( this.cc.centerX(), this.cc.centerY() ); if( debug_useGirl ) { //大きさ、位置 switch( 0 ) { case 0: //normal var sz = 1.1; this.cc.translate( 30, 200 ); this.cc.scale( sz, sz ); break; case 1: //small var sz = .4; this.cc.scale( sz, sz ); break; case 2: //big this.cc.translate( 0, 0 ); var sz = 2; this.cc.scale( sz, sz ); break; case 3: // break; } } else { } svcm.draw( this.cc ); this.cc.restore(); } app.drawMess = function() { var w = 180; var h = 150; var name = "mess" + ( messIdx % 3 + 1 ); this.cc.drawImage( this.images[ name ], this.cc.canvas.width - w, 100, w, h ); messIdx ++; } //---CLASSES //anmを束ねるクラス function Anms( timerMS ) { this.timerMS = timerMS; //クラスのメリット1: timerMSの値を共有 this.array = new Array(); } //クラスのメリット2: ポーズデータから一括で登録 Anms.prototype.addWithPose = function( pose ) { console.log( "addWithPose:", pose.name ); pose.endCount = 0; for( var i = 0; i < pose.data.length; i++ ) { var data = pose.data[ i ]; var object = data[ 0 ]; var member = data[ 1 ]; var value = data[ 2 ]; var isTheta = data[ 3 ]; var anm = new Anm( object, member, value, pose.tm, this.timerMS, isTheta ); anm.pose = pose; this.array.push( anm ); } } function Anm( object, member, endValue, tm, timerMS, isTheta ) { //check. 0~6/28に収める if( isTheta ) { endValue = Svc.utlNormalTheta( endValue ); object[ member ] = Svc.utlNormalTheta( object[ member ] ); object.limitTheta = Svc.utlNormalTheta( object.limitTheta ); } this.object = object; this.member = member; this.endValue = endValue; this.isTheta = isTheta; this.startValue = this.object[ this.member ]; if( this.isTheta ) { var pi2 = Math.PI * 2; var maxCount = tm / timerMS; if( this.endValue > this.startValue ) { if( this.object.limitTheta > this.startValue && this.object.limitTheta < this.endValue ) { //逆 var length = this.startValue + ( pi2 - this.endValue ); this.stepValue = -length / maxCount; } else { //順 var length = this.endValue - this.startValue; this.stepValue = length / maxCount; } } else { if( this.object.limitTheta < this.startValue && this.object.limitTheta > this.endValue ) { var length = this.endValue + ( pi2 - this.startValue ); this.stepValue = length / maxCount; } else { var length = this.endValue - this.startValue; this.stepValue = length / maxCount; } } } else { var length = this.endValue - this.startValue; this.stepValue = length / maxCount; } this.count = 0; this.endFlg = false; this.array = new Array(); var value = this.startValue; for( var i = 0; i < maxCount; i++ ) { value += this.stepValue; this.array.push( Svc.utlNormalTheta( value ) ); } return; //加算方向決め // this.direction = this.object[ this.member ] < this.endValue ? 1 : -1; if( this.isTheta ) { if( this.object.limitTheta > this.object[ this.member ] && this.object.limitTheta < this.endValue ) { this.stepValue *= -1 } } else { } } Anm.prototype.frame = function() { //オブジェクトの値を加算 this.object[ this.member ] = this.array[ this.count++ ]; if( this.count == this.array.length ) { this.endFlg = true; this.object[ this.member ] = this.endValue; } return; //check. thetaの場合、値を0~6.28に直す。 if( this.isTheta ) { this.object[ this.member ] = Svc.utlNormalTheta( this.object[ this.member ] ); } //check. 目的値に達した if( this.stepValue > 0 && this.object[ this.member ] >= this.endValue || this.stepValue < 0 && this.object[ this.member ] <= this.endValue ) { this.endFlg = true; this.object[ this.member ] = this.endValue; } } function Svcm() { this.top = null; this.svcs = new Object(); this.zIndex = new Array(); this.offsetObject = null; this.offsetXMember = null; this.offsetYMember = null; } Svcm.prototype.output = function() { for( var name in this.svcs ) { console.log( "[ svcm.svcs[ \"" + name + "\" ], \"thetaR\", " + Math.floor( this.svcs[ name ].thetaR * 100 ) / 100 + " ]," ); } } Svcm.prototype.frame = function() { } Svcm.prototype.poseBy = function( pose ) { var data = pose.data; for( var i = 0; i < data.length; i++ ) { var d = data[ i ]; var object = d[ 0 ]; var member = d[ 1 ]; var value = d[ 2 ]; object[ member ] = value; } } Svcm.prototype.calc = function() { this.top.calc(); } Svcm.prototype.draw = function( cc ) { cc.save(); cc.rotate( -this.offsetObject.thetaA );//-this.offsetObject.thetaA cc.translate( -this.offsetObject[ this.offsetXMember ], -this.offsetObject[ this.offsetYMember ] ); //描画 zIndex順 for( var i = 0; i < this.zIndex.length; i++ ) { var svc = this.zIndex[ i ]; svc.draw( cc ); } cc.restore(); } function Svc( args ) { this.name = args.name; this.width = args.width; this.height = args.height; this.image = args.image; this.thetaA = args.thetaA; this.limitTheta = args.limitTheta; this.isTop = args.isTop; this.LTAX = args.LTAX; this.LTAY = args.LTAY; this.initialLTAX = args.LTAX; this.initialLTAY = args.LTAY; this.zIndex = args.zIndex; this.rOriginX = 0; this.rOriginY = 0; this.thetaR = 0; this.connectors = new Object(); this.connectorP = null; this.connectorC = new Connector( this, "initial" ); //各所で未定義チェックを省くため if( 0 ) { this.connectorC.rx = 32; this.connectorC.ry = - 32; } } Svc.connect = function( parentConnector, childConnector ) { var parent = parentConnector.owner; var child = childConnector.owner; //check. 現状の接続を解除 if( child.connectorP ) child.connectorP.partner = undefined; if( child.connectorC ) child.connectorC.partner = undefined; //~connectorCを決めている場所 child.connectorP = parentConnector; child.connectorC = childConnector; child.connectorP.partner = childConnector; child.connectorC.partner = parentConnector; } Svc.utlKaiten = function( x, y, theta2 ) { var theta1 = Math.atan2( y, x ); var hankei = Math.sqrt( x * x + y * y ); return { X : Math.cos( theta1 + theta2 ) * hankei, Y : Math.sin( theta1 + theta2 ) * hankei }; } Svc.utlKaitenC = function( cx, cy, x, y, theta2 ) { x -= cx; y -= cy; var theta1 = Math.atan2( y, x ); var hankei = Math.sqrt( x * x + y * y ); return { X : Math.cos( theta1 + theta2 ) * hankei + cx, Y : Math.sin( theta1 + theta2 ) * hankei + cy }; } Svc.utlNormalTheta = function( theta ) { //-1や7.5などのthetaを0~6.28の範囲に直します。 var pi2 = Math.PI * 2; if( 1 ) { if( theta < 0 ) { theta = pi2 + theta; theta = theta % pi2; return theta; } else if( theta > pi2 ) { theta = theta % pi2; return theta; } return theta; } else { return ( pi2 + ( theta % pi2 ) ) % pi2; } } Svc.prototype.addConnector = function( name ) { return this.connectors[ name ] = new Connector( this, name ); } Svc.prototype.calcByA = function() { console.log( "calcByA" ); //calcByAは最初に1回だけ実行される //相対座標(thetaR)を算出する if( this.connectorP ) { var parent = this.connectorP.owner; this.thetaR = this.thetaA - parent.thetaA; } else { this.thetaR = this.thetaA; } //コネクタを介して連なるSvcについても計算 for( var name in this.connectors ) { var connector = this.connectors[ name ]; //check. 逆流を回避 if( connector == this.connectorC ) continue; //check. partner未設定 if( ! connector.partner ) continue; connector.partner.owner.calcByA(); } } Svc.prototype.calc = function() { //~calc //calcはアニメのたびに繰り返し実行される //絶対座標を算出する if( this.connectorP ) { //connectorP.rx,ryは親の回転により回転。 //conPK is connectorP Kaiten var parent = this.connectorP.owner; this.thetaA = parent.thetaA + this.thetaR; var conPK = Svc.utlKaiten( this.connectorP.rx, this.connectorP.ry, parent.thetaA ); this.rOriginX = parent.LTAX + conPK.X; this.rOriginY = parent.LTAY + conPK.Y; var conCK = Svc.utlKaiten( this.connectorC.rx, this.connectorC.ry, this.thetaA ); this.LTAX = this.rOriginX - conCK.X; this.LTAY = this.rOriginY - conCK.Y; } else { this.thetaA = this.thetaR; this.rOriginX = this.initialLTAX + this.connectorC.rx; this.rOriginY = this.initialLTAY + this.connectorC.ry; var iLTAK = Svc.utlKaitenC( this.rOriginX, this.rOriginY, this.initialLTAX, this.initialLTAY, this.thetaA ); this.LTAX = iLTAK.X; this.LTAY = iLTAK.Y; //LTAX,AYが変更されないなら、initialLTAXは廃止していいかも。 } //コネクタを介して連なるSvcについても計算 for( var name in this.connectors ) { var connector = this.connectors[ name ]; //check. 逆流を回避 if( connector == this.connectorC ) continue; //check. partner未設定 if( ! connector.partner ) continue; connector.partner.owner.calc(); } } Svc.prototype.draw = function( cc ) { //~draw cc.save(); cc.translate( this.rOriginX, this.rOriginY ); cc.rotate( this.thetaA ); var shape = shapes[ this.name ]; var imgX = -this.connectorC.rx; var imgY = -this.connectorC.ry; if( 1 ) { imgX -= 4.25; imgY -= 4.25; } if( 0 ) { imgX += shape.leftLineWeight / 2; imgY += shape.topLineWeight / 2; } cc.drawImage( this.image, imgX, imgY, this.width, this.height ); if( 1 ) this.debugDraw( cc, imgX, imgY, shape ); cc.restore(); //トップsvcのExcelでの描画位置を表示 if( 0 ) { if( ! this.connectorP ) { var shape = shapes[ this.name ]; var x = shape.left; var y = shape.top; var cx = shape.left + shape.Width / 2; var cy = shape.top + shape.Height / 2; var rad = shape.Rotation * ( Math.PI / 180 ); var k = Svc.utlKaitenC( cx, cy, x, y, rad ); var sz = 64; cc.line( k.X - sz, k.Y, k.X + sz, k.Y, "orange" ); cc.line( k.X, k.Y - sz, k.X, k.Y + sz, "orange" ); } } } Svc.prototype.debugDraw = function( cc, imgX, imgY, shape ) { //コネクタ if( 0 ) { for( var name in this.connectors ) { var connector = this.connectors[ name ]; var x = - this.connectorC.rx + connector.rx; var y = - this.connectorC.ry + connector.ry; cc.circle( x, y, 5, "RGBA(255,0,0,.5)", "" ); if( 0 ) { var len = 96; cc.line( x, y, x + len, y - len, "blue" ); cc.line( x + len, y - len, x + len + 48, y - len, "blue" ); cc.fillStyle = "blue"; cc.font = "8px ''"; cc.fillText( connector.name, x + len, y - len - 2 ); } } } //回転の原点 if( 0 ) cc.circle( 0, 0, 6, "", "black" ); //画像のLT if( 0 ) cc.rect( imgX, imgY, 4, 4, "red", "" ); //画像の範囲 if( 0 ) { cc.strokeStyle = "red"; cc.strokeRect( imgX, imgY, this.width, this.height ); } //excel図形サイズ if( 0 && this.name == "りんかく" ) { cc.strokeStyle = "green"; cc.strokeRect( - this.connectorC.rx + shape.diffLeft, - this.connectorC.ry + shape.diffTop, shape.Width, shape.Height ); } //excel図形の中心 if( 0 ) { xlCenterX = - this.connectorC.rx + shape.diffLeft + shape.Width / 2; //図形の中心 xlCenterY = - this.connectorC.ry + shape.diffTop + shape.Height / 2; //図形の中心 cc.line( xlCenterX - 4, xlCenterY, xlCenterX + 4, xlCenterY, "green" ); cc.line( xlCenterX, xlCenterY - 4, xlCenterX, xlCenterY + 4, "green" ); } } function Connector( owner, name ) { //コネクタコンストラクタ this.owner = owner; this.name = name; this.rx = 0; this.ry = 0; this.partner = null; } //--- app.exec();