Upgrade to Pro — share decks privately, control downloads, hide ads and more …

iOSでグラフを描くために必要な知識

E58d4a4aae067a3e1ca150bc0b5daef7?s=47 Masashi-Sutou
September 02, 2018

 iOSでグラフを描くために必要な知識

iOSDC Japan 2018/9/2 15:10~ Track A レギュラートーク(15分)
Sample: https://github.com/masashi-sutou/PieGraphSampler

E58d4a4aae067a3e1ca150bc0b5daef7?s=128

Masashi-Sutou

September 02, 2018
Tweet

Transcript

  1. J04ͰάϥϑΛඳͨ͘Ίʹ ඞཁͳ஌ࣝ ʙ5SBDL"ϨΪϡϥʔτʔΫʢ෼ʣ

  2. ελσΟϓϥεגࣜձࣾͰJ04ΤϯδχΞΛͯ͠·͢ ਢ౻ক࢙ʢ!LVSPUZBOOʣ

  3. 4UVEZQMVTͷάϥϑʢҰ෦ʣ

  4. ຊ೔ͷτʔΫͰ࿩͢͜ͱ ෼ͱ͍͏୹͍࣌ؒͰ͕͢ άϥϑΛඳͨ͘Ίʹඞཁͳ஌ࣝΛ ͋ΔҰͭͷάϥϑͰՄೳͳݶΓઆ໌͠·͢

  5. ͦΕ͸ԁάϥϑͰ͢

  6. άϥϑͷલʹԁʢ୯Ґԁʣͷ͓͞Β͍ w ୈʙ̐৅ݶͷ஌͕ࣝඞཁ w ๮ંΕઢάϥϑ͸ୈ̍৅ݶͰ͋ Δ͜ͱ͕ଟ͘ɺඳը͸ͦΕ΄Ͳ ೉͘͠ͳ͍ w TJOВ DPTВ

    UBOВʢࡾ֯ؔ਺ʣɺ ϥδΞϯʢݽ౓๏ʣ w $PSF(SBQIJDT $PSF"OJNBUJPO y x      ୈ৅ݶ ୈ৅ݶ ୈ৅ݶ ୈ৅ݶ SBE
  7. ԁάϥϑΛࣗ༝ʹඳ͘͜ͱ͕ग़དྷΕ͹ ଞͷछྨͷάϥϑ͸ԁάϥϑͷ஌ࣝΛ Ԡ༻ͯ͠΄΅ඳ͚ͦ͏ʂ ͱ͍͏͜ͱ͸ɾɾɾ

  8. w 6*,JUͱ$PSF"OJNBUJPO͸ࠨ্۱ݪ఺ʢ6QQFS-FGU0SJHJOʣ Ͱɺ$PSF(SBQIJDT͸ࠨԼ۱ݪ఺ʢ-PXFS-FGU0SJHJOʣ w 6*#F[JFS1BUI͸ɺ$PSF(SBQIJDTͷύεؔ࿈ػೳΛϥοϓ͠ ͨ0CKFDUJWF$ͷϥούʔͰ6*,JUͷΫϥε w $"-BZFSʹ6*#F[JFS1BUIΛ$(1BUIͰ౉ͤ͹ɺΞχϝʔγϣ ϯͷௐ੔͕$"#BTJD"OJNBUJPOͰՄೳ J04ͷάϥϑඳըͷجຊ΋͓͞Β͍

  9. άϥϑͰٻΊΒΕͦ͏ͳཁ݅Λͭఆٛ  άϥϑͷඳը  σʔλϥϕϧͷඳը  Ξχϝʔγϣϯ  δΣενϟʔ

  10. αϯϓϧ1JF(SBQI4BNQMFS IUUQTHJUIVCDPNNBTBTIJTVUPV1JF(SBQI4BNQMFS

  11. άϥϑͷඳը

  12. "OHMFͳͷͰ֯౓ͩͳͱࢥ͏͚Ͳ // endAngle ͸ π / 2 = 90౓ let

    endAngle: CGFloat = .pi / 2 let path = UIBezierPath(arcCenter: .zero, radius: 100, startAngle: 0, endAngle: endAngle, clockwise: true)
  13. ֯౓Ͱ͸ͳ͘ހ౓ʢϥδΞϯʣ // endAngle ͸ π / 2 = 90౓ let

    endAngle: CGFloat = .pi / 2 let path = UIBezierPath(arcCenter: .zero, radius: 100, startAngle: 0, endAngle: endAngle, clockwise: true)
  14. Ͱ΋ɺΠϝʔδͷ͠΍͍֯͢౓Ͱॻ͖͍ͨ // ֯౓ʢ90౓ʣΛހ౓ʢπ / 2ʣʹม׵͢Δ let endAngle = CGFloat(90.0.radianValue) let

    path = UIBezierPath(arcCenter: .zero, radius: 100, startAngle: 0, endAngle: endAngle, clockwise: true)
  15. J04͔Β͸.FBTVSFNFOUͰม׵Մೳ extension Double { // ֯౓(degree) -> ހ౓(radian) var radianValue:

    Double { if #available(iOS 10.0, *) { let dm = Measurement(value: self, unit: UnitAngle.degrees) let rm = dm.converted(to: .radians) return rm.value } else { return self / 180 * .pi } } // ހ౓(radian) -> ֯౓(degree) var degreeValue: Double { if #available(iOS 10.0, *) { let rm = Measurement(value: self, unit: UnitAngle.radians) let dm = rm.converted(to: .degrees) return dm.value } else { return self * 180 / .pi } } }
  16. αϯϓϧͷԁάϥϑͷඳ͖ํΛൈਮ // 0࣌ͷҐஔ͔Β։࢝ var startRad: Double = -90.0.radianValue for (index,

    data) in graphData.enumerated() { // ׂ߹ let ratio: Double = 360 * (data.value / graphDataTotal) // ϥδΞϯʢུɿradʣ let rad: Double = ratio.radianValue // ઔܕͷ൒ܘ let arcRadius: Double = (index == selectedIndex) ? radius * 1.1 : radius // ઔܕ let arcPath = UIBezierPath(arcCenter: .zero, radius: CGFloat(arcRadius), startAngle: CGFloat(startRad), endAngle: CGFloat(startRad + rad), clockwise: true) arcPath.addLine(to: .zero) arcPath.close() arcLayers[index].path = arcPath.cgPath startRad += rad }
  17. ඞཁͳͱ͖ʹ֯౓͔Βހ౓ʹม׵͢Δ // 0࣌ͷҐஔ͔Β։࢝ var startRad: Double = -90.0.radianValue for (index,

    data) in graphData.enumerated() { // ׂ߹ let ratio: Double = 360 * (data.value / graphDataTotal) // ϥδΞϯʢུɿradʣ let rad: Double = ratio.radianValue // ઔܕͷ൒ܘ let arcRadius: Double = (index == selectedIndex) ? radius * 1.1 : radius // ઔܕ let arcPath = UIBezierPath(arcCenter: .zero, radius: CGFloat(arcRadius), startAngle: CGFloat(startRad), endAngle: CGFloat(startRad + rad), clockwise: true) arcPath.addLine(to: .zero) arcPath.close() arcLayers[index].path = arcPath.cgPath startRad += rad }
  18. σʔλϥϕϧͷඳը

  19. σʔλϥϕϧͷҐஔ͕஌Γ͍ͨ

  20. Ґஔ͸ઔܕͷத৺͕ݟӫ͑ྑͦ͞͏ $(1PJOU͸ʁ $(1PJOU͸ʁ

  21. TJOͰZ࣠ DPTͰY࣠ʢۃ࠲ඪˠ௚ަ࠲ඪʣ sinθ = y r → y = r

    * sinθ cosθ = x r → x = r * cosθ В y x      S Y Z Y Z
  22. ͭ·Γɺத৺͔Βͷ֯౓ͱ൒ܘͰҐஔ͕Θ͔Δ func arcTextPoint(arcCenter: CGPoint, radian: Double, radius: Double) -> CGPoint

    { // ൒ܘͷେ͖͞Ͱԁͷ಺΍֎ʹςΩετΛϨΠΞ΢τ let x = radius * cos(radian) let y = radius * sin(radian) return CGPoint(x: arcCenter.x + CGFloat(x), y: arcCenter.y + CGFloat(y)) }
  23. ίʔυͰɺTJOͰZ࣠ DPTͰY࣠ func arcTextPoint(arcCenter: CGPoint, radian: Double, radius: Double) ->

    CGPoint { // ൒ܘͷେ͖͞Ͱԁͷ಺΍֎ʹςΩετΛϨΠΞ΢τ let x = radius * cos(radian) let y = radius * sin(radian) return CGPoint(x: arcCenter.x + CGFloat(x), y: arcCenter.y + CGFloat(y)) }
  24. Ͱ͸ɺ࠾୒ϥϕϧΛઔܕͷத৺ʹ഑ஔ͢Δʹ͸ʁ

  25. தԝͷന͍ԁͷ൒ܘ͕ԁάϥϑͷഒͷ৔߹ ̍ 

  26. ࠾୒ϥϕϧͷ൒ܘ͸    ̓ ̍ 

  27. ࠾୒ϥϕϧͷ֯౓͸ɺ࠾୒άϥϑͷ֯౓ͷ൒෼ ̓ ̍ 

  28. ࠾୒ϥϕϧͷ֯౓ͱ൒ܘ͕Θ͔ͬͨ ̓ ̍ 

  29. αϯϓϧͷσʔλϥϕϧͷॻ͖ํΛൈਮ // 0࣌ͷҐஔ͔Β։࢝ var startRad: Double = -90.0.radianValue for (index,

    data) in graphData.enumerated() { // ׂ߹ let ratio: Double = 360 * (data.value / graphDataTotal) // ϥδΞϯʢུɿradʣ let rad: Double = ratio.radianValue // ઔܕͷςΩετͷҐஔ let arcTextPoint: CGPoint = self.arcTextPoint(arcCenter: .zero, radian: startRad + rad / 2, radius: radius * 0.7) // ςΩετඳը arcTextLayers[index].position = arcTextPoint startRad += rad }
  30. ൒ܘΛഒɺ֯౓Λ൒෼ʹ͢Δ // 0࣌ͷҐஔ͔Β։࢝ var startRad: Double = -90.0.radianValue for (index,

    data) in graphData.enumerated() { // ׂ߹ let ratio: Double = 360 * (data.value / graphDataTotal) // ϥδΞϯʢུɿradʣ let rad: Double = ratio.radianValue // ઔܕͷςΩετͷҐஔ let arcTextPoint: CGPoint = self.arcTextPoint(arcCenter: .zero, radian: startRad + rad / 2, radius: radius * 0.7) // ςΩετඳը arcTextLayers[index].position = arcTextPoint startRad += rad }
  31. ͞Βʹ൒ܘͷࢉग़Λެࣜʹ͓ͯ͘͠ // 0࣌ͷҐஔ͔Β։࢝ var startRad: Double = -90.0.radianValue for (index,

    data) in graphData.enumerated() { // ׂ߹ let ratio: Double = 360 * (data.value / graphDataTotal) // ϥδΞϯʢུɿradʣ let rad: Double = ratio.radianValue // ઔܕͷςΩετͷҐஔ let arcTextRadius: Double = (radius + radius * centerCircleRadiusScale) / 2 let arcTextPoint: CGPoint = self.arcTextPoint(arcCenter: .zero, radian: startRad + rad / 2, radius: arcTextRadius) // ςΩετඳը arcTextLayers[index].position = arcTextPoint startRad += rad }
  32. Ξχϝʔγϣϯ

  33. ϧʔϨοτͷΑ͏ʹճస͢ΔΞχϝʔγϣϯ

  34. $"#BTJD"OJNBUJPOͰରԠ // ԁάϥϑ let keyPath = "transform.rotation" let animation =

    CABasicAnimation(keyPath: keyPath) animation.beginTime = 0 animation.fromValue = 0.0.radianValue animation.toValue = 360.0.radianValue animation.duration = 0 animation.speed = 0.5 animation.repeatCount = .infinity animation.autoreverses = false // ఀࢭޙͷΞχϝʔγϣϯঢ়ଶΛҡ࣋ animation.isRemovedOnCompletion = false animation.fillMode = kCAFillModeForwards let key = "rotationAnimation" arcLayers.forEach { $0.add(animation, forKey: key) }
  35. σʔλϥϕϧ͸ճస͠ͳ͍Α͏ʹٯճస // ԁάϥϑͷσʔλϥϕϧ let keyPath = "transform.rotation" let reverseAnimation =

    CABasicAnimation(keyPath: keyPath) reverseAnimation.beginTime = 0 // ٯʹճస reverseAnimation.fromValue = 360.0.radianValue reverseAnimation.toValue = 0.0.radianValue reverseAnimation.duration = 0 reverseAnimation.speed = 0.5 reverseAnimation.repeatCount = .infinity reverseAnimation.autoreverses = false reverseAnimation.isRemovedOnCompletion = false reverseAnimation.fillMode = kCAFillModeForwards let key = "rotationAnimation" arcTextLayers.forEach { $0.add(reverseAnimation, forKey: key) }
  36. ӈଆͷը໘ͷֆจࣈϥϕϧ͸ճస͠ͳ͍

  37. ॳճͷදࣔΞχϝʔγϣϯ

  38. ͪ͜Β͸$"#BTJD"OJNBUJPOͩͱ೉͍͠ɾɾɾ w ಺ଆͷന͍ԁ͸൒ܘ͕૿Ճ w ֎ଆͷԁάϥϑ͸֯౓͕૿Ճ w ಺ଆ͸lUSBOTGPSNTDBMFzͰ΋ ରԠͰ͖ͦ͏͕ͩɺಉ࣌ʹ֎ଆͷ ԁͷ֯౓΋૿Ճͤ͞Δͷ͸ɺ $"#BTJD"OJNBUJPOͰ͸೉͍͠

  39. $"%JTQMBZ-JOLͰϦϑϨογϡϨʔτͱಉظͯ͠ඳը✍ // Ξχϝʔγϣϯͷઃఆ private let curve: AnimationCurve = .ease private

    lazy var unitBezier = UnitBezier(p1: curve.p1, p2: curve.p2) @objc private func updatePathAnimation(_ sender: CADisplayLink) { guard let startTime = startTimeInterval else { return } let elapsed: Double = CACurrentMediaTime() - startTime // Ξχϝʔγϣϯਐḿ཰Λܭࢉ let progress = (elapsed > 1.0) ? 1.0 : CGFloat(elapsed / 1.0) let animationProgress = unitBezier.solve(t: progress) // άϥϑΛඳը setup(progress: Double(animationProgress)) if progress >= 1.0 { sender.invalidate() } }
  40. ܦա࣌ؒʹ߹ͬͨΞχϝʔγϣϯਐḿ཰͸ʁ // Ξχϝʔγϣϯͷઃఆ private let curve: AnimationCurve = .ease private

    lazy var unitBezier = UnitBezier(p1: curve.p1, p2: curve.p2) @objc private func updatePathAnimation(_ sender: CADisplayLink) { guard let startTime = startTimeInterval else { return } let elapsed: Double = CACurrentMediaTime() - startTime // Ξχϝʔγϣϯਐḿ཰Λܭࢉ let progress = (elapsed > 1.0) ? 1.0 : CGFloat(elapsed / 1.0) let animationProgress = unitBezier.solve(t: progress) // άϥϑΛඳը setup(progress: Double(animationProgress)) if progress >= 1.0 { sender.invalidate() } }
  41. ΞχϝʔγϣϯͷΠʔδϯάΛࣗ༝ʹ࡞Δ ࢀߟIUUQTUFDITUBSUUPEBZUFDIDPNFOUSZJPT@BOJNBUJPO@FBTJOH w !8PSME%PXO5PXO͞ΜͷهࣄΛࢀর w $".FEJB5JNJOH'VODUJPOͱಉ͡Α͏ʹɺΞχϝʔγϣϯ ͷܦա࣌ؒΛݩʹΞχϝʔγϣϯࣗମͷਐḿ཰Λܭࢉ w 8FC,JUͷϕδΣۂઢͷ$ ࣮૷Λ4XJGUͰॻ͖׵͑ͯରԠ

    // Ξχϝʔγϣϯਐḿ཰Λܭࢉ let progress = (elapsed > 1.0) ? 1.0 : CGFloat(elapsed / 1.0) let animationProgress = unitBezier.solve(t: progress) // ਐḿ཰Λ΋ͱʹͯ͠άϥϑΛඳը setup(progress: Double(animationProgress))
  42. αϯϓϧͷॻ͖ํΛൈਮ // தԝʹന͍ԁʢ֎ଆͷԁͷ0.4ഒͷେ͖͞ʣΛஔ͘ let centerCirclePath = UIBezierPath(arcCenter: .zero, radius: CGFloat(radius

    * 0.4 * progress), startAngle: CGFloat(-90.0.radianValue), endAngle: CGFloat(270.0.radianValue), clockwise: true) centerCirclePath.addLine(to: .zero) centerCirclePath.close() // ׂ߹ let ratio: Double = 360 * (data.value / graphDataTotal) // ϥδΞϯʢུɿradʣ let rad: Double = ratio.radianValue * progress
  43. Ξχϝʔγϣϯͷਐḿ཰Λ͔͚Δ // தԝʹന͍ԁʢ֎ଆͷԁͷ0.4ഒͷେ͖͞ʣΛஔ͘ let centerCirclePath = UIBezierPath(arcCenter: .zero, radius: CGFloat(radius

    * 0.4 * progress), startAngle: CGFloat(-90.0.radianValue), endAngle: CGFloat(270.0.radianValue), clockwise: true) centerCirclePath.addLine(to: .zero) centerCirclePath.close() // ׂ߹ let ratio: Double = 360 * (data.value / graphDataTotal) // ϥδΞϯʢུɿradʣ let rad: Double = ratio.radianValue * progress
  44. δΣενϟʔ

  45. 6*5BQ(FTUVSF3FDPHOJ[FS

  46. 6*5BQ(FTUVSF3FDPHOJ[FS

  47. 6*5BQ(FTUVSF3FDPHOJ[FS

  48. 6*5BQ(FTUVSF3FDPHOJ[FS

  49. // ը໘ͷத৺Ͱ͸ͳ͘άϥϑͷத৺ʹ߹ΘͤͯλοϓҐஔΛม׵ let centerOffset = CGAffineTransform(translationX: -centerPoint.x, y: -centerPoint.y) let

    tappedPoint: CGPoint = gesture.location(in: self) .applying(centerOffset) if isDonuts { guard let centerCirclePath = centerCircleLayer?.path, !centerCirclePath.contains(tappedPoint) else { // தԝͷԁΛλοϓ resetNeedsTransform() return } } αϯϓϧͷॻ͖ํΛൈਮ
  50. // ը໘ͷத৺Ͱ͸ͳ͘άϥϑͷத৺ʹ߹ΘͤͯλοϓҐஔΛม׵ let centerOffset = CGAffineTransform(translationX: -centerPoint.x, y: -centerPoint.y) let

    tappedPoint: CGPoint = gesture.location(in: self) .applying(centerOffset) if isDonuts { guard let centerCirclePath = centerCircleLayer?.path, !centerCirclePath.contains(tappedPoint) else { // தԝͷԁΛλοϓ resetNeedsTransform() return } } த৺͔Βλοϓ࠲ඪΛͣΒ͢ʁ
  51. TVCMBZFSͷݪ఺͸MBZPVU4VCWJFXT Ͱௐ੔ override func layoutSubviews() { super.layoutSubviews() arcLayers.forEach { $0.frame.origin

    = centerPoint } centerCircleLayer?.frame.origin = centerPoint }
  52. // ઔܕ let arcPath = UIBezierPath(arcCenter: .zero, radius: CGFloat(arcRadius), startAngle:

    CGFloat(startRad), endAngle: CGFloat(startRad + rad), clockwise: true) arcPath.addLine(to: .zero) arcPath.close() // தԝʹന͍ԁΛஔ͘ let centerCirclePath = UIBezierPath(arcCenter: .zero, radius: CGFloat(radius * 0.4), startAngle: CGFloat(-90.0.radianValue), endAngle: CGFloat(270.0.radianValue), clockwise: true) centerCirclePath.addLine(to: .zero) centerCirclePath.close() QBUI͸ݪ఺ʹඳը͢Δ
  53. arcLayers[index].path = arcPath.cgPath centerCircleLayer?.path = centerCirclePath.cgPath ݪ఺ʹඳ͍ͨQBUIΛTVCMBZFSQBUIʹίϐʔ

  54. λοϓͷ࠲ඪ͸ը໘Ͱ͸ͳ͘QBUIʹͦͬͯͣΒ͢ // ը໘ͷத৺Ͱ͸ͳ͘άϥϑͷத৺ʹ߹ΘͤͯλοϓҐஔΛม׵ let centerOffset = CGAffineTransform(translationX: -centerPoint.x, y: -centerPoint.y)

    let tappedPoint: CGPoint = gesture.location(in: self) .applying(centerOffset) if isDonuts { guard let centerCirclePath = centerCircleLayer?.path, !centerCirclePath.contains(tappedPoint) else { // தԝͷԁΛλοϓ resetNeedsTransform() return } }
  55. ͞ΒʹɺΞχϝʔγϣϯͰ࠲ඪ͕มԽͨ͠ͱ͖͸ʁ w TVCMBZFSQBUI͸ίϐʔͳͷ ͰΞχϝʔγϣϯޙͷมߋ ͕൓ө͞Εͳ͍

  56. Ξχϝʔγϣϯޙͷঢ়ଶ͸QSFTFOUBUJPO  // ແݶճస͕ఀࢭͨ͠ঢ়ଶͷϨΠϠʔ guard let presentation = layer.presentation() else

    { return } // ແݶճస͕ఀࢭͨ͠ϨΠϠʔ࠲ඪʹม׵ͯ͠ઔܕͷίϐʔΛ࠶࡞੒ let copyPath = UIBezierPath(cgPath: layerPath) copyPath.apply(presentation.affineTransform()) if copyPath.contains(tappedPoint) { // ઔܕΛλοϓ selectedIndex = (index == selectedIndex) ? nil : index // άϥϑΛ࠶ඳը setup(progress: 1.0, rotationRadian: Double(currentRotationRadian)) return }
  57. ΞχϝʔγϣϯޙͷQBUIΛੜ੒ͯ͠൑ఆ // ແݶճస͕ఀࢭͨ͠ঢ়ଶͷϨΠϠʔ guard let presentation = layer.presentation() else {

    return } // ແݶճస͕ఀࢭͨ͠ϨΠϠʔ࠲ඪʹม׵ͯ͠ઔܕͷίϐʔΛ࠶࡞੒ let copyPath = UIBezierPath(cgPath: layerPath) copyPath.apply(presentation.affineTransform()) if copyPath.contains(tappedPoint) { // ઔܕΛλοϓ selectedIndex = (index == selectedIndex) ? nil : index // άϥϑΛ࠶ඳը setup(progress: 1.0, rotationRadian: Double(currentRotationRadian)) return }
  58. 6*1BO(FTUVSF3FDPHOJ[FS

  59. 6*1BO(FTUVSF3FDPHOJ[FS

  60. υϥοά։͔࢝Βऴྃ·Ͱͷ֯౓͸ʁ

  61. ઌʹ݁࿦Ͱ͕͢ɺ఺ͷ܏͖͸BUBOͰٻΊΒΕΔ

  62. UBO BUBO BUBOͯɾɾɾ ࢀߟIUUQTRJJUBDPNLJNJ@ESPQDJUFNT FEDFCECEEBDPNNFOUBEFE w !LPIFSʢ͜ͻʔʣ͞Μͷίϝϯτ͕ࢀߟʹͳΓ·͢ w UBOВ͸Z࣠ͱY࣠ͷൺʢϥδΞϯʣ w

    BUBO͸UBOͷٯؔ਺ w BUBO͸൒प෼ʢ౓ʙ౓ʣͷൣғΛฦ͠ɺBUBO ͸̍प෼ʢ౓ʙ౓ʣͷൣғΛฦ͢
  63. UBOВ͸Z࣠ͱY࣠ͷൺʢϥδΞϯʣ y x  ୈ৅ݶ ୈ৅ݶ ୈ৅ݶ ୈ৅ݶ › ›

    w ౓ͷͱ͖ZY
  64. BUBO͸UBOͷٯؔ਺

  65. BUBO͸൒प෼ʢ౓ʙ౓ʣ y x      ୈ৅ݶ ୈ৅ݶ

    ୈ৅ݶ ୈ৅ݶ SBE ౓ ౓
  66. BUBO͸൒प෼ʢ౓ʙ౓ʣͷൣғΛฦ͢ ౓Ͱ͸౓Ͱճసͤ͞Δͱ͖ʹࠔΔ

  67. BUBO͸̍प෼ʢ౓ʙ౓ʣ y x      ୈ৅ݶ ୈ৅ݶ

    ୈ৅ݶ ୈ৅ݶ SBE ౓ ౓
  68. BUBO͸̍प෼ʢ౓ʙ౓ʣͷൣғΛฦ͢

  69. ̎఺ͷBUBOͷࠩ෼ͰɺυϥοΫͨ֯͠౓Λܭࢉ

  70. άϥϑͰٻΊΒΕͦ͏ͳཁ݅Λୡ੒ άϥϑͷඳը σʔλϥϕϧͷඳը Ξχϝʔγϣϯ δΣενϟʔ

  71. ·ͱΊ w ࡾ֯ؔ਺ͱހ౓๏͸άϥϑΛඳ্͘Ͱඞཁͳ஌ࣝͰ͢ʂ w ͜ΕͰϥΠϒϥϦʹཔΒͣάϥϑΛࣗ࡞͢Δ͜ͱ΋ग़དྷͦ͏ Ͱ͢Ͷʂ w Ϣʔβʔ͕تͿૉ੖Β͍͠άϥϑΛσβΠϯ͍ͨ͠ਓ45ɺ ࣮૷͍ͨ͠ਓ67͸ɺͥͻฐࣾͰҰॹʹಇ͖͠·͠ΐ͏

  72. IUUQTHJUIVCDPNNBTBTIJTVUPV1JF(SBQI4BNQMFS ͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠