@lv-x-software-house/x_view 1.3.9 → 1.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -6232,7 +6232,8 @@ function XViewSidebar({
6232
6232
  options: [
6233
6233
  { value: "Star", label: "Star" },
6234
6234
  { value: "Circle", label: "Circle" },
6235
- { value: "Column", label: "Column" }
6235
+ { value: "Column", label: "Column" },
6236
+ { value: "Network", label: "Network" }
6236
6237
  ],
6237
6238
  placeholder: "Selecione um Formato"
6238
6239
  }
@@ -20807,13 +20808,22 @@ function XViewScene({
20807
20808
  );
20808
20809
  const handleActivateReshapeLens = (0, import_react38.useCallback)(
20809
20810
  ({ type, columns, centerNodeId }) => {
20810
- const { nodeObjects, tweenGroup, camera, controls } = stateRef.current;
20811
+ const {
20812
+ nodeObjects,
20813
+ allLinks = [],
20814
+ ancestryLinks = [],
20815
+ tweenGroup,
20816
+ camera,
20817
+ controls
20818
+ } = stateRef.current;
20811
20819
  if (!nodeObjects || !tweenGroup || !camera || !controls) return;
20812
20820
  const activeNodeIds = Object.keys(nodeObjects);
20813
20821
  if (activeNodeIds.length === 0) return;
20814
20822
  let sumX = 0;
20815
20823
  let sumY = 0;
20824
+ let sumZ = 0;
20816
20825
  let nodeCount = 0;
20826
+ let networkMaxRadius = 150;
20817
20827
  activeNodeIds.forEach((id) => {
20818
20828
  const mesh = nodeObjects[id];
20819
20829
  if (mesh && mesh.material) {
@@ -20916,23 +20926,249 @@ function XViewScene({
20916
20926
  sumY += targetY;
20917
20927
  nodeCount++;
20918
20928
  });
20929
+ } else if (type === "Network") {
20930
+ const N = activeNodeIds.length;
20931
+ const activeSet = new Set(activeNodeIds.map(String));
20932
+ const edges = [];
20933
+ const degreeMap = new Map(activeNodeIds.map((id) => [String(id), 0]));
20934
+ const adjMap = new Map(activeNodeIds.map((id) => [String(id), []]));
20935
+ const seenPairs = /* @__PURE__ */ new Set();
20936
+ [...allLinks, ...ancestryLinks].forEach((line) => {
20937
+ var _a2, _b2, _c2, _d2, _e2, _f2, _g2, _h2;
20938
+ const u = String(
20939
+ ((_c2 = (_b2 = (_a2 = line.userData) == null ? void 0 : _a2.sourceNode) == null ? void 0 : _b2.userData) == null ? void 0 : _c2.id) ?? ((_d2 = line.userData) == null ? void 0 : _d2.source) ?? ""
20940
+ );
20941
+ const v = String(
20942
+ ((_g2 = (_f2 = (_e2 = line.userData) == null ? void 0 : _e2.targetNode) == null ? void 0 : _f2.userData) == null ? void 0 : _g2.id) ?? ((_h2 = line.userData) == null ? void 0 : _h2.target) ?? ""
20943
+ );
20944
+ if (u && v && u !== v && activeSet.has(u) && activeSet.has(v)) {
20945
+ const pairKey = u < v ? `${u}_${v}` : `${v}_${u}`;
20946
+ if (!seenPairs.has(pairKey)) {
20947
+ seenPairs.add(pairKey);
20948
+ edges.push([u, v]);
20949
+ degreeMap.set(u, (degreeMap.get(u) || 0) + 1);
20950
+ degreeMap.set(v, (degreeMap.get(v) || 0) + 1);
20951
+ adjMap.get(u).push(v);
20952
+ adjMap.get(v).push(u);
20953
+ }
20954
+ }
20955
+ });
20956
+ const hubCount = Array.from(degreeMap.values()).filter(
20957
+ (d) => d >= 3
20958
+ ).length;
20959
+ const volumeRadius = Math.max(
20960
+ 130,
20961
+ Math.cbrt(N) * 58 + Math.sqrt(N) * 24 + hubCount * 8
20962
+ );
20963
+ const radiusX = volumeRadius * 1.45;
20964
+ const radiusY = volumeRadius * 1.15;
20965
+ const radiusZ = volumeRadius * 1.15;
20966
+ const connectedIds = activeNodeIds.map(String).filter((id) => (degreeMap.get(id) || 0) > 0).sort((a, b) => (degreeMap.get(b) || 0) - (degreeMap.get(a) || 0));
20967
+ const isolatedIds = activeNodeIds.map(String).filter((id) => (degreeMap.get(id) || 0) === 0);
20968
+ const interleavedIds = [];
20969
+ let cIdx = 0;
20970
+ let iIdx = 0;
20971
+ for (let k = 0; k < N; k++) {
20972
+ const takeConnected = cIdx < connectedIds.length && (iIdx >= isolatedIds.length || cIdx / Math.max(1, connectedIds.length) <= iIdx / Math.max(1, isolatedIds.length));
20973
+ if (takeConnected) {
20974
+ interleavedIds.push(connectedIds[cIdx++]);
20975
+ } else if (iIdx < isolatedIds.length) {
20976
+ interleavedIds.push(isolatedIds[iIdx++]);
20977
+ }
20978
+ }
20979
+ const posMap = /* @__PURE__ */ new Map();
20980
+ const anchorMap = /* @__PURE__ */ new Map();
20981
+ const velMap = /* @__PURE__ */ new Map();
20982
+ const goldenAngle = Math.PI * (3 - Math.sqrt(5));
20983
+ interleavedIds.forEach((id, idx) => {
20984
+ const t = (idx + 0.5) / Math.max(1, N);
20985
+ const yDir = 1 - 2 * t;
20986
+ const radiusAtY = Math.sqrt(Math.max(0, 1 - yDir * yDir));
20987
+ const theta = idx * goldenAngle;
20988
+ const xDir = Math.cos(theta) * radiusAtY;
20989
+ const zDir = Math.sin(theta) * radiusAtY;
20990
+ const shell = 0.3 + 0.7 * Math.cbrt(idx * 7 % Math.max(1, N) / Math.max(1, N));
20991
+ const px = xDir * shell * radiusX;
20992
+ const py = yDir * shell * radiusY;
20993
+ const pz = zDir * shell * radiusZ;
20994
+ posMap.set(id, { x: px, y: py, z: pz });
20995
+ anchorMap.set(id, { x: px, y: py, z: pz });
20996
+ velMap.set(id, { x: 0, y: 0, z: 0 });
20997
+ });
20998
+ connectedIds.forEach((id, idx) => {
20999
+ const deg = degreeMap.get(id) || 0;
21000
+ if (deg <= 2) {
21001
+ const neighbors = adjMap.get(id) || [];
21002
+ const hubNeighbor = neighbors.reduce(
21003
+ (best, curr) => (degreeMap.get(curr) || 0) > (degreeMap.get(best) || 0) ? curr : best,
21004
+ neighbors[0]
21005
+ );
21006
+ const hubPos = posMap.get(hubNeighbor);
21007
+ const myPos = posMap.get(id);
21008
+ if (hubPos && myPos && hubNeighbor !== id) {
21009
+ const phi = idx * goldenAngle * 3;
21010
+ const costheta = Math.cos(idx * 1.732);
21011
+ const sintheta = Math.sqrt(Math.max(0, 1 - costheta * costheta));
21012
+ const orbitDist = 62 + idx % 3 * 16;
21013
+ myPos.x = hubPos.x + sintheta * Math.cos(phi) * orbitDist;
21014
+ myPos.y = hubPos.y + sintheta * Math.sin(phi) * orbitDist;
21015
+ myPos.z = hubPos.z + costheta * orbitDist;
21016
+ }
21017
+ }
21018
+ });
21019
+ const iterations = 130;
21020
+ const minNodeDist = 56;
21021
+ const minHubDist = 140;
21022
+ const idealEdgeDist = 72;
21023
+ const repulsionRadius = 150;
21024
+ for (let iter = 0; iter < iterations; iter++) {
21025
+ const cooling = 1 - iter / iterations * 0.78;
21026
+ for (let i = 0; i < N; i++) {
21027
+ const idA = interleavedIds[i];
21028
+ const pA = posMap.get(idA);
21029
+ const vA = velMap.get(idA);
21030
+ const degA = degreeMap.get(idA) || 0;
21031
+ for (let j = i + 1; j < N; j++) {
21032
+ const idB = interleavedIds[j];
21033
+ const pB = posMap.get(idB);
21034
+ const vB = velMap.get(idB);
21035
+ const degB = degreeMap.get(idB) || 0;
21036
+ const dx = pA.x - pB.x;
21037
+ const dy = pA.y - pB.y;
21038
+ const dz = pA.z - pB.z;
21039
+ const dist = Math.sqrt(dx * dx + dy * dy + dz * dz + 0.1);
21040
+ const bothHubs = degA >= 3 && degB >= 3;
21041
+ const oneHub = degA >= 3 || degB >= 3;
21042
+ const effectiveMinDist = bothHubs ? minHubDist : oneHub ? minNodeDist * 1.35 : minNodeDist;
21043
+ const effectiveRepRadius = bothHubs ? repulsionRadius * 1.6 : repulsionRadius;
21044
+ if (dist < effectiveRepRadius) {
21045
+ const force = dist < effectiveMinDist ? (effectiveMinDist - dist) / dist * 2.4 : (effectiveRepRadius - dist) / effectiveRepRadius * 0.65 / dist;
21046
+ const fx = dx * force;
21047
+ const fy = dy * force;
21048
+ const fz = dz * force;
21049
+ vA.x += fx;
21050
+ vA.y += fy;
21051
+ vA.z += fz;
21052
+ vB.x -= fx;
21053
+ vB.y -= fy;
21054
+ vB.z -= fz;
21055
+ }
21056
+ }
21057
+ }
21058
+ for (let e = 0; e < edges.length; e++) {
21059
+ const [u, v] = edges[e];
21060
+ const pU = posMap.get(u);
21061
+ const pV = posMap.get(v);
21062
+ const vU = velMap.get(u);
21063
+ const vV = velMap.get(v);
21064
+ const degU = degreeMap.get(u) || 1;
21065
+ const degV = degreeMap.get(v) || 1;
21066
+ const dx = pV.x - pU.x;
21067
+ const dy = pV.y - pU.y;
21068
+ const dz = pV.z - pU.z;
21069
+ const dist = Math.sqrt(dx * dx + dy * dy + dz * dz) || 1;
21070
+ const delta = dist - idealEdgeDist;
21071
+ const degreeDamping = 1 / Math.sqrt(degU + degV);
21072
+ const springStrength = 0.08 * degreeDamping;
21073
+ const fx = dx / dist * delta * springStrength;
21074
+ const fy = dy / dist * delta * springStrength;
21075
+ const fz = dz / dist * delta * springStrength;
21076
+ const weightU = degV / (degU + degV);
21077
+ const weightV = degU / (degU + degV);
21078
+ vU.x += fx * weightU * 1.7;
21079
+ vU.y += fy * weightU * 1.7;
21080
+ vU.z += fz * weightU * 1.7;
21081
+ vV.x -= fx * weightV * 1.7;
21082
+ vV.y -= fy * weightV * 1.7;
21083
+ vV.z -= fz * weightV * 1.7;
21084
+ }
21085
+ for (let i = 0; i < N; i++) {
21086
+ const id = interleavedIds[i];
21087
+ const p = posMap.get(id);
21088
+ const v = velMap.get(id);
21089
+ const anchor = anchorMap.get(id);
21090
+ const deg = degreeMap.get(id) || 0;
21091
+ if (deg === 0 || deg >= 3) {
21092
+ const anchorPull = deg === 0 ? 0.055 : 0.035;
21093
+ v.x += (anchor.x - p.x) * anchorPull;
21094
+ v.y += (anchor.y - p.y) * anchorPull;
21095
+ v.z += (anchor.z - p.z) * anchorPull;
21096
+ }
21097
+ const maxStep = 10 * cooling;
21098
+ const speed = Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z) || 1;
21099
+ const scale = speed > maxStep ? maxStep / speed : cooling;
21100
+ p.x += v.x * scale;
21101
+ p.y += v.y * scale;
21102
+ p.z += v.z * scale;
21103
+ v.x *= 0.4;
21104
+ v.y *= 0.4;
21105
+ v.z *= 0.4;
21106
+ }
21107
+ }
21108
+ activeNodeIds.forEach((id) => {
21109
+ const mesh = nodeObjects[id];
21110
+ const target = posMap.get(String(id)) || { x: 0, y: 0, z: 0 };
21111
+ const posTween = new import_tween2.Tween(mesh.position).to({ x: target.x, y: target.y, z: target.z }, 1500).easing(import_tween2.Easing.Cubic.InOut);
21112
+ tweenGroup.add(posTween);
21113
+ posTween.start();
21114
+ if (mesh.userData.labelObject) {
21115
+ const labelTarget = new THREE3.Vector3(
21116
+ target.x,
21117
+ target.y,
21118
+ target.z
21119
+ ).add(mesh.userData.labelOffset || new THREE3.Vector3(0, 3.8, 0));
21120
+ const lblTween = new import_tween2.Tween(mesh.userData.labelObject.position).to(labelTarget, 1500).easing(import_tween2.Easing.Cubic.InOut);
21121
+ tweenGroup.add(lblTween);
21122
+ lblTween.start();
21123
+ }
21124
+ sumX += target.x;
21125
+ sumY += target.y;
21126
+ sumZ += target.z;
21127
+ nodeCount++;
21128
+ });
21129
+ if (nodeCount > 0) {
21130
+ const cx = sumX / nodeCount;
21131
+ const cy = sumY / nodeCount;
21132
+ const cz = sumZ / nodeCount;
21133
+ let maxDist = 120;
21134
+ activeNodeIds.forEach((id) => {
21135
+ const p = posMap.get(String(id));
21136
+ if (p) {
21137
+ const d = Math.hypot(p.x - cx, p.y - cy, p.z - cz);
21138
+ if (d > maxDist) maxDist = d;
21139
+ }
21140
+ });
21141
+ networkMaxRadius = maxDist;
21142
+ }
20919
21143
  }
20920
21144
  if (nodeCount > 0) {
20921
21145
  const centerX = sumX / nodeCount;
20922
21146
  const centerY = sumY / nodeCount;
20923
- const targetFocus = new THREE3.Vector3(centerX, centerY, 0);
21147
+ const centerZ = type === "Network" ? sumZ / nodeCount : 0;
21148
+ const targetFocus = new THREE3.Vector3(centerX, centerY, centerZ);
20924
21149
  const targetTween = new import_tween2.Tween(controls.target).to({ x: targetFocus.x, y: targetFocus.y, z: targetFocus.z }, 1500).easing(import_tween2.Easing.Cubic.InOut);
20925
21150
  tweenGroup.add(targetTween);
20926
21151
  targetTween.start();
20927
21152
  let zoomZ = 140;
21153
+ let camOffsetY = 0;
20928
21154
  if (type === "Column") {
20929
21155
  const numRows = Math.ceil(nodeCount / (columns || 4));
20930
21156
  zoomZ = Math.max(140, numRows * 50);
20931
21157
  } else if (type === "Circle" || type === "Star") {
20932
21158
  const radius = Math.max(80, nodeCount * 8);
20933
21159
  zoomZ = radius * 2.5;
21160
+ } else if (type === "Network") {
21161
+ zoomZ = targetFocus.z + Math.max(220, networkMaxRadius * 1.85);
21162
+ camOffsetY = networkMaxRadius * 0.12;
20934
21163
  }
20935
- const camTween = new import_tween2.Tween(camera.position).to({ x: targetFocus.x, y: targetFocus.y, z: zoomZ }, 1500).easing(import_tween2.Easing.Cubic.InOut);
21164
+ const camTween = new import_tween2.Tween(camera.position).to(
21165
+ {
21166
+ x: targetFocus.x,
21167
+ y: targetFocus.y + camOffsetY,
21168
+ z: zoomZ
21169
+ },
21170
+ 1500
21171
+ ).easing(import_tween2.Easing.Cubic.InOut);
20936
21172
  tweenGroup.add(camTween);
20937
21173
  camTween.start();
20938
21174
  }
package/dist/index.mjs CHANGED
@@ -6193,7 +6193,8 @@ function XViewSidebar({
6193
6193
  options: [
6194
6194
  { value: "Star", label: "Star" },
6195
6195
  { value: "Circle", label: "Circle" },
6196
- { value: "Column", label: "Column" }
6196
+ { value: "Column", label: "Column" },
6197
+ { value: "Network", label: "Network" }
6197
6198
  ],
6198
6199
  placeholder: "Selecione um Formato"
6199
6200
  }
@@ -20909,13 +20910,22 @@ function XViewScene({
20909
20910
  );
20910
20911
  const handleActivateReshapeLens = useCallback8(
20911
20912
  ({ type, columns, centerNodeId }) => {
20912
- const { nodeObjects, tweenGroup, camera, controls } = stateRef.current;
20913
+ const {
20914
+ nodeObjects,
20915
+ allLinks = [],
20916
+ ancestryLinks = [],
20917
+ tweenGroup,
20918
+ camera,
20919
+ controls
20920
+ } = stateRef.current;
20913
20921
  if (!nodeObjects || !tweenGroup || !camera || !controls) return;
20914
20922
  const activeNodeIds = Object.keys(nodeObjects);
20915
20923
  if (activeNodeIds.length === 0) return;
20916
20924
  let sumX = 0;
20917
20925
  let sumY = 0;
20926
+ let sumZ = 0;
20918
20927
  let nodeCount = 0;
20928
+ let networkMaxRadius = 150;
20919
20929
  activeNodeIds.forEach((id) => {
20920
20930
  const mesh = nodeObjects[id];
20921
20931
  if (mesh && mesh.material) {
@@ -21018,23 +21028,249 @@ function XViewScene({
21018
21028
  sumY += targetY;
21019
21029
  nodeCount++;
21020
21030
  });
21031
+ } else if (type === "Network") {
21032
+ const N = activeNodeIds.length;
21033
+ const activeSet = new Set(activeNodeIds.map(String));
21034
+ const edges = [];
21035
+ const degreeMap = new Map(activeNodeIds.map((id) => [String(id), 0]));
21036
+ const adjMap = new Map(activeNodeIds.map((id) => [String(id), []]));
21037
+ const seenPairs = /* @__PURE__ */ new Set();
21038
+ [...allLinks, ...ancestryLinks].forEach((line) => {
21039
+ var _a2, _b2, _c2, _d2, _e2, _f2, _g2, _h2;
21040
+ const u = String(
21041
+ ((_c2 = (_b2 = (_a2 = line.userData) == null ? void 0 : _a2.sourceNode) == null ? void 0 : _b2.userData) == null ? void 0 : _c2.id) ?? ((_d2 = line.userData) == null ? void 0 : _d2.source) ?? ""
21042
+ );
21043
+ const v = String(
21044
+ ((_g2 = (_f2 = (_e2 = line.userData) == null ? void 0 : _e2.targetNode) == null ? void 0 : _f2.userData) == null ? void 0 : _g2.id) ?? ((_h2 = line.userData) == null ? void 0 : _h2.target) ?? ""
21045
+ );
21046
+ if (u && v && u !== v && activeSet.has(u) && activeSet.has(v)) {
21047
+ const pairKey = u < v ? `${u}_${v}` : `${v}_${u}`;
21048
+ if (!seenPairs.has(pairKey)) {
21049
+ seenPairs.add(pairKey);
21050
+ edges.push([u, v]);
21051
+ degreeMap.set(u, (degreeMap.get(u) || 0) + 1);
21052
+ degreeMap.set(v, (degreeMap.get(v) || 0) + 1);
21053
+ adjMap.get(u).push(v);
21054
+ adjMap.get(v).push(u);
21055
+ }
21056
+ }
21057
+ });
21058
+ const hubCount = Array.from(degreeMap.values()).filter(
21059
+ (d) => d >= 3
21060
+ ).length;
21061
+ const volumeRadius = Math.max(
21062
+ 130,
21063
+ Math.cbrt(N) * 58 + Math.sqrt(N) * 24 + hubCount * 8
21064
+ );
21065
+ const radiusX = volumeRadius * 1.45;
21066
+ const radiusY = volumeRadius * 1.15;
21067
+ const radiusZ = volumeRadius * 1.15;
21068
+ const connectedIds = activeNodeIds.map(String).filter((id) => (degreeMap.get(id) || 0) > 0).sort((a, b) => (degreeMap.get(b) || 0) - (degreeMap.get(a) || 0));
21069
+ const isolatedIds = activeNodeIds.map(String).filter((id) => (degreeMap.get(id) || 0) === 0);
21070
+ const interleavedIds = [];
21071
+ let cIdx = 0;
21072
+ let iIdx = 0;
21073
+ for (let k = 0; k < N; k++) {
21074
+ const takeConnected = cIdx < connectedIds.length && (iIdx >= isolatedIds.length || cIdx / Math.max(1, connectedIds.length) <= iIdx / Math.max(1, isolatedIds.length));
21075
+ if (takeConnected) {
21076
+ interleavedIds.push(connectedIds[cIdx++]);
21077
+ } else if (iIdx < isolatedIds.length) {
21078
+ interleavedIds.push(isolatedIds[iIdx++]);
21079
+ }
21080
+ }
21081
+ const posMap = /* @__PURE__ */ new Map();
21082
+ const anchorMap = /* @__PURE__ */ new Map();
21083
+ const velMap = /* @__PURE__ */ new Map();
21084
+ const goldenAngle = Math.PI * (3 - Math.sqrt(5));
21085
+ interleavedIds.forEach((id, idx) => {
21086
+ const t = (idx + 0.5) / Math.max(1, N);
21087
+ const yDir = 1 - 2 * t;
21088
+ const radiusAtY = Math.sqrt(Math.max(0, 1 - yDir * yDir));
21089
+ const theta = idx * goldenAngle;
21090
+ const xDir = Math.cos(theta) * radiusAtY;
21091
+ const zDir = Math.sin(theta) * radiusAtY;
21092
+ const shell = 0.3 + 0.7 * Math.cbrt(idx * 7 % Math.max(1, N) / Math.max(1, N));
21093
+ const px = xDir * shell * radiusX;
21094
+ const py = yDir * shell * radiusY;
21095
+ const pz = zDir * shell * radiusZ;
21096
+ posMap.set(id, { x: px, y: py, z: pz });
21097
+ anchorMap.set(id, { x: px, y: py, z: pz });
21098
+ velMap.set(id, { x: 0, y: 0, z: 0 });
21099
+ });
21100
+ connectedIds.forEach((id, idx) => {
21101
+ const deg = degreeMap.get(id) || 0;
21102
+ if (deg <= 2) {
21103
+ const neighbors = adjMap.get(id) || [];
21104
+ const hubNeighbor = neighbors.reduce(
21105
+ (best, curr) => (degreeMap.get(curr) || 0) > (degreeMap.get(best) || 0) ? curr : best,
21106
+ neighbors[0]
21107
+ );
21108
+ const hubPos = posMap.get(hubNeighbor);
21109
+ const myPos = posMap.get(id);
21110
+ if (hubPos && myPos && hubNeighbor !== id) {
21111
+ const phi = idx * goldenAngle * 3;
21112
+ const costheta = Math.cos(idx * 1.732);
21113
+ const sintheta = Math.sqrt(Math.max(0, 1 - costheta * costheta));
21114
+ const orbitDist = 62 + idx % 3 * 16;
21115
+ myPos.x = hubPos.x + sintheta * Math.cos(phi) * orbitDist;
21116
+ myPos.y = hubPos.y + sintheta * Math.sin(phi) * orbitDist;
21117
+ myPos.z = hubPos.z + costheta * orbitDist;
21118
+ }
21119
+ }
21120
+ });
21121
+ const iterations = 130;
21122
+ const minNodeDist = 56;
21123
+ const minHubDist = 140;
21124
+ const idealEdgeDist = 72;
21125
+ const repulsionRadius = 150;
21126
+ for (let iter = 0; iter < iterations; iter++) {
21127
+ const cooling = 1 - iter / iterations * 0.78;
21128
+ for (let i = 0; i < N; i++) {
21129
+ const idA = interleavedIds[i];
21130
+ const pA = posMap.get(idA);
21131
+ const vA = velMap.get(idA);
21132
+ const degA = degreeMap.get(idA) || 0;
21133
+ for (let j = i + 1; j < N; j++) {
21134
+ const idB = interleavedIds[j];
21135
+ const pB = posMap.get(idB);
21136
+ const vB = velMap.get(idB);
21137
+ const degB = degreeMap.get(idB) || 0;
21138
+ const dx = pA.x - pB.x;
21139
+ const dy = pA.y - pB.y;
21140
+ const dz = pA.z - pB.z;
21141
+ const dist = Math.sqrt(dx * dx + dy * dy + dz * dz + 0.1);
21142
+ const bothHubs = degA >= 3 && degB >= 3;
21143
+ const oneHub = degA >= 3 || degB >= 3;
21144
+ const effectiveMinDist = bothHubs ? minHubDist : oneHub ? minNodeDist * 1.35 : minNodeDist;
21145
+ const effectiveRepRadius = bothHubs ? repulsionRadius * 1.6 : repulsionRadius;
21146
+ if (dist < effectiveRepRadius) {
21147
+ const force = dist < effectiveMinDist ? (effectiveMinDist - dist) / dist * 2.4 : (effectiveRepRadius - dist) / effectiveRepRadius * 0.65 / dist;
21148
+ const fx = dx * force;
21149
+ const fy = dy * force;
21150
+ const fz = dz * force;
21151
+ vA.x += fx;
21152
+ vA.y += fy;
21153
+ vA.z += fz;
21154
+ vB.x -= fx;
21155
+ vB.y -= fy;
21156
+ vB.z -= fz;
21157
+ }
21158
+ }
21159
+ }
21160
+ for (let e = 0; e < edges.length; e++) {
21161
+ const [u, v] = edges[e];
21162
+ const pU = posMap.get(u);
21163
+ const pV = posMap.get(v);
21164
+ const vU = velMap.get(u);
21165
+ const vV = velMap.get(v);
21166
+ const degU = degreeMap.get(u) || 1;
21167
+ const degV = degreeMap.get(v) || 1;
21168
+ const dx = pV.x - pU.x;
21169
+ const dy = pV.y - pU.y;
21170
+ const dz = pV.z - pU.z;
21171
+ const dist = Math.sqrt(dx * dx + dy * dy + dz * dz) || 1;
21172
+ const delta = dist - idealEdgeDist;
21173
+ const degreeDamping = 1 / Math.sqrt(degU + degV);
21174
+ const springStrength = 0.08 * degreeDamping;
21175
+ const fx = dx / dist * delta * springStrength;
21176
+ const fy = dy / dist * delta * springStrength;
21177
+ const fz = dz / dist * delta * springStrength;
21178
+ const weightU = degV / (degU + degV);
21179
+ const weightV = degU / (degU + degV);
21180
+ vU.x += fx * weightU * 1.7;
21181
+ vU.y += fy * weightU * 1.7;
21182
+ vU.z += fz * weightU * 1.7;
21183
+ vV.x -= fx * weightV * 1.7;
21184
+ vV.y -= fy * weightV * 1.7;
21185
+ vV.z -= fz * weightV * 1.7;
21186
+ }
21187
+ for (let i = 0; i < N; i++) {
21188
+ const id = interleavedIds[i];
21189
+ const p = posMap.get(id);
21190
+ const v = velMap.get(id);
21191
+ const anchor = anchorMap.get(id);
21192
+ const deg = degreeMap.get(id) || 0;
21193
+ if (deg === 0 || deg >= 3) {
21194
+ const anchorPull = deg === 0 ? 0.055 : 0.035;
21195
+ v.x += (anchor.x - p.x) * anchorPull;
21196
+ v.y += (anchor.y - p.y) * anchorPull;
21197
+ v.z += (anchor.z - p.z) * anchorPull;
21198
+ }
21199
+ const maxStep = 10 * cooling;
21200
+ const speed = Math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z) || 1;
21201
+ const scale = speed > maxStep ? maxStep / speed : cooling;
21202
+ p.x += v.x * scale;
21203
+ p.y += v.y * scale;
21204
+ p.z += v.z * scale;
21205
+ v.x *= 0.4;
21206
+ v.y *= 0.4;
21207
+ v.z *= 0.4;
21208
+ }
21209
+ }
21210
+ activeNodeIds.forEach((id) => {
21211
+ const mesh = nodeObjects[id];
21212
+ const target = posMap.get(String(id)) || { x: 0, y: 0, z: 0 };
21213
+ const posTween = new Tween2(mesh.position).to({ x: target.x, y: target.y, z: target.z }, 1500).easing(Easing2.Cubic.InOut);
21214
+ tweenGroup.add(posTween);
21215
+ posTween.start();
21216
+ if (mesh.userData.labelObject) {
21217
+ const labelTarget = new THREE3.Vector3(
21218
+ target.x,
21219
+ target.y,
21220
+ target.z
21221
+ ).add(mesh.userData.labelOffset || new THREE3.Vector3(0, 3.8, 0));
21222
+ const lblTween = new Tween2(mesh.userData.labelObject.position).to(labelTarget, 1500).easing(Easing2.Cubic.InOut);
21223
+ tweenGroup.add(lblTween);
21224
+ lblTween.start();
21225
+ }
21226
+ sumX += target.x;
21227
+ sumY += target.y;
21228
+ sumZ += target.z;
21229
+ nodeCount++;
21230
+ });
21231
+ if (nodeCount > 0) {
21232
+ const cx = sumX / nodeCount;
21233
+ const cy = sumY / nodeCount;
21234
+ const cz = sumZ / nodeCount;
21235
+ let maxDist = 120;
21236
+ activeNodeIds.forEach((id) => {
21237
+ const p = posMap.get(String(id));
21238
+ if (p) {
21239
+ const d = Math.hypot(p.x - cx, p.y - cy, p.z - cz);
21240
+ if (d > maxDist) maxDist = d;
21241
+ }
21242
+ });
21243
+ networkMaxRadius = maxDist;
21244
+ }
21021
21245
  }
21022
21246
  if (nodeCount > 0) {
21023
21247
  const centerX = sumX / nodeCount;
21024
21248
  const centerY = sumY / nodeCount;
21025
- const targetFocus = new THREE3.Vector3(centerX, centerY, 0);
21249
+ const centerZ = type === "Network" ? sumZ / nodeCount : 0;
21250
+ const targetFocus = new THREE3.Vector3(centerX, centerY, centerZ);
21026
21251
  const targetTween = new Tween2(controls.target).to({ x: targetFocus.x, y: targetFocus.y, z: targetFocus.z }, 1500).easing(Easing2.Cubic.InOut);
21027
21252
  tweenGroup.add(targetTween);
21028
21253
  targetTween.start();
21029
21254
  let zoomZ = 140;
21255
+ let camOffsetY = 0;
21030
21256
  if (type === "Column") {
21031
21257
  const numRows = Math.ceil(nodeCount / (columns || 4));
21032
21258
  zoomZ = Math.max(140, numRows * 50);
21033
21259
  } else if (type === "Circle" || type === "Star") {
21034
21260
  const radius = Math.max(80, nodeCount * 8);
21035
21261
  zoomZ = radius * 2.5;
21262
+ } else if (type === "Network") {
21263
+ zoomZ = targetFocus.z + Math.max(220, networkMaxRadius * 1.85);
21264
+ camOffsetY = networkMaxRadius * 0.12;
21036
21265
  }
21037
- const camTween = new Tween2(camera.position).to({ x: targetFocus.x, y: targetFocus.y, z: zoomZ }, 1500).easing(Easing2.Cubic.InOut);
21266
+ const camTween = new Tween2(camera.position).to(
21267
+ {
21268
+ x: targetFocus.x,
21269
+ y: targetFocus.y + camOffsetY,
21270
+ z: zoomZ
21271
+ },
21272
+ 1500
21273
+ ).easing(Easing2.Cubic.InOut);
21038
21274
  tweenGroup.add(camTween);
21039
21275
  camTween.start();
21040
21276
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lv-x-software-house/x_view",
3
- "version": "1.3.9",
3
+ "version": "1.4.1",
4
4
  "description": "Pacote privado contendo os componentes e lógica de renderização 3D do X View.",
5
5
  "author": "iv.x - Engenharia de Software - ivxsoftwarehouse@gmail.com",
6
6
  "license": "UNLICENSED",