Network Security Internet Technology Development Database Servers Mobile Phone Android Software Apple Software Computer Software News IT Information

In addition to Weibo, there is also WeChat

Please pay attention

WeChat public account

Shulou

A tutorial on the method of writing a force-oriented diagram from 0

2025-03-26 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >

Share

Shulou(Shulou.com)06/03 Report--

This article mainly explains "the method course of writing a force-oriented diagram from 0". The content of the explanation in this article is simple and clear, and it is easy to learn and understand. let's study and learn the method course of writing a force-oriented diagram from 0.

The following figure is an example of the dynamic project we are going to talk about:

Preface

The force map is no stranger to everyone, and the force map is not lack of force, and when initializing the node and dragging the node in the case of a large amount of data, the whole force map will be moving all the time, and the dense situation will be more serious. and in order to better point to the point, flexible control, to meet different needs, so I intend to implement a simple force map. And make an exploration of collision detection in the process.

The contents include

The whole content is divided into two parts.

Problems in developing force-oriented diagrams with d3.js

Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community

The treatment of multiple edges between two points

Box selection of points

Deletion of point

Thumbnail image

Drag, zoom and thumbnail of the main picture

Implement a simple topology diagram by yourself

Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community

Collision detection

Detection of rectangle and rectangle

Circle and circle

Circle and rectangle

Distribution of points

Movement of points after collision

Drag

Problems in the development of force-oriented diagrams using d3.js

The treatment of multiple edges between two points

The idea is to group the lines between two points, the middle, left and right are divided into three groups, after a good group, when tick rendering, through the number of grouped content, change the bending degree of path to the grouped content.

Box selection of points

Drag and drop to create a rectangular box, drag and drop to determine whether the center point is in the rectangular box is selected. Note: the position needs to be calculated in conjunction with the scale scaled by d3

Delete

The deletion of a point actually deletes all the related points and lines, and after emptying the canvas, it is redrawn with the deleted data.

Thumbnail image

The current logic of the thumbnail is the maximum multiple of the main image as the background, and the width and height of the main image as the width and height of the thumbnail field of view (blue box). Because the width and height of the dom of the thumbnail is determined by css, and the viewbox is the actual width and height, the width and height of the given main image (normal) will be scaled automatically. When dragging the point of the main image and the corresponding operation, the point of the thumbnail is also changed accordingly, in fact, the content of the main image is drawn again in the thumbnail.

/ * * @ params * width thumbnail width * height thumbnail height * width of mainWidth main image * height of mainHeight main image * maximum zoom ratio of zoomMax * / thumbSvg.attr ('width', width) .attr (' height', height) .attr ('viewBox') () = > {/ / the width and height of the thumbnail is the maximum thumbnail ratio of the main image w = mainWidth * zoomMax H = mainHeight * zoomMax; / / sets the offset to move the background image to the center. The difference / 2 between the thumbnail and the main image is the distance x =-(w-mainWidth) / 2; y =-(h-mainHeight) / 2; return `$ {x} ${w} ${h} `;}) DragThumb.attr ('width', mainWidth). Attr (' height', mainHeight); drag, zoom and thumbnail of the main picture

When you call the zoom of the main image (zoom), you will get the zoom and drag information. The thumbnail uses the drag information. Because of the viewbox, the drag information will be scaled automatically. However, it should be noted that the scaling of the main image will change the translate, so you need to deal with the displacement caused by the zooming process.

Because zooming will cause the translate of the main image to change and there will be a difference between the translate caused by manual dragging and dragging, the offset caused by zooming should be deducted.

/ * * @ params * innerZoomInfo thumbnail zoom information * mainTransform main image zoom information * mainWidth,mainHeight main image width and height * / const {innerZoomInfo, mainWidth,mainHeight,} = this / / if the passed scaling value is inconsistent with the previously recorded scaling value, the offset value if (! innerZoomInfo | | innerZoomInfo.k! = = mainTransform.k) {this.moveDiff = {x: (mainWidth-innerZoomInfo.k * mainWidth) / 2, / / the displacement caused by scaling y: (mainHeight-innerZoomInfo.k * mainHeight) / 2 } } const {x: diffX, y: diffY} = this.moveDiff; const {x, y, k} = mainTransform / / the offset of the main image and the scaling data this.dragThumb .attr ('width', mainWidth / k) .attr (' height', mainHeight / k) .attr ('transform', () = > setTransform ({x:-(x-diffX) / k), / / this place should not be directly divided by k. Y should subtract the difference of scaling and divide by K y: (y-diffY) / k),})) Implement a simple topology diagram collision detection rectangle and rectangle detection

The collision between rectangle and rectangle is the best to detect.

From the above diagram, which basically covers the intersection of regular rectangles, we can know that A: red rectangle B: green rectangle is through Y, left and right is through X.

A.x

< B.x + B.width && A.x + A.width >

B.X & & A.Y

< B.y + B.h && A.h + A.y >

B.y

But if the interior is a circle, then if the purple area will be judged as a collision, then the accuracy has a certain deviation, there needs to be a circle detection.

Circle and circle

The logic of circle and circle is also relatively simple, that is, if the distance between two points is less than the sum of the radii of two points, it is a collision.

Var a = dot2.x-dot1.x; var b = dot2.y-dot1.y; return Math.sqrt (a*a+b*b)

< a.radius + b.radius;圆形与矩形 首先来看 矩形与圆形相交是什么样,从图所知矩形与圆形相交,表现为圆点距离矩形最近的点小于圆点半径 则为相交 那么如何得到圆点距离矩形最近的点 从下图就知道了 圆点的延伸是圆点边的一点。crashX = 如果 圆点位于矩形 左侧 矩形(rect).x; 右侧 = rect.x + rect.w 上下 圆点(circle).x crashY = 如果 圆点位于矩形 左右 circle.y; 上 rect.y 上下 rect.y + h 那么两点有了,可以得出两点之间的距离套用圆与圆的公式 var a = crash.x-dot1.x; var b = crash.y-dot1.y; return Math.sqrt(a*a+b*b) < a.radius; 上面就是基本的碰撞逻辑,更复杂的逻辑可以看下面参考文章 [1] 点的分配 点的位置的分配 就是确定中心点后,将关系最多的点作为中心点,其关系点向四周分散,没有关系的同级点,则向中心点四周进行分散,其关系点以确定后位置的点的坐标向周围分散。 根据三角形的正玄、余弦来得值;假设一个圆的圆心坐标是(a,b),半径为r,角度为d 则圆上每个点的坐标可以通过下面的公式得到 /* * @params * d 角度 * r 半径长度 */ X = a + Math.cos(((Math.PI * 2) / 360) * d) * r; Y = b + Math.sin(((Math.PI * 2) / 360) * d) * r; 角度可以通过 关系边进行得到. d = 360/关系边的数量,确定第一圈点的角度。拿到角度后 ,维持一个所有点坐标的对象,再结合圆形与圆形碰撞检测,我们就可以遍历 获取所有点的坐标了 /* * @params * dotsLocations 所有点的坐标信息 */ initNodes() { const { x: centerX, y: centerY } = this.center; const { distance } = this; const getDeg = (all, now) =>

/ (all-(now | | 0)); / / assign the center point to the point with the most lines const centerdot = this.dots [0]; centerdot.x = centerX; centerdot.y = centerY; this.dotsLocations [centerdot.id] = {x: centerX, y: centerY}; this.dots.forEach ((dot) = > {const {x: outx, y: outy} = dot If (! outx & &! outy) {/ / sibling points (unrelated points) traverse by default at 10 degrees of the central store dot = this.getLocation (dot, centerX, centerY,10, distance) .dot;} const {x: cx, y: cy} = dot; const dotsLength = dot.relationDots.length; let {distance: innerDistance} = this / / let addDeg = getDeg (dotsLength); dot.relationDots.forEach ((relationId, index) = > {let relationDot = this.findDot (relationId); if (! relationDot.x & &! relationDot.y) {const {dot: resultDot, isPlus, outerR,} = this.getLocation (relationDot, cx, cy, addDeg, innerDistance) If (isPlus) {/ / if the first lap is finished, start traversing with radius * 2 as the second lap innerDistance = outerR; addDeg = getDeg (dotsLength, index); addDeg + = randomNumber (5,9) / / prevent the angle generated by the points of the first circle and the second circle from being the same, causing the linked lines to overlap} relationDot = resultDot;}};} / / assign the position getLocation (dot, cx, cy, addDeg, distance) {/ / know from the first picture that-90 degrees is the top and cycle from the top to the top let outerDeg =-90 Let outerR = distance; const {distance: addDistance} = this; let firsted; / / for the week after distribution while (Object.keys (this.checkDotLocation (dot)). Length! = 0) {outerDeg + = addDeg; if (outerDeg > 360) {/ / the angle of the second circle is randomly generated before positioning the current point addDeg = randomNumber (10,35); outerDeg = addDeg If (firsted) {outerR + = addDistance;} firsted = true;} const innerLocation = getDegXy (cx, cy, outerDeg, outerR); dot = Object.assign (dot, innerLocation);} this.dotsLocations [dot.id] = {x: dot.x, y: dot.y}; return {dot, isPlus: firsted, outerR,} CheckDotLocation (circleA) {let repeat = false; if (! circleA.x | |! circleA.y) return true; const {forceCollide} = this; console.log (this.dotsLocations) Object.keys (this.dotsLocations). ForEach ((key) = > {if (key = = circleA.id) {return;} const circleB = this.dotsLocations [key] Let isRepeat = Math.sqrt (Math.pow (circleA.x-circleB.x, 2) + Math.pow (circleA.y-circleB.y, 2))

< forceCollide * 2; if(isRepeat)repeat = true; }); return repeat; } } 生成时间与D3 的差不多 碰撞后点的移动 (力?) 碰撞后的逻辑呢 简单的就是已拖动点为圆点,计算碰撞点与圆点的夹角,再通过角度与距离得出碰撞后被碰撞点的x,y的坐标 changeLocation(data, x, y, eliminate) { // 先对原来的点进行赋值 data.x = x; data.y = y; // 对点的坐标进行赋值,使之后的碰撞使用新值进行计算 this.dotsLocations[data.id] = { x, y }; let crashDots = this.checkDotLocation(data); // 获得所有被碰撞的点 Object.keys(crashDots).forEach((crashId) =>

{if (eliminate = crashId) return; / / collision prevention from changing the current drag element const crashDot = this.findDot (crashId); / / get the x _ crashX y value const {x: crashX, y: crashY} = crashDot; / / the angle here is the direction to be moved let deg = getDeg (crashDot.x,crashDot.y,data.x,data.y) / / the purpose of / /-180 is to be consistent with the black image above / / 2 is the radius of 2 pixels moved after the collision const {x _ v _ endX _ r _ y} = getDegXy (crashDot.x, crashDot.y, deg-180,2) / this.changeLocation (crashDot, endX, endY, data.id) to change the value of the collided point as a dot and perform collision detection (no nesting doll);});}

Get the angle between the angles

Function getDeg {/ / Center point let cx = x1; let cy = y1; / / let C1 = Math.atan2 (y1-cy, x1-cx) * 180 / (Math.PI); let c2 = Math.atan2 (y2-cy, x2-cx) * 180 / (Math.PI); let angle C1 = C1 = svgH-10) {/ / Boundary dragDom = null; return;} force.changeLocation (dragDom.data, x, y); tick ();}); d3.select ("svg"). On ("mouseup", function (d) {dragDom = null;}) Thank you for your reading. the above is the content of "the method course of writing a force-oriented diagram from 0". After the study of this article, I believe you have a deeper understanding of the problem of writing a force-oriented diagram method tutorial from 0, and the specific use still needs to be verified by practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!

Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.

Views: 0

*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.

Share To

Development

Wechat

© 2024 shulou.com SLNews company. All rights reserved.

12
Report