diff --git a/.idea/libraries-with-intellij-classes.xml b/.idea/libraries-with-intellij-classes.xml deleted file mode 100644 index 9fa31567f1..0000000000 --- a/.idea/libraries-with-intellij-classes.xml +++ /dev/null @@ -1,65 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index fcd78fd57f..2d7c04ef94 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -8,7 +8,7 @@ - + \ No newline at end of file diff --git a/flutter-idea/src/io/flutter/editor/FilteredIndentsHighlightingPass.java b/flutter-idea/src/io/flutter/editor/FilteredIndentsHighlightingPass.java deleted file mode 100644 index 018f9a17f8..0000000000 --- a/flutter-idea/src/io/flutter/editor/FilteredIndentsHighlightingPass.java +++ /dev/null @@ -1,568 +0,0 @@ -// Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. - -/* - * @author max - */ -package io.flutter.editor; - -import com.intellij.codeHighlighting.TextEditorHighlightingPass; -import com.intellij.codeInsight.highlighting.BraceMatcher; -import com.intellij.codeInsight.highlighting.BraceMatchingUtil; -import com.intellij.lang.Language; -import com.intellij.lang.LanguageParserDefinitions; -import com.intellij.lang.ParserDefinition; -import com.intellij.openapi.editor.*; -import com.intellij.openapi.editor.colors.EditorColors; -import com.intellij.openapi.editor.colors.EditorColorsScheme; -import com.intellij.openapi.editor.ex.DocumentEx; -import com.intellij.openapi.editor.ex.EditorEx; -import com.intellij.openapi.editor.ex.util.EditorUtil; -import com.intellij.openapi.editor.highlighter.HighlighterIterator; -import com.intellij.openapi.editor.markup.CustomHighlighterRenderer; -import com.intellij.openapi.editor.markup.HighlighterTargetArea; -import com.intellij.openapi.editor.markup.MarkupModel; -import com.intellij.openapi.editor.markup.RangeHighlighter; -import com.intellij.openapi.fileTypes.FileType; -import com.intellij.openapi.progress.ProgressIndicator; -import com.intellij.openapi.progress.ProgressManager; -import com.intellij.openapi.project.DumbAware; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.Segment; -import com.intellij.openapi.util.TextRange; -import com.intellij.psi.PsiFile; -import com.intellij.psi.tree.IElementType; -import com.intellij.psi.tree.TokenSet; -import com.intellij.psi.util.PsiUtilBase; -import com.intellij.psi.util.PsiUtilCore; -import com.intellij.ui.paint.LinePainter2D; -import com.intellij.util.DocumentUtil; -import com.intellij.util.containers.IntStack; -import com.intellij.util.text.CharArrayUtil; -import org.jetbrains.annotations.NotNull; - -import java.awt.*; -import java.util.List; -import java.util.*; - -import static java.lang.Math.min; - -/** - * This is an FilteredIndentsHighlightingPass class forked from com.intellij.codeInsight.daemon.impl.FilteredIndentsHighlightingPass - * that supports filtering out indent guides that conflict with widget indent - * guides as determined by calling WidgetIndentsHighlightingPass.isIndentGuideHidden. - */ -@SuppressWarnings("ALL") -public class FilteredIndentsHighlightingPass extends TextEditorHighlightingPass implements DumbAware { - private static final Key> INDENT_HIGHLIGHTERS_IN_EDITOR_KEY = Key.create("INDENT_HIGHLIGHTERS_IN_EDITOR_KEY"); - private static final Key LAST_TIME_INDENTS_BUILT = Key.create("LAST_TIME_INDENTS_BUILT"); - - private final EditorEx myEditor; - private final PsiFile myFile; - - private volatile List myRanges = Collections.emptyList(); - private volatile List myDescriptors = Collections.emptyList(); - - private static final CustomHighlighterRenderer RENDERER = (editor, highlighter, g) -> { - int startOffset = highlighter.getStartOffset(); - final Document doc = highlighter.getDocument(); - final int textLength = doc.getTextLength(); - if (startOffset >= textLength) return; - - final int endOffset = min(highlighter.getEndOffset(), textLength); - final int endLine = doc.getLineNumber(endOffset); - - int off; - int startLine = doc.getLineNumber(startOffset); - IndentGuideDescriptor descriptor = editor.getIndentsModel().getDescriptor(startLine, endLine); - - final CharSequence chars = doc.getCharsSequence(); - do { - int start = doc.getLineStartOffset(startLine); - int end = doc.getLineEndOffset(startLine); - off = CharArrayUtil.shiftForward(chars, start, end, " \t"); - startLine--; - } - while (startLine > 1 && off < doc.getTextLength() && chars.charAt(off) == '\n'); - - final VisualPosition startPosition = editor.offsetToVisualPosition(off); - int indentColumn = startPosition.column; - - // It's considered that indent guide can cross not only white space but comments, javadoc etc. Hence, there is a possible - // case that the first indent guide line is, say, single-line comment where comment symbols ('//') are located at the first - // visual column. We need to calculate correct indent guide column then. - int lineShift = 1; - if (indentColumn <= 0 && descriptor != null) { - indentColumn = descriptor.indentLevel; - lineShift = 0; - } - if (indentColumn <= 0) return; - - final FoldingModel foldingModel = editor.getFoldingModel(); - if (foldingModel.isOffsetCollapsed(off)) return; - - final FoldRegion headerRegion = foldingModel.getCollapsedRegionAtOffset(doc.getLineEndOffset(doc.getLineNumber(off))); - final FoldRegion tailRegion = foldingModel.getCollapsedRegionAtOffset(doc.getLineStartOffset(doc.getLineNumber(endOffset))); - - if (tailRegion != null && tailRegion == headerRegion) return; - - final boolean selected; - final IndentGuideDescriptor guide = editor.getIndentsModel().getCaretIndentGuide(); - if (guide != null) { - final CaretModel caretModel = editor.getCaretModel(); - final int caretOffset = caretModel.getOffset(); - selected = - caretOffset >= off && caretOffset < endOffset && caretModel.getLogicalPosition().column == indentColumn; - } - else { - selected = false; - } - - Point start = editor.visualPositionToXY(new VisualPosition(startPosition.line + lineShift, indentColumn)); - final VisualPosition endPosition = editor.offsetToVisualPosition(endOffset); - Point end = editor.visualPositionToXY(new VisualPosition(endPosition.line, endPosition.column)); - int maxY = end.y; - if (endPosition.line == editor.offsetToVisualPosition(doc.getTextLength()).line) { - maxY += editor.getLineHeight(); - } - - Rectangle clip = g.getClipBounds(); - if (clip != null) { - if (clip.y >= maxY || clip.y + clip.height <= start.y) { - return; - } - maxY = min(maxY, clip.y + clip.height); - } - - if (WidgetIndentsHighlightingPass.isIndentGuideHidden(editor, new LineRange(startLine, endPosition.line))) { - // Avoid rendering this guide as it overlaps with the Widget indent guides. - return; - } - - final EditorColorsScheme scheme = editor.getColorsScheme(); - g.setColor(scheme.getColor(selected ? EditorColors.SELECTED_INDENT_GUIDE_COLOR : EditorColors.INDENT_GUIDE_COLOR)); - - // There is a possible case that indent line intersects soft wrap-introduced text. Example: - // this is a long line - // that| is soft-wrapped - // | - // | <- vertical indent - // - // Also it's possible that no additional intersections are added because of soft wrap: - // this is a long line - // | that is soft-wrapped - // | - // | <- vertical indent - // We want to use the following approach then: - // 1. Show only active indent if it crosses soft wrap-introduced text; - // 2. Show indent as is if it doesn't intersect with soft wrap-introduced text; - if (selected) { - LinePainter2D.paint((Graphics2D)g, start.x + WidgetIndentsHighlightingPass.INDENT_GUIDE_DELTA, start.y, start.x + WidgetIndentsHighlightingPass.INDENT_GUIDE_DELTA, maxY - 1); - } - else { - int y = start.y; - int newY = start.y; - SoftWrapModel softWrapModel = editor.getSoftWrapModel(); - int lineHeight = editor.getLineHeight(); - for (int i = Math.max(0, startLine + lineShift); i < endLine && newY < maxY; i++) { - List softWraps = softWrapModel.getSoftWrapsForLine(i); - int logicalLineHeight = softWraps.size() * lineHeight; - if (i > startLine + lineShift) { - logicalLineHeight += lineHeight; // We assume that initial 'y' value points just below the target line. - } - if (!softWraps.isEmpty() && softWraps.get(0).getIndentInColumns() < indentColumn) { - if (y < newY || i > startLine + lineShift) { // There is a possible case that soft wrap is located on indent start line. - LinePainter2D.paint((Graphics2D)g, start.x + WidgetIndentsHighlightingPass.INDENT_GUIDE_DELTA, y, start.x + WidgetIndentsHighlightingPass.INDENT_GUIDE_DELTA, newY + lineHeight - 1); - } - newY += logicalLineHeight; - y = newY; - } - else { - newY += logicalLineHeight; - } - - FoldRegion foldRegion = foldingModel.getCollapsedRegionAtOffset(doc.getLineEndOffset(i)); - if (foldRegion != null && foldRegion.getEndOffset() < doc.getTextLength()) { - i = doc.getLineNumber(foldRegion.getEndOffset()); - } - } - - if (y < maxY) { - LinePainter2D.paint((Graphics2D)g, start.x + WidgetIndentsHighlightingPass.INDENT_GUIDE_DELTA, y, start.x + WidgetIndentsHighlightingPass.INDENT_GUIDE_DELTA, maxY - 1); - } - } - }; - - // TODO(jacobr): some of this logic to compute what portion of the guide to - // render is duplicated from RENDERER. - static io.flutter.editor.LineRange getGuideLineRange(Editor editor, RangeHighlighter highlighter) { - final int startOffset = highlighter.getStartOffset(); - final Document doc = highlighter.getDocument(); - final int textLength = doc.getTextLength(); - if (startOffset >= textLength || !highlighter.isValid()) return null; - - final int endOffset = min(highlighter.getEndOffset(), textLength); - final int endLine = doc.getLineNumber(endOffset); - - int off; - int startLine = doc.getLineNumber(startOffset); - final IndentGuideDescriptor descriptor = editor.getIndentsModel().getDescriptor(startLine, endLine); - - final CharSequence chars = doc.getCharsSequence(); - do { - final int start = doc.getLineStartOffset(startLine); - final int end = doc.getLineEndOffset(startLine); - off = CharArrayUtil.shiftForward(chars, start, end, " \t"); - startLine--; - } - while (startLine > 1 && off < textLength && chars.charAt(off) == '\n'); - - final VisualPosition startPosition = editor.offsetToVisualPosition(off); - int indentColumn = startPosition.column; - - // It's considered that indent guide can cross not only white space but comments, javadoc etc. Hence, there is a possible - // case that the first indent guide line is, say, single-line comment where comment symbols ('//') are located at the first - // visual column. We need to calculate correct indent guide column then. - int lineShift = 1; - if (indentColumn <= 0 && descriptor != null) { - indentColumn = descriptor.indentLevel; - lineShift = 0; - } - if (indentColumn <= 0) return null; - - final FoldingModel foldingModel = editor.getFoldingModel(); - if (foldingModel.isOffsetCollapsed(off)) return null; - - final FoldRegion headerRegion = foldingModel.getCollapsedRegionAtOffset(doc.getLineEndOffset(doc.getLineNumber(off))); - final FoldRegion tailRegion = foldingModel.getCollapsedRegionAtOffset(doc.getLineStartOffset(doc.getLineNumber(endOffset))); - - if (tailRegion != null && tailRegion == headerRegion) return null; - - final VisualPosition endPosition = editor.offsetToVisualPosition(endOffset); - return new io.flutter.editor.LineRange(startLine, endPosition.line); - } - - FilteredIndentsHighlightingPass(@NotNull Project project, @NotNull Editor editor, @NotNull PsiFile file) { - super(project, editor.getDocument(), false); - myEditor = (EditorEx)editor; - myFile = file; - } - - public static void onWidgetIndentsChanged(EditorEx editor, WidgetIndentHitTester oldHitTester, WidgetIndentHitTester newHitTester) { - final List highlighters = editor.getUserData(INDENT_HIGHLIGHTERS_IN_EDITOR_KEY); - if (highlighters != null) { - final Document doc = editor.getDocument(); - final int textLength = doc.getTextLength(); - for (RangeHighlighter highlighter : highlighters) { - if (!highlighter.isValid()) { - continue; - } - final LineRange range = getGuideLineRange(editor, highlighter); - if (range != null) { - final boolean before = WidgetIndentsHighlightingPass.isIndentGuideHidden(oldHitTester, range); - final boolean after = WidgetIndentsHighlightingPass.isIndentGuideHidden(newHitTester, range); - if (before != after) { - int safeStart = min(highlighter.getStartOffset(), textLength); - int safeEnd = min(highlighter.getEndOffset(), textLength); - if (safeEnd > safeStart) { - editor.repaint(safeStart, safeEnd); - } - } - } - } - } - } - - @Override - public void doCollectInformation(@NotNull ProgressIndicator progress) { - assert myDocument != null; - final Long stamp = myEditor.getUserData(LAST_TIME_INDENTS_BUILT); - if (stamp != null && stamp.longValue() == nowStamp()) return; - - myDescriptors = buildDescriptors(); - - final ArrayList ranges = new ArrayList<>(); - for (IndentGuideDescriptor descriptor : myDescriptors) { - ProgressManager.checkCanceled(); - final int endOffset = - descriptor.endLine < myDocument.getLineCount() ? myDocument.getLineStartOffset(descriptor.endLine) : myDocument.getTextLength(); - ranges.add(new TextRange(myDocument.getLineStartOffset(descriptor.startLine), endOffset)); - } - - Collections.sort(ranges, Segment.BY_START_OFFSET_THEN_END_OFFSET); - myRanges = ranges; - } - - private long nowStamp() { - // If regular indent guides are being shown then the user has disabled - // the custom WidgetIndentGuides and we should skip this pass in favor - // of the regular indent guides instead of our fork. - if (myEditor.getSettings().isIndentGuidesShown()) return -1; - assert myDocument != null; - return myDocument.getModificationStamp(); - } - - public static void cleanupHighlighters(Editor editor) { - final List highlighters = editor.getUserData(INDENT_HIGHLIGHTERS_IN_EDITOR_KEY); - if (highlighters == null) return; - - for (RangeHighlighter highlighter : highlighters) { - highlighter.dispose(); - } - editor.putUserData(INDENT_HIGHLIGHTERS_IN_EDITOR_KEY, null); - editor.putUserData(LAST_TIME_INDENTS_BUILT, null); - } - - @Override - public void doApplyInformationToEditor() { - final Long stamp = myEditor.getUserData(LAST_TIME_INDENTS_BUILT); - if (stamp != null && stamp.longValue() == nowStamp()) return; - - List oldHighlighters = myEditor.getUserData(INDENT_HIGHLIGHTERS_IN_EDITOR_KEY); - final List newHighlighters = new ArrayList<>(); - final MarkupModel mm = myEditor.getMarkupModel(); - - int curRange = 0; - - if (oldHighlighters != null) { - // after document change some range highlighters could have become invalid, or the order could have been broken - oldHighlighters.sort(Comparator.comparing((RangeHighlighter h) -> !h.isValid()) - .thenComparing(Segment.BY_START_OFFSET_THEN_END_OFFSET)); - int curHighlight = 0; - while (curRange < myRanges.size() && curHighlight < oldHighlighters.size()) { - TextRange range = myRanges.get(curRange); - RangeHighlighter highlighter = oldHighlighters.get(curHighlight); - if (!highlighter.isValid()) break; - - int cmp = compare(range, highlighter); - if (cmp < 0) { - newHighlighters.add(createHighlighter(mm, range)); - curRange++; - } - else if (cmp > 0) { - highlighter.dispose(); - curHighlight++; - } - else { - newHighlighters.add(highlighter); - curHighlight++; - curRange++; - } - } - - for (; curHighlight < oldHighlighters.size(); curHighlight++) { - RangeHighlighter highlighter = oldHighlighters.get(curHighlight); - if (!highlighter.isValid()) break; - highlighter.dispose(); - } - } - - final int startRangeIndex = curRange; - assert myDocument != null; - DocumentUtil.executeInBulk(myDocument, myRanges.size() > 10000, () -> { - for (int i = startRangeIndex; i < myRanges.size(); i++) { - newHighlighters.add(createHighlighter(mm, myRanges.get(i))); - } - }); - - - myEditor.putUserData(INDENT_HIGHLIGHTERS_IN_EDITOR_KEY, newHighlighters); - myEditor.putUserData(LAST_TIME_INDENTS_BUILT, nowStamp()); - myEditor.getIndentsModel().assumeIndents(myDescriptors); - } - - private List buildDescriptors() { - // If regular indent guides are being shown then the user has disabled - // the custom WidgetIndentGuides and we should skip this pass in favor - // of the regular indent guides instead of our fork. - if (myEditor.getSettings().isIndentGuidesShown()) return Collections.emptyList(); - - IndentsCalculator calculator = new IndentsCalculator(); - calculator.calculate(); - int[] lineIndents = calculator.lineIndents; - - IntStack lines = new IntStack(); - IntStack indents = new IntStack(); - - lines.push(0); - indents.push(0); - assert myDocument != null; - List descriptors = new ArrayList<>(); - for (int line = 1; line < lineIndents.length; line++) { - ProgressManager.checkCanceled(); - int curIndent = Math.abs(lineIndents[line]); - - while (!indents.empty() && curIndent <= indents.peek()) { - ProgressManager.checkCanceled(); - final int level = indents.pop(); - int startLine = lines.pop(); - if (level > 0) { - for (int i = startLine; i < line; i++) { - if (level != Math.abs(lineIndents[i])) { - descriptors.add(createDescriptor(level, startLine, line, lineIndents)); - break; - } - } - } - } - - int prevLine = line - 1; - int prevIndent = Math.abs(lineIndents[prevLine]); - - if (curIndent - prevIndent > 1) { - lines.push(prevLine); - indents.push(prevIndent); - } - } - - while (!indents.empty()) { - ProgressManager.checkCanceled(); - final int level = indents.pop(); - int startLine = lines.pop(); - if (level > 0) { - descriptors.add(createDescriptor(level, startLine, myDocument.getLineCount(), lineIndents)); - } - } - return descriptors; - } - - private IndentGuideDescriptor createDescriptor(int level, int startLine, int endLine, int[] lineIndents) { - while (startLine > 0 && lineIndents[startLine] < 0) startLine--; - int codeConstructStartLine = findCodeConstructStartLine(startLine); - return new IndentGuideDescriptor(level, codeConstructStartLine, startLine, endLine); - } - - private int findCodeConstructStartLine(int startLine) { - DocumentEx document = myEditor.getDocument(); - CharSequence text = document.getImmutableCharSequence(); - int lineStartOffset = document.getLineStartOffset(startLine); - int firstNonWsOffset = CharArrayUtil.shiftForward(text, lineStartOffset, " \t"); - FileType type = PsiUtilBase.getPsiFileAtOffset(myFile, firstNonWsOffset).getFileType(); - Language language = PsiUtilCore.getLanguageAtOffset(myFile, firstNonWsOffset); - BraceMatcher braceMatcher = BraceMatchingUtil.getBraceMatcher(type, language); - HighlighterIterator iterator = myEditor.getHighlighter().createIterator(firstNonWsOffset); - if (braceMatcher.isLBraceToken(iterator, text, type)) { - int codeConstructStart = braceMatcher.getCodeConstructStart(myFile, firstNonWsOffset); - return document.getLineNumber(codeConstructStart); - } - else { - return startLine; - } - } - - @NotNull - private static RangeHighlighter createHighlighter(MarkupModel mm, TextRange range) { - final RangeHighlighter highlighter = - mm.addRangeHighlighter(range.getStartOffset(), range.getEndOffset(), 0, null, HighlighterTargetArea.EXACT_RANGE); - highlighter.setCustomRenderer(RENDERER); - return highlighter; - } - - private static int compare(@NotNull TextRange r, @NotNull RangeHighlighter h) { - int answer = r.getStartOffset() - h.getStartOffset(); - return answer != 0 ? answer : r.getEndOffset() - h.getEndOffset(); - } - - private class IndentsCalculator { - @NotNull final Map myComments = new HashMap<>(); - @NotNull final int[] lineIndents; // negative value means the line is empty (or contains a comment) and indent - // (denoted by absolute value) was deduced from enclosing non-empty lines - @NotNull final CharSequence myChars; - - IndentsCalculator() { - assert myDocument != null; - lineIndents = new int[myDocument.getLineCount()]; - myChars = myDocument.getCharsSequence(); - } - - /** - * Calculates line indents for the {@link #myDocument target document}. - */ - void calculate() { - assert myDocument != null; - final FileType fileType = myFile.getFileType(); - int tabSize = EditorUtil.getTabSize(myEditor); - - for (int line = 0; line < lineIndents.length; line++) { - ProgressManager.checkCanceled(); - int lineStart = myDocument.getLineStartOffset(line); - int lineEnd = myDocument.getLineEndOffset(line); - int offset = lineStart; - int column = 0; - outer: - while (offset < lineEnd) { - switch (myChars.charAt(offset)) { - case ' ': - column++; - break; - case '\t': - column = (column / tabSize + 1) * tabSize; - break; - default: - break outer; - } - offset++; - } - // treating commented lines in the same way as empty lines - // Blank line marker - lineIndents[line] = offset == lineEnd || isComment(offset) ? -1 : column; - } - - int topIndent = 0; - for (int line = 0; line < lineIndents.length; line++) { - ProgressManager.checkCanceled(); - if (lineIndents[line] >= 0) { - topIndent = lineIndents[line]; - } - else { - int startLine = line; - while (line < lineIndents.length && lineIndents[line] < 0) { - //noinspection AssignmentToForLoopParameter - line++; - } - - int bottomIndent = line < lineIndents.length ? lineIndents[line] : topIndent; - - int indent = min(topIndent, bottomIndent); - if (bottomIndent < topIndent) { - int lineStart = myDocument.getLineStartOffset(line); - int lineEnd = myDocument.getLineEndOffset(line); - int nonWhitespaceOffset = CharArrayUtil.shiftForward(myChars, lineStart, lineEnd, " \t"); - HighlighterIterator iterator = myEditor.getHighlighter().createIterator(nonWhitespaceOffset); - if (BraceMatchingUtil.isRBraceToken(iterator, myChars, fileType)) { - indent = topIndent; - } - } - - for (int blankLine = startLine; blankLine < line; blankLine++) { - assert lineIndents[blankLine] == -1; - lineIndents[blankLine] = -min(topIndent, indent); - } - - //noinspection AssignmentToForLoopParameter - line--; // will be incremented back at the end of the loop; - } - } - } - - private boolean isComment(int offset) { - final HighlighterIterator it = myEditor.getHighlighter().createIterator(offset); - IElementType tokenType = it.getTokenType(); - Language language = tokenType.getLanguage(); - TokenSet comments = myComments.get(language); - if (comments == null) { - ParserDefinition definition = LanguageParserDefinitions.INSTANCE.forLanguage(language); - if (definition != null) { - comments = definition.getCommentTokens(); - } - if (comments == null) { - return false; - } - else { - myComments.put(language, comments); - } - } - return comments.contains(tokenType); - } - } -} diff --git a/flutter-idea/src/io/flutter/editor/InlinePreviewViewController.java b/flutter-idea/src/io/flutter/editor/InlinePreviewViewController.java deleted file mode 100644 index 44e9426b4a..0000000000 --- a/flutter-idea/src/io/flutter/editor/InlinePreviewViewController.java +++ /dev/null @@ -1,403 +0,0 @@ -/* - * Copyright 2019 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -package io.flutter.editor; - -import com.intellij.openapi.Disposable; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.VisualPosition; -import com.intellij.openapi.editor.ex.EditorEx; -import com.intellij.openapi.editor.markup.CustomHighlighterRenderer; -import com.intellij.openapi.editor.markup.RangeHighlighter; -import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.ui.JBColor; -import io.flutter.inspector.DiagnosticsNode; -import io.flutter.inspector.InspectorService; -import io.flutter.inspector.Screenshot; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import javax.swing.*; -import java.awt.*; -import java.awt.geom.Rectangle2D; - -/** - * PreviewViewController that renders inline in a code editor. - */ -public class InlinePreviewViewController extends PreviewViewControllerBase implements CustomHighlighterRenderer { - - protected float previewWidthScale = 0.7f; - - public InlinePreviewViewController(InlineWidgetViewModelData data, boolean drawBackground, Disposable disposable) { - super(data, drawBackground, disposable); - data.context.editorPositionService.addListener(getEditor(), new EditorPositionService.Listener() { - @Override - public void updateVisibleArea(Rectangle newRectangle) { - InlinePreviewViewController.this.updateVisibleArea(newRectangle); - } - - @Override - public void onVisibleChanged() { - InlinePreviewViewController.this.onVisibleChanged(); - } - }, - this); - } - - InlineWidgetViewModelData getData() { - return (InlineWidgetViewModelData)data; - } - - protected EditorEx getEditor() { - return getData().editor; - } - - public Point offsetToPoint(int offset) { - return getEditor().visualPositionToXY(getEditor().offsetToVisualPosition(offset)); - } - - @Override - public void forceRender() { - if (!visible) return; - - getEditor().getComponent().repaint(); - // TODO(jacobr): consider forcing a repaint of just a fraction of the - // editor window instead. - // For example, if the marker corresponded to just the lines in the text - // editor that the preview is rendereded on, we could do the following: - - // final TextRange marker = data.getMarker(); - // if (marker == null) return; - // - // data.editor.repaint(marker.getStartOffset(), marker.getEndOffset()); - } - - InspectorService.Location getLocation() { - final WidgetIndentGuideDescriptor descriptor = getDescriptor(); - if (descriptor == null || descriptor.widget == null) return null; - - return InspectorService.Location.outlineToLocation(getEditor(), descriptor.outlineNode); - } - - public WidgetIndentGuideDescriptor getDescriptor() { - return getData().descriptor; - } - - @Override - public void computeScreenshotBounds() { - final Rectangle previousScreenshotBounds = screenshotBounds; - screenshotBounds = null; - maxHeight = Math.round(PREVIEW_MAX_HEIGHT * 0.16f); - final WidgetIndentGuideDescriptor descriptor = getDescriptor(); - - final int lineHeight = getEditor() != null ? getEditor().getLineHeight() : defaultLineHeight; - extraHeight = descriptor != null && screenshot != null ? lineHeight : 0; - - final Rectangle visibleRect = this.visibleRect; - if (visibleRect == null) { - return; - } - - if (descriptor == null) { - // Special case to float in the bottom right corner. - final Screenshot latestScreenshot = getScreenshotNow(); - int previewWidth = Math.round(PREVIEW_MAX_WIDTH * previewWidthScale); - int previewHeight = Math.round((PREVIEW_MAX_HEIGHT * 0.16f) * previewWidthScale); - if (latestScreenshot != null) { - previewWidth = (int)(latestScreenshot.image.getWidth() / getDPI()); - previewHeight = (int)(latestScreenshot.image.getHeight() / getDPI()); - } - final int previewStartX = Math.max(0, visibleRect.x + visibleRect.width - previewWidth - PREVIEW_PADDING_X); - previewHeight = Math.min(previewHeight, visibleRect.height); - - maxHeight = visibleRect.height; - final int previewStartY = Math.max(visibleRect.y, visibleRect.y + visibleRect.height - previewHeight); - screenshotBounds = new Rectangle(previewStartX, previewStartY, previewWidth, previewHeight); - return; - } - - final TextRange marker = getData().getMarker(); - if (marker == null) return; - final int startOffset = marker.getStartOffset(); - final Document doc = getData().document; - final int textLength = doc.getTextLength(); - if (startOffset >= textLength) return; - - final int endOffset = Math.min(marker.getEndOffset(), textLength); - - int off; - final int startLine = doc.getLineNumber(startOffset); - - final int widgetOffset = getDescriptor().widget.getGuideOffset(); - final int widgetLine = doc.getLineNumber(widgetOffset); - final int lineEndOffset = doc.getLineEndOffset(widgetLine); - - // Request a thumbnail and render it in the space available. - VisualPosition visualPosition = getEditor().offsetToVisualPosition(lineEndOffset); // e - visualPosition = new VisualPosition(Math.max(visualPosition.line, 0), 81); - final Point start = getEditor().visualPositionToXY(visualPosition); - final Point endz = offsetToPoint(endOffset); - final int endY = endz.y; - final int visibleEndX = visibleRect.x + visibleRect.width; - final int width = Math.max(0, visibleEndX - 20 - start.x); - final int height = Math.max(0, endY - start.y); - int previewStartY = start.y; - int previewStartX = start.x; - final int visibleStart = visibleRect.y; - final int visibleEnd = (int)visibleRect.getMaxY(); - - // Add extra room for the descriptor. - final Screenshot latestScreenshot = getScreenshotNow(); - int previewWidth = PREVIEW_MAX_WIDTH; - int previewHeight = PREVIEW_MAX_HEIGHT / 6; - if (latestScreenshot != null) { - previewWidth = (int)(latestScreenshot.image.getWidth() / getDPI()); - previewHeight = (int)(latestScreenshot.image.getHeight() / getDPI()); - } - previewStartX = Math.max(previewStartX, visibleEndX - previewWidth - PREVIEW_PADDING_X); - previewHeight += extraHeight; - previewHeight = Math.min(previewHeight, height); - - maxHeight = endz.y - start.y; - if (popupActive()) { - // Keep the bounds sticky maintining the same lastScreenshotBoundsWindow. - screenshotBounds = new Rectangle(lastScreenshotBoundsWindow); - screenshotBounds.translate(visibleRect.x, visibleRect.y); - } - else { - boolean lockUpdate = false; - if (isVisiblityLocked()) { - // TODO(jacobr): also need to keep sticky if there is some minor scrolling - if (previousScreenshotBounds != null && visibleRect.contains(previousScreenshotBounds)) { - screenshotBounds = new Rectangle(previousScreenshotBounds); - - // Fixup if the screenshot changed - if (previewWidth != screenshotBounds.width) { - screenshotBounds.x += screenshotBounds.width - previewWidth; - screenshotBounds.width = previewWidth; - } - screenshotBounds.height = previewHeight; - - // TODO(jacobr): refactor this code so there is less duplication. - lastScreenshotBoundsWindow = new Rectangle(screenshotBounds); - lastScreenshotBoundsWindow.translate(-visibleRect.x, -visibleRect.y); - lockUpdate = true; - } - } - - if (!lockUpdate) { - lastLockedRectangle = null; - if (start.y <= visibleEnd && endY >= visibleStart) { - if (visibleStart > previewStartY) { - previewStartY = Math.max(previewStartY, visibleStart); - previewStartY = Math.min(previewStartY, Math.min(endY - previewHeight, visibleEnd - previewHeight)); - } - screenshotBounds = new Rectangle(previewStartX, previewStartY, previewWidth, previewHeight); - lastScreenshotBoundsWindow = new Rectangle(screenshotBounds); - lastScreenshotBoundsWindow.translate(-visibleRect.x, -visibleRect.y); - } - } - } - - if (popupActive()) { - lastLockedRectangle = new Rectangle(visibleRect); - } - } - - @Override - protected Dimension getPreviewSize() { - int previewWidth; - int previewHeight; - previewWidth = PREVIEW_MAX_WIDTH; - previewHeight = PREVIEW_MAX_HEIGHT; - - if (getDescriptor() == null) { - previewWidth = Math.round(previewWidth * previewWidthScale); - previewHeight = Math.round(previewHeight * previewWidthScale); - } - return new Dimension(previewWidth, previewHeight); - } - - private void updateVisibleArea(Rectangle newRectangle) { - visibleRect = newRectangle; - if (getDescriptor() == null || getData().getMarker() == null) { - if (!visible) { - visible = true; - onVisibleChanged(); - } - return; - } - final TextRange marker = getData().getMarker(); - if (marker == null) return; - - final Point start = offsetToPoint(marker.getStartOffset()); - final Point end = offsetToPoint(marker.getEndOffset()); - final boolean nowVisible = newRectangle == null || newRectangle.y <= end.y && newRectangle.y + newRectangle.height >= start.y || - updateVisiblityLocked(newRectangle); - if (visible != nowVisible) { - visible = nowVisible; - onVisibleChanged(); - } - } - - @Override - public void dispose() { - } - - @Override - protected Component getComponent() { - return getEditor().getComponent(); - } - - @Override - protected VirtualFile getVirtualFile() { - return getEditor().getVirtualFile(); - } - - @Override - protected Document getDocument() { - return getEditor().getDocument(); - } - - @Override - protected void showPopup(Point location, DiagnosticsNode node) { - location = - SwingUtilities.convertPoint( - getEditor().getContentComponent(), - location, - getEditor().getComponent() - ); - popup = PropertyEditorPanel - .showPopup(data.context.inspectorGroupManagerService, getEditor(), node, node.getCreationLocation().getLocation(), - data.context.flutterDartAnalysisService, location); - } - - @Override - TextRange getActiveRange() { - return getData().getMarker(); - } - - @Override - void setCustomCursor(@Nullable Cursor cursor) { - if (getEditor() == null) { - // TODO(jacobr): customize the cursor when there is not an associated editor. - return; - } - getEditor().setCustomCursor(this, cursor); - } - - // Determine zOrder of overlapping previews. - // Ideally we should work harder to prevent overlapping. - public int getPriority() { - int priority = 0; - if (popupActive()) { - priority += 20; - } - if (isVisiblityLocked()) { - priority += 10; - } - - if (isSelected) { - priority += 5; - } - - if (getDescriptor() != null) { - priority += 1; - } - - if (screenshot == null && (elements == null || elements.isEmpty())) { - priority -= 5; - if (getDescriptor() != null) { - priority -= 100; - } - } - else { - if (hasCurrentHits() || _mouseInScreenshot) { - priority += 10; - } - } - if (_mouseInScreenshot) { - priority += 20; - } - return priority; - } - - @Override - public void paint(@NotNull Editor editor, @NotNull RangeHighlighter highlighter, @NotNull Graphics g) { - if (editor != getEditor()) { - // Don't want to render on the wrong editor. This shouldn't happen. - return; - } - if (getEditor().isPurePaintingMode()) { - // Don't show previews in pure mode. - return; - } - if (!highlighter.isValid()) { - return; - } - if (getDescriptor() != null && !getDescriptor().widget.isValid()) { - return; - } - final int lineHeight = editor.getLineHeight(); - paint(g, lineHeight); - } - - public void paint(@NotNull Graphics g, int lineHeight) { - final Graphics2D g2d = (Graphics2D)g.create(); - - final Screenshot latestScreenshot = getScreenshotNow(); - if (latestScreenshot != null) { - final int imageWidth = (int)(latestScreenshot.image.getWidth() * getDPI()); - final int imageHeight = (int)(latestScreenshot.image.getHeight() * getDPI()); - if (extraHeight > 0) { - if (drawBackground) { - g2d.setColor(JBColor.LIGHT_GRAY); - g2d.fillRect(screenshotBounds.x, screenshotBounds.y, Math.min(screenshotBounds.width, imageWidth), - Math.min(screenshotBounds.height, extraHeight)); - } - final WidgetIndentGuideDescriptor descriptor = getDescriptor(); - if (descriptor != null) { - final int line = descriptor.widget.getLine() + 1; - final int column = descriptor.widget.getColumn() + 1; - final int numActive = elements != null ? elements.size() : 0; - String message = descriptor.outlineNode.getClassName() + " ";//+ " Widget "; - if (numActive == 0) { - message += "(inactive)"; - } - else if (numActive > 1) { - message += "(" + (activeIndex + 1) + " of " + numActive + ")"; - } - if (numActive > 0 && screenshot != null && screenshot.transformedRect != null) { - final Rectangle2D bounds = screenshot.transformedRect.getRectangle(); - final long w = Math.round(bounds.getWidth()); - final long h = Math.round(bounds.getHeight()); - message += " " + w + "x" + h; - } - - g2d.setColor(JBColor.BLACK); - drawMultilineString(g2d, - message, - screenshotBounds.x + 4, - screenshotBounds.y + lineHeight - 6, lineHeight); - } - } - } - super.paint(g, lineHeight); - } - - @Override - String getNoScreenshotMessage() { - final WidgetIndentGuideDescriptor descriptor = getDescriptor(); - if (descriptor == null) { - return super.getNoScreenshotMessage(); - } - final int line = descriptor.widget.getLine() + 1; - final int column = descriptor.widget.getColumn() + 1; - return descriptor.outlineNode.getClassName() + " Widget " + line + ":" + column + "\n" + - "not currently active"; - } -} diff --git a/flutter-idea/src/io/flutter/editor/InlineWidgetViewModelData.java b/flutter-idea/src/io/flutter/editor/InlineWidgetViewModelData.java deleted file mode 100644 index 5d7164bf25..0000000000 --- a/flutter-idea/src/io/flutter/editor/InlineWidgetViewModelData.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2019 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -package io.flutter.editor; - -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.ex.EditorEx; -import com.intellij.openapi.util.TextRange; - -public class InlineWidgetViewModelData extends WidgetViewModelData { - public final WidgetIndentGuideDescriptor descriptor; - public final Document document; - public final EditorEx editor; - - public InlineWidgetViewModelData( - WidgetIndentGuideDescriptor descriptor, - EditorEx editor, - WidgetEditingContext context - ) { - super(context); - this.descriptor = descriptor; - this.document = editor.getDocument(); - this.editor = editor; - } - - public TextRange getMarker() { - if (descriptor != null) { - return descriptor.getMarker(); - } - return null; - } -} diff --git a/flutter-idea/src/io/flutter/editor/LineRange.java b/flutter-idea/src/io/flutter/editor/LineRange.java deleted file mode 100644 index 04d3d4b6c8..0000000000 --- a/flutter-idea/src/io/flutter/editor/LineRange.java +++ /dev/null @@ -1,19 +0,0 @@ -/* - * Copyright 2019 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -package io.flutter.editor; - -/** - * A line range describing the extent of and indent guide. - */ -public class LineRange { - LineRange(int startLine, int endLine) { - this.startLine = startLine; - this.endLine = endLine; - } - - final int startLine; - final int endLine; -} diff --git a/flutter-idea/src/io/flutter/editor/OutlineLocation.java b/flutter-idea/src/io/flutter/editor/OutlineLocation.java index c325a08e2f..6925563f88 100644 --- a/flutter-idea/src/io/flutter/editor/OutlineLocation.java +++ b/flutter-idea/src/io/flutter/editor/OutlineLocation.java @@ -110,16 +110,6 @@ public class OutlineLocation implements Comparable { private final int line; private final int column; private final int indent; - private final int offset; - /** - * Tracker for the range of lines indent guides for the outline should show. - */ - private final TextRangeTracker guideTracker; - - /** - * Tracker for the entire range of text describing this outline location. - */ - private final TextRangeTracker fullTracker; @Nullable private String nodeStartingWord; @@ -130,8 +120,7 @@ public OutlineLocation( int line, int column, int indent, - VirtualFile file, - WidgetIndentsHighlightingPass pass + VirtualFile file ) { this.line = line; this.column = column; @@ -146,21 +135,9 @@ public OutlineLocation( assert (column >= indent); assert (line >= 0); this.indent = indent; - final int nodeOffset = pass.getConvertedOffset(node); - final int endOffset = pass.getConvertedOffset(node.getOffset() + node.getLength()); - fullTracker = new TextRangeTracker(nodeOffset, endOffset); - final int delta = Math.max(column - indent, 0); - this.offset = Math.max(nodeOffset - delta, 0); - guideTracker = new TextRangeTracker(offset, nodeOffset + 1); } public void dispose() { - if (guideTracker != null) { - guideTracker.dispose(); - } - if (fullTracker != null) { - fullTracker.dispose(); - } } /** @@ -172,11 +149,7 @@ public void dispose() { */ public void track(Document document) { this.document = document; - assert (indent <= column); - - fullTracker.track(document); - guideTracker.track(document); } @Override @@ -184,7 +157,6 @@ public int hashCode() { int hashCode = line; hashCode = hashCode * 31 + column; hashCode = hashCode * 31 + indent; - hashCode = hashCode * 31 + offset; return hashCode; } @@ -194,37 +166,7 @@ public boolean equals(Object o) { if (!(o instanceof OutlineLocation other)) return false; return line == other.line && column == other.column && - indent == other.indent && - offset == other.offset && - getGuideOffset() == other.getGuideOffset(); - } - - /** - * Offset in the document accurate even if the document has been edited. - */ - public int getGuideOffset() { - if (guideTracker.isTracking() && guideTracker.getRange() != null) { - return guideTracker.getRange().getStartOffset(); - } - return offset; - } - - // Sometimes markers stop being valid in which case we need to stop - // displaying the rendering until they are valid again. - public boolean isValid() { - if (!guideTracker.isTracking()) return true; - - return guideTracker.isConsistentEndingWord(); - } - - /** - * Line in the document this outline node is at. - */ - public int getLine() { - if (guideTracker.isTracking() && guideTracker.getRange() != null) { - return document.getLineNumber(guideTracker.getRange().getStartOffset()); - } - return line; + indent == other.indent; } public int getColumnForOffset(int offset) { @@ -233,44 +175,6 @@ public int getColumnForOffset(int offset) { return offset - document.getLineStartOffset(currentLine); } - /* - * Indent of the line to use for line visualization. - * - * This may intentionally differ from the column as for the line - * ` child: Text(` - * The indent will be 2 while the column is 9. - */ - public int getIndent() { - if (!guideTracker.isTracking()) { - return indent; - } - final TextRange range = guideTracker.getRange(); - assert (range != null); - return getColumnForOffset(range.getStartOffset()); - } - - /** - * Column this outline node is at. - *

- * This is the column offset of the start of the widget constructor call. - */ - public int getColumn() { - if (!guideTracker.isTracking()) { - return column; - } - final TextRange range = guideTracker.getRange(); - assert (range != null); - return getColumnForOffset(Math.max(range.getStartOffset(), range.getEndOffset() - 1)); - } - - public TextRange getGuideTextRange() { - return guideTracker.getRange(); - } - - public TextRange getFullRange() { - return fullTracker.getRange(); - } - @Override public int compareTo(OutlineLocation o) { // We use the initial location of the outline location when performing diff --git a/flutter-idea/src/io/flutter/editor/PreviewViewController.java b/flutter-idea/src/io/flutter/editor/PreviewViewController.java deleted file mode 100644 index 830fc8ba8e..0000000000 --- a/flutter-idea/src/io/flutter/editor/PreviewViewController.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * Copyright 2019 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -package io.flutter.editor; - -import com.intellij.openapi.Disposable; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.vfs.VirtualFile; -import io.flutter.inspector.DiagnosticsNode; -import io.flutter.inspector.InspectorService; -import org.jetbrains.annotations.Nullable; - -import java.awt.*; - -public class PreviewViewController extends PreviewViewControllerBase { - - Rectangle screenshotBoundsOverride; - final Component component; - - public PreviewViewController(WidgetViewModelData data, boolean drawBackground, Component component, Disposable parentDisposable) { - super(data, drawBackground, parentDisposable); - visible = true; - this.component = component; - } - - public void setScreenshotBounds(Rectangle bounds) { - final boolean sizeChanged = - screenshotBounds != null && (screenshotBounds.width != bounds.width || screenshotBounds.height != bounds.height); - screenshotBoundsOverride = bounds; - screenshotBounds = bounds; - if (sizeChanged) { - // Get a new screenshot as the existing screenshot isn't the right resolution. - // TODO(jacobr): only bother if the resolution is sufficiently different. - fetchScreenshot(false); - } - } - - @Override - protected Component getComponent() { - return component; - } - - @Override - protected VirtualFile getVirtualFile() { - return null; - } - - @Override - protected Document getDocument() { - return null; - } - - @Override - protected void showPopup(Point location, DiagnosticsNode node) { - popup = PropertyEditorPanel - .showPopup(data.context.inspectorGroupManagerService, data.context.project, getComponent(), node, - node.getCreationLocation().getLocation(), data.context.flutterDartAnalysisService, location); - } - - @Override - TextRange getActiveRange() { - return null; - } - - @Override - void setCustomCursor(@Nullable Cursor cursor) { - getComponent().setCursor(cursor); - } - - @Override - public void computeScreenshotBounds() { - screenshotBounds = screenshotBoundsOverride; - } - - @Override - protected Dimension getPreviewSize() { - return screenshotBoundsOverride.getSize(); - } - - @Override - public void forceRender() { - if (component != null) { - component.revalidate(); - component.repaint(); - } - } - - @Override - InspectorService.Location getLocation() { - return null; - } -} diff --git a/flutter-idea/src/io/flutter/editor/PreviewViewControllerBase.java b/flutter-idea/src/io/flutter/editor/PreviewViewControllerBase.java deleted file mode 100644 index fb9e707fe2..0000000000 --- a/flutter-idea/src/io/flutter/editor/PreviewViewControllerBase.java +++ /dev/null @@ -1,678 +0,0 @@ -/* - * Copyright 2019 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -package io.flutter.editor; - -import com.intellij.openapi.Disposable; -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.ui.popup.Balloon; -import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.ui.JBColor; -import com.intellij.util.ui.JBUI; -import com.intellij.util.ui.UIUtil; -import io.flutter.inspector.*; -import io.flutter.utils.AsyncRateLimiter; -import io.flutter.utils.math.Matrix4; -import io.flutter.utils.math.Vector3; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.awt.*; -import java.awt.event.MouseEvent; -import java.awt.geom.AffineTransform; -import java.awt.geom.Rectangle2D; -import java.util.ArrayList; -import java.util.Objects; -import java.util.concurrent.CompletableFuture; - -import static io.flutter.inspector.InspectorService.toSourceLocationUri; -import static java.lang.Math.min; - -/** - * Controller that displays interactive screen mirrors for specific widget - * subtrees or the entire app. - *

- * This controller abstracts away from the rendering code enough that it can be - * used both when rendering previews directly in the code editor and as part of - * a regular a regular component. - */ -public abstract class PreviewViewControllerBase extends WidgetViewController { - // Disable popup support initially as it isn't needed given we are showing - // properties in the outline view. - public static final boolean ENABLE_POPUP_SUPPORT = false; - public static final int PREVIEW_PADDING_X = 20; - public static final double MOUSE_FRAMES_PER_SECOND = 10.0; - public static final double SCREENSHOT_FRAMES_PER_SECOND = 3.0; - public static final int PREVIEW_MAX_WIDTH = 280; - public static final int PREVIEW_MAX_HEIGHT = 520; - private static final Stroke SOLID_STROKE = new BasicStroke(1); - private static final Color SHADOW_COLOR = new JBColor(new Color(0, 0, 0, 64), new Color(0, 0, 0, 64)); - protected static final int defaultLineHeight = 20; - protected final boolean drawBackground; - protected Balloon popup; - protected Point lastPoint; - protected AsyncRateLimiter mouseRateLimiter; - protected AsyncRateLimiter screenshotRateLimiter; - protected boolean controlDown; - protected ArrayList currentHits; - protected InspectorObjectGroupManager hover; - protected InspectorService.ObjectGroup screenshotGroup; - protected boolean screenshotDirty = false; - protected int extraHeight = 0; - protected boolean screenshotLoading; - protected boolean popopOpenInProgress; - protected boolean altDown; - protected Screenshot screenshot; - protected ArrayList boxes; - Rectangle relativeRect; - Rectangle lastLockedRectangle; - Rectangle screenshotBounds; - // Screenshot bounds in absolute window coordinates. - Rectangle lastScreenshotBoundsWindow; - int maxHeight; - boolean _mouseInScreenshot = false; - - public PreviewViewControllerBase(WidgetViewModelData data, boolean drawBackground, Disposable parent) { - super(data, parent); - this.drawBackground = drawBackground; - } - - @Override - public void dispose() { - if (popup != null) { - popup.dispose(); - popup = null; - } - if (hover != null) { - hover.clear(false); - hover = null; - } - screenshot = null; - super.dispose(); - } - - AsyncRateLimiter getMouseRateLimiter() { - if (mouseRateLimiter != null) return mouseRateLimiter; - mouseRateLimiter = new AsyncRateLimiter(MOUSE_FRAMES_PER_SECOND, () -> { - return updateMouse(false); - }, this); - return mouseRateLimiter; - } - - AsyncRateLimiter getScreenshotRateLimiter() { - if (screenshotRateLimiter != null) return screenshotRateLimiter; - screenshotRateLimiter = new AsyncRateLimiter(SCREENSHOT_FRAMES_PER_SECOND, this::updateScreenshot, this); - return screenshotRateLimiter; - } - - public InspectorObjectGroupManager getHovers() { - if (hover != null && hover.getInspectorService() == getInspectorService()) { - return hover; - } - if (getInspectorService() == null) return null; - hover = new InspectorObjectGroupManager(getInspectorService(), "hover"); - return hover; - } - - public @Nullable - InspectorService.ObjectGroup getScreenshotGroup() { - if (screenshotGroup != null && screenshotGroup.getInspectorService() == getInspectorService()) { - return screenshotGroup; - } - if (getInspectorService() == null) return null; - screenshotGroup = getInspectorService().createObjectGroup("screenshot"); - return screenshotGroup; - } - - protected double getDPI() { - return JBUI.pixScale(getComponent()); - } - - protected abstract Component getComponent(); - - protected int toPixels(int value) { - return (int)(value * getDPI()); - } - - Rectangle getScreenshotBoundsTight() { - // TODO(jacobr): cache this. - if (screenshotBounds == null || extraHeight == 0) return screenshotBounds; - final Rectangle bounds = new Rectangle(screenshotBounds); - bounds.height -= extraHeight; - bounds.y += extraHeight; - return bounds; - } - - @Override - public void onSelectionChanged(DiagnosticsNode selection) { - super.onSelectionChanged(selection); - if (!visible) { - return; - } - - if (getLocation() != null) { - // The screenshot is dirty because the selection could influence which - // of the widgets matching the location should be displayed. - screenshotDirty = true; - } - // Fetching a screenshot is somewhat slow so we schedule getting just the - // bounding boxes before the screenshot arives to show likely correct - // bounding boxes sooner. - getGroups().cancelNext(); - final InspectorService.ObjectGroup nextGroup = getGroups().getNext(); - final CompletableFuture> selectionResults = nextGroup.getBoundingBoxes(getSelectedElement(), selection); - - nextGroup.safeWhenComplete(selectionResults, (boxes, error) -> { - if (nextGroup.isDisposed()) return; - if (error != null || boxes == null) { - return; - } - // To be paranoid, avoid the inspector going out of scope. - if (getGroups() == null) return; - getGroups().promoteNext(); - this.boxes = boxes; - forceRender(); - }); - - // Showing just the selected elements is dangerous as they may be out of - // sync with the current screenshot if it has changed. To be safe, schedule - // a screenshot with the updated UI. - getScreenshotRateLimiter().scheduleRequest(); - } - - public CompletableFuture updateMouse(boolean navigateTo) { - final InspectorObjectGroupManager hoverGroups = getHovers(); - if (hoverGroups == null || ((popupActive() || popopOpenInProgress) && !navigateTo)) { - return CompletableFuture.completedFuture(null); - } - final Screenshot latestScreenshot = getScreenshotNow(); - if (screenshotBounds == null || - latestScreenshot == null || - lastPoint == null || - !getScreenshotBoundsTight().contains(lastPoint) || - getSelectedElement() == null) { - return CompletableFuture.completedFuture(null); - } - hoverGroups.cancelNext(); - final InspectorService.ObjectGroup nextGroup = hoverGroups.getNext(); - final Matrix4 matrix = buildTransformToScreenshot(latestScreenshot); - matrix.invert(); - final Vector3 point = matrix.perspectiveTransform(new Vector3(lastPoint.getX(), lastPoint.getY(), 0)); - final String file; - final int startLine, endLine; - if (controlDown || getVirtualFile() == null) { - file = null; - } - else { - file = toSourceLocationUri(getVirtualFile().getPath()); - } - - final TextRange activeRange = getActiveRange(); - if (controlDown || activeRange == null || getDocument() == null) { - startLine = -1; - endLine = -1; - } - else { - startLine = getDocument().getLineNumber(activeRange.getStartOffset()); - endLine = getDocument().getLineNumber(activeRange.getEndOffset()); - } - - final CompletableFuture> hitResults = - nextGroup.hitTest(getSelectedElement(), point.getX(), point.getY(), file, startLine, endLine); - nextGroup.safeWhenComplete(hitResults, (hits, error) -> { - - if (nextGroup.isDisposed()) return; - if (error != null || hits == null) { - return; - } - currentHits = hits; - hoverGroups.promoteNext(); - // TODO(jacobr): consider removing the navigateTo option? - if (navigateTo && popopOpenInProgress) { - final DiagnosticsNode node = !hits.isEmpty() ? hits.get(0) : null; - if (node == null) return; - final TransformedRect transform = node.getTransformToRoot(); - if (transform != null) { - double x, y; - final Matrix4 transformMatrix = buildTransformToScreenshot(latestScreenshot); - transformMatrix.multiply(transform.getTransform()); - final Rectangle2D rect = transform.getRectangle(); - final Vector3 transformed = transformMatrix.perspectiveTransform(new Vector3(new double[]{rect.getCenterX(), rect.getMinY(), 0})); - final Point pendingPopupOpenLocation = new Point((int)Math.round(transformed.getX()), (int)Math.round(transformed.getY() + 1)); - showPopup(pendingPopupOpenLocation, node); - } - popopOpenInProgress = false; - } - if (navigateTo && !hits.isEmpty()) { - getGroups().getCurrent().setSelection(hits.get(0).getValueRef(), false, false); - } - forceRender(); - }); - return hitResults; - } - - protected abstract VirtualFile getVirtualFile(); - - abstract protected Document getDocument(); - - protected abstract void showPopup(Point location, DiagnosticsNode node); - - abstract @Nullable - TextRange getActiveRange(); - - void setMouseInScreenshot(boolean v) { - if (_mouseInScreenshot == v) return; - _mouseInScreenshot = v; - forceRender(); - } - - public void updateMouseCursor() { - if (screenshotBounds == null) { - setMouseInScreenshot(false); - return; - } - if (lastPoint != null && screenshotBounds.contains(lastPoint)) { - // TODO(jacobr): consider CROSSHAIR_CURSOR instead which gives more of - // a pixel selection feel. - setCustomCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); - setMouseInScreenshot(true); - if (!getScreenshotBoundsTight().contains(lastPoint)) { - cancelHovers(); - } - } - else { - setCustomCursor(null); - _mouseOutOfScreenshot(); - } - } - - abstract void setCustomCursor(@Nullable Cursor cursor); - - void registerLastEvent(MouseEvent event) { - lastPoint = event.getPoint(); - controlDown = event.isControlDown(); - altDown = event.isAltDown(); - updateMouseCursor(); - } - - public void onMouseMoved(MouseEvent event) { - assert (!event.isConsumed()); - registerLastEvent(event); - if (_mouseInScreenshot) { - event.consume(); - if (getScreenshotBoundsTight().contains(lastPoint) && !popupActive()) { - getMouseRateLimiter().scheduleRequest(); - } - } - } - - boolean popupActive() { - return popup != null && !popup.isDisposed() || popopOpenInProgress; - } - - protected void _hidePopup() { - if (popup != null && !popup.isDisposed()) { - popup.dispose(); - popup = null; - } - } - - public void onMousePressed(MouseEvent event) { - registerLastEvent(event); - final Point point = event.getPoint(); - if (screenshotBounds != null && screenshotBounds.contains(point)) { - event.consume(); - if (isPopupTrigger(event)) { - _hidePopup(); - popopOpenInProgress = true; - updateMouse(true); - } - } - } - - private boolean isPopupTrigger(MouseEvent event) { - return ENABLE_POPUP_SUPPORT && event.isPopupTrigger(); - } - - @Override - public void onMouseReleased(MouseEvent event) { - registerLastEvent(event); - - if (screenshotBounds == null) return; - final Point point = event.getPoint(); - if (screenshotBounds.contains(point)) { - final Rectangle tightBounds = getScreenshotBoundsTight(); - event.consume(); - if (tightBounds.contains(point)) { - if (isPopupTrigger(event)) { - _hidePopup(); - popopOpenInProgress = true; - } - updateMouse(true); - } - else { - // Title hit. - _hidePopup(); - if (elements != null) { - if (elements.size() > 1) { - int newIndex = this.activeIndex + 1; - if (newIndex >= elements.size()) { - newIndex = 1; // Wrap around hack case because the first index is out of order. - } - // TODO(jacobr): we could update activeIndex now instead of waitng for the UI to update. - getGroups().getCurrent().setSelection(elements.get(newIndex).getValueRef(), false, true); - } - else if (elements.size() == 1) { - // Select current. - getGroups().getCurrent().setSelection(elements.get(0).getValueRef(), false, true); - } - } - } - } - } - - public void onMouseExited(MouseEvent event) { - lastPoint = null; - controlDown = false; - altDown = false; - setMouseInScreenshot(false); - updateMouseCursor(); - } - - public void onFlutterFrame() { - fetchScreenshot(false); - } - - public void _mouseOutOfScreenshot() { - setMouseInScreenshot(false); - lastPoint = null; - cancelHovers(); - } - - public void cancelHovers() { - hover = getHovers(); - if (hover != null) { - hover.cancelNext(); - controlDown = false; - altDown = false; - } - if (currentHits != null) { - currentHits = null; - forceRender(); - } - } - - public void onMouseEntered(MouseEvent event) { - onMouseMoved(event); - } - - void clearState() { - screenshotDirty = true; - currentHits = new ArrayList<>(); - if (hover != null) { - hover.clear(true); - } - screenshot = null; - } - - @Override - public void onInspectorAvailabilityChanged() { - clearState(); - } - - public void onVisibleChanged() { - if (!visible) { - _hidePopup(); - } - if (visible) { - computeScreenshotBounds(); - if (getInspectorService() != null) { - if (screenshot == null || screenshotDirty) { - fetchScreenshot(false); - } - } - } - } - - abstract public void computeScreenshotBounds(); - - @Override - public boolean updateVisiblityLocked(Rectangle newRectangle) { - if (popupActive()) { - lastLockedRectangle = new Rectangle(newRectangle); - return true; - } - if (lastLockedRectangle != null && visible) { - if (newRectangle.intersects(lastLockedRectangle)) { - return true; - } - // Stop locking. - lastLockedRectangle = null; - } - return false; - } - - // When visibility is locked, we should try to keep the preview in view even - // if the user navigates around the code editor. - public boolean isVisiblityLocked() { - if (popupActive()) { - return true; - } - if (lastLockedRectangle != null && visible && visibleRect != null) { - return visibleRect.intersects(lastLockedRectangle); - } - return false; - } - - public Screenshot getScreenshotNow() { - return screenshot; - } - - /** - * Builds a transform that maps global window coordinates to coordinates - * within the screenshot. - */ - protected Matrix4 buildTransformToScreenshot(Screenshot latestScreenshot) { - final Matrix4 matrix = Matrix4.identity(); - matrix.translate(screenshotBounds.x, screenshotBounds.y + extraHeight, 0); - final Rectangle2D imageRect = latestScreenshot.transformedRect.getRectangle(); - final double centerX = imageRect.getCenterX(); - final double centerY = imageRect.getCenterY(); - matrix.translate(-centerX, -centerY, 0); - matrix.scale(1 / getDPI(), 1 / getDPI(), 1 / getDPI()); - matrix.translate(centerX * getDPI(), centerY * getDPI(), 0); - matrix.multiply(latestScreenshot.transformedRect.getTransform()); - return matrix; - } - - protected ArrayList getNodesToHighlight() { - return currentHits != null && !currentHits.isEmpty() ? currentHits : boxes; - } - - protected void clearScreenshot() { - if (getScreenshotNow() != null) { - screenshot = null; - computeScreenshotBounds(); - forceRender(); - } - } - - @Override - public void setElements(ArrayList elements) { - super.setElements(elements); - currentHits = null; - boxes = null; - } - - boolean hasCurrentHits() { - return currentHits != null && !currentHits.isEmpty(); - } - - public void onMaybeFetchScreenshot() { - if (screenshot == null || screenshotDirty) { - fetchScreenshot(false); - } - } - - void fetchScreenshot(boolean mightBeIncompatible) { - if (mightBeIncompatible) { - screenshotLoading = true; - } - screenshotDirty = true; - if (!visible) return; - - getScreenshotRateLimiter().scheduleRequest(); - } - - protected CompletableFuture updateScreenshot() { - final InspectorService.ObjectGroup group = getScreenshotGroup(); - if (group == null) { - return CompletableFuture.completedFuture(null); - } - - final Dimension previewSize = getPreviewSize(); - final long startTime = System.currentTimeMillis(); - // 0.7 is a tweak to ensure we do not try to download enormous screenshots. - final CompletableFuture screenshotFuture = - group.getScreenshotAtLocation(getLocation(), 10, toPixels(previewSize.width), toPixels(previewSize.height), getDPI() * 0.7); - group.safeWhenComplete( - screenshotFuture, - (pair, e2) -> { - if (e2 != null || group.isDisposed() || getInspectorService() == null) return; - if (pair == null) { - setElements(null); - screenshot = null; - boxes = null; - } - else { - setElements(pair.elements); - screenshot = pair.screenshot; - boxes = pair.boxes; - } - screenshotDirty = false; - screenshotLoading = false; - // This calculation might be out of date due to the new screenshot. - computeScreenshotBounds(); - forceRender(); - } - ); - return screenshotFuture; - } - - protected abstract Dimension getPreviewSize(); - - public void paint(@NotNull Graphics g, int lineHeight) { - final Graphics2D g2d = (Graphics2D)g.create(); - // Required to render colors with an alpha channel. Rendering with an - // alpha chanel makes it easier to keep relationships between shadows - // and lines looking consistent when the background color changes such - // as in the case of selection or a different highlighter turning the - // background yellow. - g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1)); - final Rectangle clip = g2d.getClipBounds(); - computeScreenshotBounds(); - if (screenshotBounds == null || !clip.intersects(screenshotBounds)) { - return; - } - - final Screenshot latestScreenshot = getScreenshotNow(); - final int imageWidth = screenshotBounds.width; - final int imageHeight = screenshotBounds.height; - - if (drawBackground) { - // TODO(jacobr): draw a shadow around the outline in a more principled way. - final int SHADOW_HEIGHT = 5; - for (int h = 1; h < SHADOW_HEIGHT; h++) { - g2d.setColor(new Color(43, 43, 43, 100 - h * 20)); - g2d.fillRect(screenshotBounds.x - h + 1, screenshotBounds.y + min(screenshotBounds.height, imageHeight) + h, - min(screenshotBounds.width, imageWidth) + h - 1, - 1); - g2d.fillRect(screenshotBounds.x - h + 1, - screenshotBounds.y - h, - min(screenshotBounds.width, imageWidth) + h - 1, - 1); - g2d.fillRect(screenshotBounds.x - h, screenshotBounds.y - h, 1, imageHeight + 2 * h); - } - } - g2d.clip(screenshotBounds); - - final Font font = UIUtil.getFont(UIUtil.FontSize.MINI, UIUtil.getTreeFont()); - g2d.setFont(font); - g2d.setColor(isSelected ? JBColor.GRAY : JBColor.LIGHT_GRAY); - - if (latestScreenshot != null) { - g2d.clip(getScreenshotBoundsTight()); - - g2d.drawImage(latestScreenshot.image, - new AffineTransform(1 / getDPI(), 0f, 0f, 1 / getDPI(), screenshotBounds.x, screenshotBounds.y + extraHeight), null); - - final java.util.List nodesToHighlight = getNodesToHighlight(); - // Sometimes it is fine to display even if we are loading. - // TODO(jacobr): be smarter and track if the highlights are associated with a different screenshot. - if (nodesToHighlight != null && !nodesToHighlight.isEmpty()) { //&& !screenshotLoading) { - boolean first = true; - for (DiagnosticsNode box : nodesToHighlight) { - final TransformedRect transform = box.getTransformToRoot(); - if (transform != null) { - double x, y; - final Matrix4 matrix = buildTransformToScreenshot(latestScreenshot); - matrix.multiply(transform.getTransform()); - final Rectangle2D rect = transform.getRectangle(); - - final Vector3[] points = new Vector3[]{ - matrix.perspectiveTransform(new Vector3(new double[]{rect.getMinX(), rect.getMinY(), 0})), - matrix.perspectiveTransform(new Vector3(new double[]{rect.getMaxX(), rect.getMinY(), 0})), - matrix.perspectiveTransform(new Vector3(new double[]{rect.getMaxX(), rect.getMaxY(), 0})), - matrix.perspectiveTransform(new Vector3(new double[]{rect.getMinX(), rect.getMaxY(), 0})) - }; - - // The widget's bounding box may be rotated or otherwise - // transformed so we can't simply draw a rectangle. - final Polygon polygon = new Polygon(); - for (Vector3 point : points) { - polygon.addPoint((int)Math.round(point.getX()), (int)Math.round(point.getY())); - } - - if (first && !elements.isEmpty() && !Objects.equals(box.getValueRef(), elements.get(0).getValueRef())) { - g2d.setColor(FlutterEditorColors.HIGHLIGHTED_RENDER_OBJECT_BORDER_COLOR); - g2d.fillPolygon(polygon); - } - g2d.setStroke(SOLID_STROKE); - g2d.setColor(FlutterEditorColors.HIGHLIGHTED_RENDER_OBJECT_BORDER_COLOR); - g2d.drawPolygon(polygon); - } - first = false; - } - } - } - else { - if (drawBackground) { - g2d.setColor(isSelected ? JBColor.GRAY : JBColor.LIGHT_GRAY); - g2d.fillRect(screenshotBounds.x, screenshotBounds.y, screenshotBounds.width, screenshotBounds.height); - g2d.setColor(FlutterEditorColors.SHADOW_GRAY); - } - g2d.setColor(JBColor.BLACK); - - drawMultilineString(g2d, getNoScreenshotMessage(), screenshotBounds.x + 4, screenshotBounds.y + lineHeight - 4, lineHeight); - } - g2d.setClip(clip); - - g2d.dispose(); - } - - String getNoScreenshotMessage() { - return getInspectorService() == null ? "Run the application to\nactivate device mirror." : "Loading..."; - } - - // TODO(jacobr): perhaps cache and optimize. - // This UI is sometimes rendered in the code editor where we can't simply use a JBLabel. - protected void drawMultilineString(Graphics g, String s, int x, int y, int lineHeight) { - for (String line : s.split("\n")) { - g.drawString(line, x, y); - y += lineHeight; - } - } -} diff --git a/flutter-idea/src/io/flutter/editor/PreviewsForEditor.java b/flutter-idea/src/io/flutter/editor/PreviewsForEditor.java deleted file mode 100644 index 601b16de80..0000000000 --- a/flutter-idea/src/io/flutter/editor/PreviewsForEditor.java +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2019 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -package io.flutter.editor; - -import com.intellij.openapi.Disposable; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.ex.EditorEx; -import com.intellij.openapi.editor.markup.CustomHighlighterRenderer; -import com.intellij.openapi.editor.markup.RangeHighlighter; -import com.intellij.openapi.util.Disposer; -import org.jetbrains.annotations.NotNull; - -import java.awt.*; -import java.awt.event.MouseEvent; -import java.util.ArrayList; -import java.util.Comparator; - -/** - * Class that manages rendering screenshots of all build methods in a file - * directly in the text editor. - */ -public class PreviewsForEditor implements CustomHighlighterRenderer { - static final boolean showOverallPreview = false; - private final EditorEx editor; - private final Disposable parentDisposable; - private final WidgetEditingContext data; - private final InlinePreviewViewController overallPreview; - private ArrayList previews; - - public PreviewsForEditor(WidgetEditingContext data, - EditorMouseEventService editorEventService, - EditorEx editor, - Disposable parentDisposable) { - this.data = data; - this.editor = editor; - this.parentDisposable = parentDisposable; - previews = new ArrayList<>(); - if (showOverallPreview) { - overallPreview = new InlinePreviewViewController( - new InlineWidgetViewModelData(null, editor, data), - true, - parentDisposable - ); - } - else { - overallPreview = null; - } - editorEventService.addListener( - editor, - new EditorMouseEventService.Listener() { - - @Override - public void onMouseMoved(MouseEvent event) { - for (PreviewViewControllerBase preview : getAllPreviews(false)) { - if (event.isConsumed()) { - preview.onMouseExited(event); - } - else { - preview.onMouseMoved(event); - } - } - } - - @Override - public void onMousePressed(MouseEvent event) { - for (PreviewViewControllerBase preview : getAllPreviews(false)) { - preview.onMousePressed(event); - if (event.isConsumed()) break; - } - } - - @Override - public void onMouseReleased(MouseEvent event) { - for (PreviewViewControllerBase preview : getAllPreviews(false)) { - preview.onMouseReleased(event); - if (event.isConsumed()) break; - } - } - - @Override - public void onMouseEntered(MouseEvent event) { - for (PreviewViewControllerBase preview : getAllPreviews(false)) { - if (event.isConsumed()) { - preview.onMouseExited(event); - } - else { - preview.onMouseEntered(event); - } - } - } - - @Override - public void onMouseExited(MouseEvent event) { - for (PreviewViewControllerBase preview : getAllPreviews(false)) { - preview.onMouseExited(event); - } - } - }, - parentDisposable - ); - } - - public void outlinesChanged(Iterable newDescriptors) { - final ArrayList newPreviews = new ArrayList<>(); - - int i = 0; - // TODO(jacobr): be smarter about reusing. - for (WidgetIndentGuideDescriptor descriptor : newDescriptors) { - if (descriptor.parent == null) { - if (i >= previews.size() || !descriptor.equals(previews.get(i).getDescriptor())) { - newPreviews.add(new InlinePreviewViewController(new InlineWidgetViewModelData(descriptor, editor, data), true, parentDisposable)); - } - else { - newPreviews.add(previews.get(i)); - i++; - } - } - } - while (i < previews.size()) { - Disposer.dispose(previews.get(i)); - i++; - } - previews = newPreviews; - } - - protected Iterable getAllPreviews(boolean paintOrder) { - final ArrayList all = new ArrayList<>(); - if (overallPreview != null) { - all.add(overallPreview); - } - all.addAll(previews); - if (paintOrder) { - all.sort(Comparator.comparingInt(InlinePreviewViewController::getPriority)); - } - else { - all.sort((a, b) -> Integer.compare(b.getPriority(), a.getPriority())); - } - return all; - } - - @Override - public void paint(@NotNull Editor editor, @NotNull RangeHighlighter highlighter, @NotNull Graphics graphics) { - for (InlinePreviewViewController preview : getAllPreviews(true)) { - if (preview.visible) { - preview.paint(editor, highlighter, graphics); - } - } - } -} diff --git a/flutter-idea/src/io/flutter/editor/WidgetIndentGuideDescriptor.java b/flutter-idea/src/io/flutter/editor/WidgetIndentGuideDescriptor.java deleted file mode 100644 index a60c7ef515..0000000000 --- a/flutter-idea/src/io/flutter/editor/WidgetIndentGuideDescriptor.java +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright 2019 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -package io.flutter.editor; - -import com.intellij.openapi.editor.Document; -import com.intellij.openapi.editor.RangeMarker; -import com.intellij.openapi.util.TextRange; -import org.dartlang.analysis.server.protocol.FlutterOutline; -import org.dartlang.analysis.server.protocol.FlutterOutlineAttribute; -import org.dartlang.analysis.server.protocol.Location; - -import java.util.ArrayList; - -/** - * Analog to the IndentGuideDescriptor class from the regular FilteredIndentsHighlightingPass. - *

- * The core difference relative to IndentGuideDescriptor is this descriptor - * tracks a list of child nodes to visualize the tree structure of a build - * method. WidgetIndentsHighlightingPass will use this information to draw horizontal - * lines to show part-child relationships. - *

- * Widget indent guides depend on the analysis service as the source of truth, - * so more information has to be still accurate even after the document is - * edited as there will be a slight delay before new analysis data is available. - */ -public class WidgetIndentGuideDescriptor { - public static class WidgetPropertyDescriptor { - private RangeMarker marker; - private final FlutterOutlineAttribute attribute; - - WidgetPropertyDescriptor(FlutterOutlineAttribute attribute) { - this.attribute = attribute; - } - - public String getName() { - return attribute.getName(); - } - - public FlutterOutlineAttribute getAttribute() { - return attribute; - } - - public int getEndOffset() { - if (marker == null) { - final Location location = attribute.getValueLocation(); - final int startOffset = location.getOffset(); - final int endOffset = startOffset + location.getLength(); - return endOffset; - } - return marker.getEndOffset(); - } - - public void track(Document document) { - if (marker != null) { - // TODO(jacobr): it does indicate a bit of a logic bug if we are calling this method twice. - assert (marker.getDocument() == document); - return; - } - - // Create a range marker that goes from the start of the indent for the line - // to the column of the actual entity. - final int docLength = document.getTextLength(); - final Location location = attribute.getValueLocation(); - final int startOffset = Math.min(location.getOffset(), docLength); - final int endOffset = Math.min(startOffset + location.getLength(), docLength); - - marker = document.createRangeMarker(startOffset, endOffset); - } - - public void dispose() { - if (marker != null) { - marker.dispose(); - } - } - } - - public final WidgetIndentGuideDescriptor parent; - public final ArrayList childLines; - public final OutlineLocation widget; - public final int indentLevel; - public final int startLine; - public final int endLine; - - public final FlutterOutline outlineNode; - - public WidgetIndentGuideDescriptor( - WidgetIndentGuideDescriptor parent, - int indentLevel, - int startLine, - int endLine, - ArrayList childLines, - OutlineLocation widget, - FlutterOutline outlineNode - ) { - this.parent = parent; - this.childLines = childLines; - this.widget = widget; - this.indentLevel = indentLevel; - this.startLine = startLine; - this.endLine = endLine; - this.outlineNode = outlineNode; - } - - void dispose() { - if (widget != null) { - widget.dispose(); - } - if (childLines == null) return; - for (OutlineLocation childLine : childLines) { - childLine.dispose(); - } - - childLines.clear(); - } - - /** - * This method must be called to opt the indent guide into tracking - * location changes due to document edits. - *

- * If trackLocations is called on a descriptor, you must later call dispose - * to stop listening for changes to the document once the descriptor is - * obsolete. - */ - boolean tracked = false; - - public void trackLocations(Document document) { - - if (tracked) return; - tracked = true; - if (widget != null) { - widget.track(document); - } - if (childLines == null) return; - for (OutlineLocation childLine : childLines) { - childLine.track(document); - } - } - - public TextRange getMarker() { - return widget.getFullRange(); - } - - @Override - public int hashCode() { - int result = indentLevel; - result = 31 * result + startLine; - result = 31 * result + endLine; - if (childLines != null) { - for (OutlineLocation location : childLines) { - result = 31 * result + location.hashCode(); - } - } - if (widget != null) { - result = 31 * result + widget.hashCode(); - } - return result; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - final WidgetIndentGuideDescriptor that = (WidgetIndentGuideDescriptor)o; - - if (endLine != that.endLine) return false; - if (indentLevel != that.indentLevel) return false; - if (startLine != that.startLine) return false; - - if (childLines == null || that.childLines == null) { - return childLines == that.childLines; - } - - if (childLines.size() != that.childLines.size()) { - return false; - } - - for (int i = 0; i < childLines.size(); ++i) { - if (!childLines.get(i).equals(that.childLines.get(i))) { - return false; - } - } - - return true; - } - - public int compareTo(WidgetIndentGuideDescriptor that) { - int answer = endLine - that.endLine; - if (answer != 0) { - return answer; - } - answer = indentLevel - that.indentLevel; - if (answer != 0) { - return answer; - } - answer = startLine - that.startLine; - if (answer != 0) { - return answer; - } - - if (childLines == that.childLines) { - return 0; - } - - if (childLines == null || that.childLines == null) { - return childLines == null ? -1 : 1; - } - - answer = childLines.size() - that.childLines.size(); - - if (answer != 0) { - return answer; - } - - for (int i = 0; i < childLines.size(); ++i) { - answer = childLines.get(i).compareTo(that.childLines.get(i)); - if (answer != 0) { - return answer; - } - } - return 0; - } -} diff --git a/flutter-idea/src/io/flutter/editor/WidgetIndentHitTester.java b/flutter-idea/src/io/flutter/editor/WidgetIndentHitTester.java deleted file mode 100644 index e95595e327..0000000000 --- a/flutter-idea/src/io/flutter/editor/WidgetIndentHitTester.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright 2019 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -package io.flutter.editor; - -import com.intellij.openapi.editor.Document; - -import java.util.Arrays; -import java.util.List; - -import static java.lang.Math.max; -import static java.lang.Math.min; - -/** - * Class used to determine whether regular indent guides intersect with - * WidgetIndentGuides. - *

- * If indent guides are allowed to render that intersect with widget indent - * guides, there will be flickering or other strange visual artifacts. - *

- * It is not possible to get indent guides to render in a stable z-order so - * there is no way to make regular indent guides render before widget indent - * guides. Even if that was possible it would not be desirable as there are - * cases where displaying both guides would be distracting such as showing a - * regular guide at indent 2 that draws a line through the middle of the - * horizontal leg of widget indent guides for a list of children with indent 4. - */ -public class WidgetIndentHitTester { - /** - * Whether each line overlaps a Widget Indent Guide. - */ - private final boolean[] lines; - - WidgetIndentHitTester(List descriptors, Document document) { - final int lineCount = document.getLineCount(); - lines = new boolean[lineCount]; - // TODO(jacobr): optimize using a more clever data structure. - for (WidgetIndentGuideDescriptor descriptor : descriptors) { - // if (descriptor.parent) - { - final int last = min(lines.length, descriptor.endLine + 1); - for (int i = max(descriptor.startLine - 1, 0); i < last; i++) { - lines[i] = true; - } - } - } - } - - @Override - public int hashCode() { - return Arrays.hashCode(lines); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof WidgetIndentHitTester other)) return false; - return Arrays.equals(lines, other.lines); - } - - // TODO(jacobr): we could be smarter about intersection detection by - // considering the indent of the intersecting lineRange as well. - // If we could do that we would have to filter out fewer regular indent - // guides that appear to intersect with the widget indent guides. - // This could really be reframed as a rectangle intersection problem but that - // would introduce additional complexity and we have yet to receive feedback - // complaining about the missing regular indent guides for cases where the - // guides overlap horizontally. - public boolean intersects(LineRange lineRange) { - final int last = min(lines.length, lineRange.endLine + 1); - // TODO(jacobr): why the -1 on startLine? - for (int i = max(lineRange.startLine - 1, 0); i < last; i++) { - if (lines[i]) { - return true; - } - } - return false; - } -} diff --git a/flutter-idea/src/io/flutter/editor/WidgetIndentsHighlightingPass.java b/flutter-idea/src/io/flutter/editor/WidgetIndentsHighlightingPass.java deleted file mode 100644 index e897a07205..0000000000 --- a/flutter-idea/src/io/flutter/editor/WidgetIndentsHighlightingPass.java +++ /dev/null @@ -1,922 +0,0 @@ -/* - * Copyright 2019 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -package io.flutter.editor; - -import com.intellij.openapi.Disposable; -import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.editor.*; -import com.intellij.openapi.editor.colors.EditorColors; -import com.intellij.openapi.editor.colors.EditorColorsScheme; -import com.intellij.openapi.editor.ex.EditorEx; -import com.intellij.openapi.editor.impl.EditorImpl; -import com.intellij.openapi.editor.markup.*; -import com.intellij.openapi.progress.ProgressManager; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Key; -import com.intellij.openapi.util.Segment; -import com.intellij.openapi.util.TextRange; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiDocumentManager; -import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiFile; -import com.intellij.ui.Gray; -import com.intellij.ui.JBColor; -import com.intellij.ui.paint.LinePainter2D; -import com.intellij.util.DocumentUtil; -import com.intellij.util.text.CharArrayUtil; -import com.jetbrains.lang.dart.analyzer.DartAnalysisServerService; -import com.jetbrains.lang.dart.psi.DartCallExpression; -import io.flutter.dart.FlutterDartAnalysisServer; -import io.flutter.inspector.InspectorGroupManagerService; -import io.flutter.settings.FlutterSettings; -import org.dartlang.analysis.server.protocol.FlutterOutline; -import org.jetbrains.annotations.NotNull; - -import java.awt.*; -import java.util.List; -import java.util.*; - -import static java.lang.Math.*; - -// Instructions for how this code should be tested: -// This code could be tested by true integration tests or better yet by -// unittests that are able to create Editor object instances. Testing this -// code does not require running a Flutter application but it does require -// creating Editor object instances and would benefit from creating a live -// Dart analysis server to communicate with. -// -// Suggested steps to test this code: -// Create a representative Dart file containing a a couple build methods with -// deeply nested widget trees. -// Create an Editor instance from the dart file. -// Get a Flutter outline from that file or inject a snapshotted Flutter outline -// iof it isn't feasible to get a true Flutter outline. -// Verify that calling -// pass = new WidgetIndentsHighlightingPass(project, editor); -// pass.setOutline(flutterOutline); -// results in adding highlights to the expected ranges. Highlighters can be -// found querying the editor directly or calling. - -// final CustomHighlighterRenderer renderer = highlighter.getCustomRenderer(); -// ((WidgetCustomHighlighterRenderer)renderer).dispose(); -// You could then even call the render method on a highlighter if you wanted -// a golden image that just contained the widget indent tree diagram. In -// practice it would be sufficient to get the WidgetIndentGuideDescriptor from -// the renderer and verify that the child locations are correct. The -// important piece to test is that the child widget locations are acurate even -// after making some edits to the document which brings to the next step: -// Make a couple edits to the document and verify that the widget indents are -// still accurate even after the change. The machinery in Editor will track the -// edits and update the widget indents appropriately even before a new -// FlutterOutline is available. -// -// Final step: create a new FlutterOutline and verify passing it in updates the -// widget guides removing guides not part of the outline. For example, Add a -// character to a constructor name so the constructor is not a Widget subclass. -// That will cause the outermost guide in the tree to be removed. Alternately, -// add another widget to the list of children for a widget. -// -// You could also performa golden image integration test to verify that the -// actual render of the text editor matched what was expected but changes -// in font rendering would make that tricky. - -/** - * A WidgetIndentsHighlightingPass draws UI as Code Guides for a code editor using a - * FlutterOutline. - *

- * This class is similar to a TextEditorHighlightingPass but doesn't actually - * implement TextEditorHighlightingPass as it is driven by changes to the - * FlutterOutline which is only available when the AnalysisServer computes a - * new outline while TextEditorHighlightingPass assumes all information needed - * is available immediately. - */ -public class WidgetIndentsHighlightingPass { - private static final Logger LOG = Logger.getInstance(WidgetIndentsHighlightingPass.class); - - // Delta between the start of a column and where indent guides should start; - public static int INDENT_GUIDE_DELTA = -2; - private final static Stroke SOLID_STROKE = new BasicStroke(1); - private final static JBColor VERY_LIGHT_GRAY = new JBColor(Gray._224, Gray._80); - private final static JBColor SHADOW_GRAY = new JBColor(Gray._192, Gray._100); - private final static JBColor OUTLINE_LINE_COLOR = new JBColor(Gray._128, Gray._128); - private final static JBColor OUTLINE_LINE_COLOR_PAST_BLOCK = new JBColor(new Color(128, 128, 128, 65), new Color(128, 128, 128, 65)); - - private static final Key INDENTS_PASS_DATA_KEY = Key.create("INDENTS_PASS_DATA_KEY"); - - /** - * When this debugging flag is true, problematic text ranges are reported. - */ - private final static boolean DEBUG_WIDGET_INDENTS = false; - - private static class WidgetCustomHighlighterRenderer implements CustomHighlighterRenderer { - @NotNull private final WidgetIndentGuideDescriptor descriptor; - @NotNull private final Document document; - - private boolean isSelected = false; - - WidgetCustomHighlighterRenderer(@NotNull WidgetIndentGuideDescriptor descriptor, @NotNull Document document) { - this.descriptor = descriptor; - this.document = document; - - descriptor.trackLocations(document); - } - - void dispose() { - // Descriptors must be disposed so they stop getting notified about - // changes to the Editor. - descriptor.dispose(); - } - - boolean setSelection(boolean value) { - if (value == isSelected) return false; - isSelected = value; - return true; - } - - boolean updateSelected(@NotNull Editor editor, @NotNull RangeHighlighter highlighter, Caret carat) { - if (carat == null) { - return setSelection(false); - } - final CaretModel caretModel = editor.getCaretModel(); - final int startOffset = highlighter.getStartOffset(); - final Document doc = highlighter.getDocument(); - final int caretOffset = carat.getOffset(); - - if (startOffset < 0 || startOffset >= doc.getTextLength()) { - return setSelection(false); - } - - final int endOffset = highlighter.getEndOffset(); - - int off; - int startLine = doc.getLineNumber(startOffset); - { - final CharSequence chars = doc.getCharsSequence(); - do { - final int start = doc.getLineStartOffset(startLine); - final int end = doc.getLineEndOffset(startLine); - off = CharArrayUtil.shiftForward(chars, start, end, " \t"); - startLine--; - } - while (startLine > 1 && off < doc.getTextLength() && chars.charAt(off) == '\n'); - } - - final VisualPosition startPosition = editor.offsetToVisualPosition(off); - final int indentColumn = startPosition.column; - - final LogicalPosition logicalPosition = caretModel.getLogicalPosition(); - if (logicalPosition.line == startLine + 1 && descriptor.widget != null) { - // Be more permissive about what constitutes selection for the first - // line within a widget constructor. - return setSelection(caretModel.getLogicalPosition().column >= indentColumn); - } - return setSelection( - caretOffset >= off && caretOffset < endOffset && caretModel.getLogicalPosition().column == indentColumn); - } - - @Override - public void paint(@NotNull Editor editor, @NotNull RangeHighlighter highlighter, @NotNull Graphics g) { - if (!highlighter.isValid()) { - return; - } - if (!descriptor.widget.isValid()) { - return; - } - final FlutterSettings settings = FlutterSettings.getInstance(); - - final Graphics2D g2d = (Graphics2D)g.create(); - // Required to render colors with an alpha channel. Rendering with an - // alpha chanel makes it easier to keep relationships between shadows - // and lines looking consistent when the background color changes such - // as in the case of selection or a different highlighter turning the - // background yellow. - g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1)); - - final int startOffset = highlighter.getStartOffset(); - final Document doc = highlighter.getDocument(); - final int textLength = doc.getTextLength(); - if (startOffset >= textLength) return; - - final int endOffset = min(highlighter.getEndOffset(), textLength); - - int off; - int startLine = doc.getLineNumber(startOffset); - - final CharSequence chars = doc.getCharsSequence(); - do { - final int start = doc.getLineStartOffset(startLine); - final int end = doc.getLineEndOffset(startLine); - off = CharArrayUtil.shiftForward(chars, start, end, " \t"); - startLine--; - } - while (startLine > 1 && off < doc.getTextLength() && chars.charAt(off) == '\n'); - - final VisualPosition startPosition = editor.offsetToVisualPosition(off); - int indentColumn = startPosition.column; - - // It's considered that indent guide can cross not only white space but comments, javadoc etc. Hence, there is a possible - // case that the first indent guide line is, say, single-line comment where comment symbols ('//') are located at the first - // visual column. We need to calculate correct indent guide column then. - int lineShift = 1; - if (indentColumn <= 0) { - indentColumn = descriptor.indentLevel; - lineShift = 0; - } - if (indentColumn <= 0) return; - - final FoldingModel foldingModel = editor.getFoldingModel(); - if (foldingModel.isOffsetCollapsed(off)) return; - - final FoldRegion headerRegion = foldingModel.getCollapsedRegionAtOffset(doc.getLineEndOffset(doc.getLineNumber(off))); - final FoldRegion tailRegion = foldingModel.getCollapsedRegionAtOffset(doc.getLineStartOffset(doc.getLineNumber(endOffset))); - - if (tailRegion != null && tailRegion == headerRegion) return; - - final CaretModel caretModel = editor.getCaretModel(); - final int caretOffset = caretModel.getOffset(); - // updateSelected(editor, highlighter, caretOffset); - final boolean selected = isSelected; - - final Point start = editor.visualPositionToXY(new VisualPosition(startPosition.line + lineShift, indentColumn)); - - final VisualPosition endPosition = editor.offsetToVisualPosition(endOffset); - final ArrayList childLines = descriptor.childLines; - final Point end = editor.visualPositionToXY(endPosition); - double splitY = -1; - int maxY = end.y; - boolean includeLastLine = endPosition.line == editor.offsetToVisualPosition(doc.getTextLength()).line; - - int endLine = doc.getLineNumber(endOffset); - if (childLines != null && !childLines.isEmpty()) { - final VisualPosition endPositionLastChild = editor.offsetToVisualPosition(childLines.get(childLines.size() - 1).getGuideOffset()); - if (endPositionLastChild.line == endPosition.line) { - // The last child is on the same line as the end of the block. - // This happens if code wasn't formatted with flutter style, for example: - // Center( - // child: child); - - includeLastLine = true; - // TODO(jacobr): make sure we don't run off the edge of the document. - if ((endLine + 1) < document.getLineCount()) { - endLine++; - } - } - } - // By default we stop at the start of the last line instead of the end of the last line in the range. - if (includeLastLine) { - maxY += editor.getLineHeight(); - } - - final Rectangle clip = g2d.getClipBounds(); - if (clip != null) { - if (clip.y > maxY || clip.y + clip.height < start.y) { - return; - } - maxY = min(maxY, clip.y + clip.height); - } - - final EditorColorsScheme scheme = editor.getColorsScheme(); - final JBColor lineColor = selected ? JBColor.BLUE : OUTLINE_LINE_COLOR; - g2d.setColor(lineColor); - final Color pastBlockColor = selected ? scheme.getColor(EditorColors.SELECTED_INDENT_GUIDE_COLOR) : OUTLINE_LINE_COLOR_PAST_BLOCK; - - // TODO(jacobr): this logic for softwraps is duplicated for the FilteredIndentsHighlightingPass - // and may be more conservative than sensible for WidgetIndents. - - // There is a possible case that indent line intersects soft wrap-introduced text. Example: - // this is a long line - // that| is soft-wrapped - // | - // | <- vertical indent - // - // Also it's possible that no additional intersections are added because of soft wrap: - // this is a long line - // | that is soft-wrapped - // | - // | <- vertical indent - // We want to use the following approach then: - // 1. Show only active indent if it crosses soft wrap-introduced text; - // 2. Show indent as is if it doesn't intersect with soft wrap-introduced text; - - int y = start.y; - int newY = start.y; - final int maxYWithChildren = y; - final SoftWrapModel softWrapModel = editor.getSoftWrapModel(); - final int lineHeight = editor.getLineHeight(); - int iChildLine = 0; - for (int i = max(0, startLine + lineShift); i < endLine && newY < maxY; i++) { - OutlineLocation childLine = null; - if (childLines != null) { - while (iChildLine < childLines.size()) { - final OutlineLocation currentChildLine = childLines.get(iChildLine); - if (currentChildLine.isValid()) { - if (currentChildLine.getLine() > i) { - // We haven't reached child line yet. - break; - } - if (currentChildLine.getLine() == i) { - childLine = currentChildLine; - iChildLine++; - if (iChildLine >= childLines.size()) { - splitY = newY + (lineHeight * 0.5); - } - break; - } - } - iChildLine++; - } - - if (childLine != null) { - final int childIndent = childLine.getIndent(); - // Draw horizontal line to the child. - final GuidelineOffsetDetail guidelineOffsetDetail = getGuidelineOffsetDetail(childLine, editor, doc, chars); - final VisualPosition widgetVisualPosition = editor.offsetToVisualPosition(guidelineOffsetDetail.textStartOffset); - final Point widgetPoint = editor.visualPositionToXY(widgetVisualPosition); - - final int deltaX = widgetPoint.x - start.x; - // We add a larger amount of panding at the end of the line if the indent is larger up until a max of 6 pixels which is the max - // amount that looks reasonable. We could remove this and always used a fixed padding. - final int padding = max(min(abs(deltaX) / 3, 6), 2); - if (deltaX > 0) { - // This is the normal case where we draw a foward line to the connected child. - LinePainter2D.paint( - g2d, - start.x + INDENT_GUIDE_DELTA, - newY + lineHeight * 0.5, - //start.x + charWidth * childIndent - padding, - widgetPoint.x - padding, - newY + lineHeight * 0.5 - ); - } - else { - // If there are other characters on the same line as the widget, - // avoid drawing a backwards line. - if (guidelineOffsetDetail.textStartOffset != guidelineOffsetDetail.offset) { - return; - } - // Edge case where we draw a backwards line to clarify - // that the node is still a child even though the line is in - // the wrong direction. This is mainly for debugging but could help - // users fix broken UI. - // We draw this line so it is inbetween the lines of text so it - // doesn't get in the way. - final int loopBackLength = 6; - - // int endX = start.x + charWidth * (childIndent -1) - padding - loopBackLength; - final int endX = widgetPoint.x - padding; - LinePainter2D.paint( - g2d, - start.x + INDENT_GUIDE_DELTA, - newY, - endX, - newY - ); - LinePainter2D.paint( - g2d, - endX, - newY, - endX, - newY + lineHeight * 0.5 - ); - LinePainter2D.paint( - g2d, - endX, - newY + lineHeight * 0.5, - endX + loopBackLength, - newY + lineHeight * 0.5 - ); - } - } - } - - final FoldRegion foldRegion = foldingModel.getCollapsedRegionAtOffset(doc.getLineEndOffset(i)); - if (foldRegion != null && foldRegion.getEndOffset() < doc.getTextLength()) { - i = doc.getLineNumber(foldRegion.getEndOffset()) - 1; - continue; - } - - final List softWraps = softWrapModel.getSoftWrapsForLine(i); - int logicalLineHeight = softWraps.size() * lineHeight; - if (i > startLine + lineShift) { - logicalLineHeight += lineHeight; // We assume that initial 'y' value points just below the target line. - } - if (!softWraps.isEmpty() && softWraps.get(0).getIndentInColumns() < indentColumn) { - if (y < newY || i > startLine + lineShift) { // There is a possible case that soft wrap is located on indent start line. - drawVerticalLineHelper(g2d, lineColor, start.x, y, newY + lineHeight, childLines); - } - newY += logicalLineHeight; - y = newY; - } - else { - newY += logicalLineHeight; - } - - } - - if (childLines != null && iChildLine < childLines.size() && splitY == -1) { - // Clipped rectangle is all within the main body. - splitY = maxY; - } - if (y < maxY) { - if (splitY != -1) { - drawVerticalLineHelper(g2d, lineColor, start.x, y, splitY, childLines); - g2d.setColor(pastBlockColor); - g2d.drawLine(start.x + INDENT_GUIDE_DELTA, (int)splitY + 1, start.x + INDENT_GUIDE_DELTA, maxY); - } - else { - g2d.setColor(pastBlockColor); - g2d.drawLine(start.x + INDENT_GUIDE_DELTA, y, start.x + INDENT_GUIDE_DELTA, maxY); - } - } - g2d.dispose(); - } - - private static class GuidelineOffsetDetail { - // This is the offset of the widget. - final int offset; - // This is the offset of the start of text in the line of the widget (which may differ from offset). - final int textStartOffset; - - GuidelineOffsetDetail(int offset, int textStartOffset) { - this.offset = offset; - this.textStartOffset = textStartOffset; - } - } - - private GuidelineOffsetDetail getGuidelineOffsetDetail(OutlineLocation childLine, - Editor editor, - Document doc, - CharSequence chars) { - // This additional point is computed because sometimes there are other - // characters on a line before the widget, e.g. if a widget has been moved - // to a line with other code but the new outline has not been received yet - // (see issue #4297). We want to compute the earliest position to avoid - // drawing a line through any characters before the widget if they're - // present. - final int startIndex = doc.getLineStartOffset(childLine.getLine()); - final int endIndex = doc.getLineEndOffset(childLine.getLine()); - int firstCharPosition = 0; - while (startIndex + firstCharPosition < endIndex && Character.isWhitespace(chars.charAt(startIndex + firstCharPosition))) { - firstCharPosition += 1; - } - - return new GuidelineOffsetDetail( - childLine.getGuideOffset(), - editor.logicalPositionToOffset(new LogicalPosition(childLine.getLine(), firstCharPosition)) - ); - } - } - - private final EditorEx myEditor; - private final Document myDocument; - private final Project myProject; - private final VirtualFile myFile; - private final boolean convertOffsets; - private final PsiFile psiFile; - private final EditorMouseEventService editorEventService; - private final WidgetEditingContext context; - - WidgetIndentsHighlightingPass( - @NotNull Project project, - @NotNull EditorEx editor, - boolean convertOffsets, - FlutterDartAnalysisServer flutterDartAnalysisService, - InspectorGroupManagerService inspectorGroupManagerService, - EditorMouseEventService editorEventService, - EditorPositionService editorPositionService - ) { - this.myDocument = editor.getDocument(); - this.myEditor = editor; - this.myProject = project; - this.myFile = editor.getVirtualFile(); - this.convertOffsets = convertOffsets; - this.editorEventService = editorEventService; - context = new WidgetEditingContext( - project, flutterDartAnalysisService, inspectorGroupManagerService, editorPositionService); - psiFile = PsiDocumentManager.getInstance(myProject).getPsiFile(myDocument); - final WidgetIndentsPassData data = getIndentsPassData(); - setIndentsPassData(editor, data); - } - - private static void drawVerticalLineHelper( - Graphics2D g, - Color lineColor, - int x, - double yStart, - double yEnd, - ArrayList childLines - ) { - g.setColor(lineColor); - g.drawLine(x + INDENT_GUIDE_DELTA, (int)yStart, x + INDENT_GUIDE_DELTA, (int)yEnd + 1); - } - - public static int compare(@NotNull TextRangeDescriptorPair r, @NotNull RangeHighlighter h) { - int answer = r.range.getStartOffset() - h.getStartOffset(); - if (answer != 0) { - return answer; - } - answer = r.range.getEndOffset() - h.getEndOffset(); - if (answer != 0) { - return answer; - } - final CustomHighlighterRenderer renderer = h.getCustomRenderer(); - if (renderer instanceof WidgetCustomHighlighterRenderer widgetRenderer) { - return widgetRenderer.descriptor.compareTo(r.descriptor); - } - return -1; - } - - /** - * Indent guides are hidden if they overlap with a widget indent guide. - */ - public static boolean isIndentGuideHidden(@NotNull Editor editor, @NotNull LineRange lineRange) { - final WidgetIndentsPassData data = getIndentsPassData(editor); - return data != null && isIndentGuideHidden(data.hitTester, lineRange); - } - - public static boolean isIndentGuideHidden(WidgetIndentHitTester hitTester, @NotNull LineRange lineRange) { - return hitTester != null && hitTester.intersects(lineRange); - } - - public static void onCaretPositionChanged(EditorEx editor, Caret caret) { - final WidgetIndentsPassData data = getIndentsPassData(editor); - if (data == null || data.highlighters == null) return; - for (RangeHighlighter h : data.highlighters) { - if (h.getCustomRenderer() instanceof WidgetCustomHighlighterRenderer renderer) { - final boolean changed = renderer.updateSelected(editor, h, caret); - if (changed) { - editor.repaint(h.getStartOffset(), h.getEndOffset()); - } - } - } - } - - private static WidgetIndentsPassData getIndentsPassData(Editor editor) { - if (editor == null) return null; - return editor.getUserData(INDENTS_PASS_DATA_KEY); - } - - public static void disposeHighlighter(RangeHighlighter highlighter) { - final CustomHighlighterRenderer renderer = highlighter.getCustomRenderer(); - if (renderer instanceof WidgetCustomHighlighterRenderer) { - ((WidgetCustomHighlighterRenderer)renderer).dispose(); - } - highlighter.dispose(); - } - - public static void cleanupHighlighters(Editor editor) { - final WidgetIndentsPassData data = getIndentsPassData(editor); - if (data == null) return; - - final List oldHighlighters = data.highlighters; - if (oldHighlighters != null) { - for (RangeHighlighter highlighter : oldHighlighters) { - disposeHighlighter(highlighter); - } - } - setIndentsPassData(editor, null); - } - - public static void run(@NotNull Project project, - @NotNull EditorEx editor, - @NotNull FlutterOutline outline, - FlutterDartAnalysisServer flutterDartAnalysisService, - InspectorGroupManagerService inspectorGroupManagerService, - EditorMouseEventService editorEventService, - EditorPositionService editorPositionService, - boolean convertOffsets - ) { - final WidgetIndentsHighlightingPass widgetIndentsHighlightingPass = new WidgetIndentsHighlightingPass( - project, - editor, - convertOffsets, - flutterDartAnalysisService, - inspectorGroupManagerService, - editorEventService, - editorPositionService - ); - widgetIndentsHighlightingPass.setOutline(outline); - } - - /** - * This method must be called on the main UI thread. - *

- * Some of this logic would appear to be safe to call on a background thread but - * there are race conditions where the data will be out of order if the document - * is being edited while the code is executing. - *

- * If there are performance concerns we can work to perform more of this - * computation on a separate thread. - */ - public void setOutline(FlutterOutline outline) { - assert (outline != null); - - final WidgetIndentsPassData data = getIndentsPassData(); - if (data.outline == outline) { - // The outline has not changed. There is nothing we need to do. - return; - } - - final ArrayList descriptors = new ArrayList<>(); - - buildWidgetDescriptors(descriptors, outline, null); - updateHitTester(new WidgetIndentHitTester(descriptors, myDocument), data); - // TODO(jacobr): we need to trigger a rerender of highlighters that will render differently due to the changes in highlighters? - data.myDescriptors = descriptors; - doCollectInformationUpdateOutline(data); - doApplyIndentInformationToEditor(data); - setIndentsPassData(data); - updatePreviewHighlighter(myEditor.getMarkupModel(), data); - } - - private void updateHitTester(WidgetIndentHitTester hitTester, WidgetIndentsPassData data) { - if (Objects.equals(data.hitTester, hitTester)) { - return; - } - FilteredIndentsHighlightingPass.onWidgetIndentsChanged(myEditor, data.hitTester, hitTester); - data.hitTester = hitTester; - } - - private WidgetIndentsPassData getIndentsPassData() { - WidgetIndentsPassData data = getIndentsPassData(myEditor); - if (data == null) { - data = new WidgetIndentsPassData(); - } - return data; - } - - static void setIndentsPassData(Editor editor, WidgetIndentsPassData data) { - editor.putUserData(INDENTS_PASS_DATA_KEY, data); - } - - void setIndentsPassData(WidgetIndentsPassData data) { - setIndentsPassData(myEditor, data); - } - - public void doCollectInformationUpdateOutline(WidgetIndentsPassData data) { - assert myDocument != null; - - if (data.myDescriptors != null) { - final ArrayList ranges = new ArrayList<>(); - for (WidgetIndentGuideDescriptor descriptor : data.myDescriptors) { - ProgressManager.checkCanceled(); - final TextRange range; - if (descriptor.widget != null) { - range = descriptor.widget.getFullRange(); - } - else { - final int endOffset = - descriptor.endLine < myDocument.getLineCount() ? myDocument.getLineStartOffset(descriptor.endLine) : myDocument.getTextLength(); - range = new TextRange(myDocument.getLineStartOffset(descriptor.startLine), endOffset); - } - // TODO(jacobr): calling trackLocations multiple times is harmless - // but we should still audit where we are calling it so that we - // only call it once on each descriptor for typical use cases. - descriptor.trackLocations(myDocument); - ranges.add(new TextRangeDescriptorPair(range, descriptor)); - } - ranges.sort((a, b) -> Segment.BY_START_OFFSET_THEN_END_OFFSET.compare(a.range, b.range)); - data.myRangesWidgets = ranges; - } - } - - public void doApplyIndentInformationToEditor(WidgetIndentsPassData data) { - final MarkupModel mm = myEditor.getMarkupModel(); - - final List oldHighlighters = data.highlighters; - final List newHighlighters = new ArrayList<>(); - - int curRange = 0; - - final List ranges = data.myRangesWidgets; - if (oldHighlighters != null) { - // after document change some range highlighters could have become - // invalid, or the order could have been broken. - // This is similar to logic in FilteredIndentsHighlightingPass.java that also attempts to - // only update highlighters that have actually changed. - oldHighlighters.sort(Comparator.comparing((RangeHighlighter h) -> !h.isValid()) - .thenComparing(Segment.BY_START_OFFSET_THEN_END_OFFSET)); - int curHighlight = 0; - // It is fine if we cleanupHighlighters and update some old highlighters that are - // still valid but it is not ok if we leave even one highlighter that - // really changed as that will cause rendering artifacts. - while (curRange < ranges.size() && curHighlight < oldHighlighters.size()) { - final TextRangeDescriptorPair entry = ranges.get(curRange); - final RangeHighlighter highlighter = oldHighlighters.get(curHighlight); - - if (!highlighter.isValid()) break; - - final int cmp = compare(entry, highlighter); - if (cmp < 0) { - newHighlighters.add(createHighlighter(mm, entry, data)); - curRange++; - } - else if (cmp > 0) { - disposeHighlighter(highlighter); - curHighlight++; - } - else { - newHighlighters.add(highlighter); - curHighlight++; - curRange++; - } - } - - for (; curHighlight < oldHighlighters.size(); curHighlight++) { - final RangeHighlighter highlighter = oldHighlighters.get(curHighlight); - if (!highlighter.isValid()) break; - disposeHighlighter(highlighter); - } - } - - - final int startRangeIndex = curRange; - assert myDocument != null; - DocumentUtil.executeInBulk(myDocument, ranges.size() > 10000, () -> { - for (int i = startRangeIndex; i < ranges.size(); i++) { - newHighlighters.add(createHighlighter(mm, ranges.get(i), data)); - } - }); - - data.highlighters = newHighlighters; - } - - private DartAnalysisServerService getAnalysisService() { - return DartAnalysisServerService.getInstance(myProject); - } - - /** - * All calls to convert offsets for indent highlighting must go through this method. - *

- * Sometimes we need to use the raw offsets and sometimes we need - * to use the converted offsets depending on whether the FlutterOutline - * matches the current document or the expectations given by the - * - * @param node the FlutterOutline to retreive the offset for - */ - int getConvertedOffset(FlutterOutline node) { - return getConvertedOffset(node.getOffset()); - } - - int getConvertedOffset(int offset) { - return convertOffsets ? getAnalysisService().getConvertedOffset(myFile, offset) : offset; - } - - private OutlineLocation computeLocation(FlutterOutline node) { - assert (myDocument != null); - final int documentLength = myDocument.getTextLength(); - final int rawOffset = getConvertedOffset(node); - final int nodeOffset = min(rawOffset, documentLength); - final int line = myDocument.getLineNumber(nodeOffset); - final int lineStartOffset = myDocument.getLineStartOffset(line); - - final int column = nodeOffset - lineStartOffset; - final CharSequence chars = myDocument.getCharsSequence(); - int indent; - - // TODO(jacobr): we only really want to include the previous token (e.g. - // "child: " instead of the entire line). That won't matter much but could - // lead to slightly better results on code edits. - for (indent = 0; indent < column; indent++) { - if (!Character.isWhitespace(chars.charAt(lineStartOffset + indent))) { - break; - } - } - - return new OutlineLocation(node, line, column, indent, myFile, this); - } - - DartCallExpression getCallExpression(PsiElement element) { - if (element == null) { - return null; - } - if (element instanceof DartCallExpression) { - return (DartCallExpression)element; - } - - return getCallExpression(element.getParent()); - } - - private void buildWidgetDescriptors( - final List widgetDescriptors, - FlutterOutline outlineNode, - WidgetIndentGuideDescriptor parent - ) { - if (outlineNode == null) return; - - final String kind = outlineNode.getKind(); - final boolean widgetConstructor = "NEW_INSTANCE".equals(kind) || (parent != null && ("VARIABLE".equals(kind))); - - final List children = outlineNode.getChildren(); - if (children == null || children.isEmpty()) return; - - if (widgetConstructor) { - final OutlineLocation location = computeLocation(outlineNode); - int minChildIndent = Integer.MAX_VALUE; - final ArrayList childrenLocations = new ArrayList<>(); - int endLine = location.getLine(); - - for (FlutterOutline child : children) { - final OutlineLocation childLocation = computeLocation(child); - if (childLocation.getLine() <= location.getLine()) { - // Skip children that don't actually occur on a later line. There is no - // way for us to draw good looking line art for them. - // TODO(jacobr): consider adding these children anyway so we can render - // them if there are edits and they are now properly formatted. - continue; - } - - minChildIndent = min(minChildIndent, childLocation.getIndent()); - endLine = max(endLine, childLocation.getLine()); - childrenLocations.add(childLocation); - } - final Set childrenOffsets = new HashSet<>(); - for (OutlineLocation childLocation : childrenLocations) { - childrenOffsets.add(childLocation.getGuideOffset()); - } - - final PsiElement element = psiFile.findElementAt(location.getGuideOffset()); - if (!childrenLocations.isEmpty()) { - // The indent is only used for sorting and disambiguating descriptors - // as at render time we will pick the real indent for the outline based - // on local edits that may have been made since the outline was computed. - final int lineIndent = location.getIndent(); - final WidgetIndentGuideDescriptor descriptor = new WidgetIndentGuideDescriptor( - parent, - lineIndent, - location.getLine(), - endLine + 1, - childrenLocations, - location, - outlineNode - ); - if (!descriptor.childLines.isEmpty()) { - widgetDescriptors.add(descriptor); - parent = descriptor; - } - } - } - for (FlutterOutline child : children) { - buildWidgetDescriptors(widgetDescriptors, child, parent); - } - } - - @NotNull - private RangeHighlighter createHighlighter(MarkupModel mm, TextRangeDescriptorPair entry, WidgetIndentsPassData data) { - final TextRange range = entry.range; - final FlutterSettings settings = FlutterSettings.getInstance(); - if (range.getEndOffset() >= myDocument.getTextLength() && DEBUG_WIDGET_INDENTS) { - LOG.info("Warning: highlighter extends past the end of document."); - } - final RangeHighlighter highlighter = mm.addRangeHighlighter( - Math.max(range.getStartOffset(), 0), - Math.min(range.getEndOffset(), myDocument.getTextLength()), - HighlighterLayer.FIRST, - null, - HighlighterTargetArea.EXACT_RANGE - ); - highlighter.setCustomRenderer(new WidgetCustomHighlighterRenderer(entry.descriptor, myDocument)); - return highlighter; - } - - private void updatePreviewHighlighter(MarkupModel mm, WidgetIndentsPassData data) { - final FlutterSettings settings = FlutterSettings.getInstance(); - if (!settings.isEnableHotUiInCodeEditor()) return; - - if (data.previewsForEditor == null && myEditor instanceof EditorImpl) { - // TODO(jacobr): is there a way to get access to a disposable that will - // trigger when the editor disposes than casting to EditorImpl? - - final Disposable parentDisposable = ((EditorImpl)myEditor).getDisposable(); - - final TextRange range = new TextRange(0, Integer.MAX_VALUE); - final RangeHighlighter highlighter = - mm.addRangeHighlighter( - 0, - myDocument.getTextLength(), - HighlighterLayer.FIRST, - null, - HighlighterTargetArea.LINES_IN_RANGE - ); - data.previewsForEditor = new PreviewsForEditor(context, editorEventService, myEditor, parentDisposable); - highlighter.setCustomRenderer(data.previewsForEditor); - } - if (data.previewsForEditor != null) { - data.previewsForEditor.outlinesChanged(data.myDescriptors); - } - } -} - -class TextRangeDescriptorPair { - @NotNull final TextRange range; - @NotNull final WidgetIndentGuideDescriptor descriptor; - - TextRangeDescriptorPair(@NotNull TextRange range, @NotNull WidgetIndentGuideDescriptor descriptor) { - this.range = range; - this.descriptor = descriptor; - } -} diff --git a/flutter-idea/src/io/flutter/editor/WidgetIndentsHighlightingPassFactory.java b/flutter-idea/src/io/flutter/editor/WidgetIndentsHighlightingPassFactory.java deleted file mode 100644 index 7ded08e7c3..0000000000 --- a/flutter-idea/src/io/flutter/editor/WidgetIndentsHighlightingPassFactory.java +++ /dev/null @@ -1,323 +0,0 @@ -/* - * Copyright 2019 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -package io.flutter.editor; - -import com.intellij.application.options.editor.EditorOptionsListener; -import com.intellij.codeHighlighting.Pass; -import com.intellij.codeHighlighting.TextEditorHighlightingPass; -import com.intellij.codeHighlighting.TextEditorHighlightingPassFactory; -import com.intellij.codeHighlighting.TextEditorHighlightingPassRegistrar; -import com.intellij.openapi.Disposable; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.editor.EditorFactory; -import com.intellij.openapi.editor.event.CaretEvent; -import com.intellij.openapi.editor.event.CaretListener; -import com.intellij.openapi.editor.event.EditorEventMulticaster; -import com.intellij.openapi.editor.ex.DocumentEx; -import com.intellij.openapi.editor.ex.EditorEx; -import com.intellij.openapi.editor.ex.EditorSettingsExternalizable; -import com.intellij.openapi.project.DumbAware; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; -import com.intellij.psi.PsiFile; -import com.intellij.util.ui.EdtInvocationManager; -import com.jetbrains.lang.dart.analyzer.DartAnalysisServerService; -import io.flutter.FlutterUtils; -import io.flutter.dart.FlutterDartAnalysisServer; -import io.flutter.inspector.InspectorGroupManagerService; -import io.flutter.inspector.InspectorService; -import io.flutter.settings.FlutterSettings; -import org.dartlang.analysis.server.protocol.FlutterOutline; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Objects; - -/** - * Factory that drives all rendering of widget indents. - *

- * Warning: it is unsafe to register the WidgetIndentsHighlightingPassFactory - * without using WidgetIndentsHighlightingPassFactoryRegistrar as recent - * versions of IntelliJ will unpredictably clear out all existing highlighting - * pass factories and then rerun all registrars. - */ -public class WidgetIndentsHighlightingPassFactory implements TextEditorHighlightingPassFactory, Disposable, DumbAware { - // This is a debugging flag to track down bugs that are hard to spot if - // analysis server updates are occuring at their normal rate. If there are - // bugs, With this flag on you should be able to spot flickering or invalid - // widget indent guide lines after editing code containing guides. - private static final boolean SIMULATE_SLOW_ANALYSIS_UPDATES = false; - - @NotNull private final Project project; - private final FlutterSettings.Listener settingsListener; - private final ActiveEditorsOutlineService.Listener outlineListener; - protected InspectorService inspectorService; - - // Current configuration settings used to display Widget Indent Guides cached from the FlutterSettings class. - private boolean isShowBuildMethodGuides; - // Current configuration setting used to display regular indent guides cached from EditorSettingsExternalizable. - private boolean isIndentGuidesShown; - - @Nullable - public static WidgetIndentsHighlightingPassFactory getInstance(Project project) { - return project.getService(WidgetIndentsHighlightingPassFactory.class); - } - - public WidgetIndentsHighlightingPassFactory(@NotNull Project project) { - this.project = project; - // TODO(jacobr): I'm not clear which Disposable it is best to tie the - // lifecycle of this object. The FlutterDartAnalysisServer is chosen at - // random as a Disposable with generally the right lifecycle. IntelliJ - // returns a lint warning if you tie the lifecycle to the Project. - this.settingsListener = this::onSettingsChanged; - this.outlineListener = this::updateEditor; - syncSettings(FlutterSettings.getInstance()); - FlutterSettings.getInstance().addListener(settingsListener); - isIndentGuidesShown = EditorSettingsExternalizable.getInstance().isIndentGuidesShown(); - ApplicationManager.getApplication().getMessageBus().connect(this).subscribe( - EditorOptionsListener.APPEARANCE_CONFIGURABLE_TOPIC, () -> { - final boolean newValue = EditorSettingsExternalizable.getInstance().isIndentGuidesShown(); - if (isIndentGuidesShown != newValue) { - isIndentGuidesShown = newValue; - updateAllEditors(); - } - }); - - final EditorEventMulticaster eventMulticaster = EditorFactory.getInstance().getEventMulticaster(); - eventMulticaster.addCaretListener(new CaretListener() { - @Override - public void caretPositionChanged(@NotNull CaretEvent event) { - final Editor editor = event.getEditor(); - if (editor.getProject() != project) return; - if (editor.isDisposed() || project.isDisposed()) return; - if (!(editor instanceof EditorEx editorEx)) return; - WidgetIndentsHighlightingPass.onCaretPositionChanged(editorEx, event.getCaret()); - } - }, this); - getEditorOutlineService().addListener(outlineListener); - } - - @NotNull - private FlutterDartAnalysisServer getAnalysisService() { - return FlutterDartAnalysisServer.getInstance(project); - } - - @NotNull - private InspectorGroupManagerService getInspectorGroupManagerService() { - return InspectorGroupManagerService.getInstance(project); - } - - @NotNull - private EditorMouseEventService getEditorEventService() { - return EditorMouseEventService.getInstance(project); - } - - @NotNull - private EditorPositionService getEditorPositionService() { - return EditorPositionService.getInstance(project); - } - - @NotNull - private ActiveEditorsOutlineService getEditorOutlineService() { - return ActiveEditorsOutlineService.getInstance(project); - } - - /** - * Updates all editors if the settings have changed. - *

- * This is useful for adding the guides in after they were turned on from the settings menu. - */ - private void syncSettings(FlutterSettings settings) { - if (isShowBuildMethodGuides != settings.isShowBuildMethodGuides()) { - isShowBuildMethodGuides = settings.isShowBuildMethodGuides(); - updateAllEditors(); - } - } - - /** - * Updates the indent guides in the editor for the file at {@param path}. - */ - private void updateEditor(@NotNull final String path, @Nullable FlutterOutline outline) { - ApplicationManager.getApplication().invokeLater(() -> { - if (project.isDisposed()) { - return; - } - for (EditorEx editor : getEditorOutlineService().getActiveDartEditors()) { - final String filePath = editor.getVirtualFile().getCanonicalPath(); - if (!editor.isDisposed() && Objects.equals(filePath, path)) { - runWidgetIndentsPass(editor, outline); - } - } - }); - } - - // Updates all editors instead of just a specific editor. - private void updateAllEditors() { - ApplicationManager.getApplication().invokeLater(() -> { - // Find visible editors for the path. If the file is not actually - // being displayed on screen, there is no need to go through the - // work of updating the outline. - if (project.isDisposed()) { - return; - } - for (EditorEx editor : getEditorOutlineService().getActiveDartEditors()) { - if (!editor.isDisposed()) { - runWidgetIndentsPass(editor, getEditorOutlineService().getOutline(editor.getVirtualFile().getCanonicalPath())); - } - } - }); - } - - @Nullable - @Override - public TextEditorHighlightingPass createHighlightingPass(@NotNull PsiFile file, @NotNull Editor e) { - // Surprisingly, the highlighting pass returned by this method isn't the - // highlighting pass to display the indent guides. It is the highlighting - // pass that display regular indent guides for Dart files that filters out - // indent guides that intersect with the widget indent guides. because - // the widget indent guides are powered by the Dart Analyzer, computing the - // widget indent guides themselves must be done asynchronously driven by - // analysis server updates not IntelliJ's default assumptions about how a - // text highlighting pass should work. See runWidgetIndentsPass for the - // logic that handles the actual widget indent guide pass. - if (file.getVirtualFile() == null) return null; - if (!FlutterUtils.isDartFile(file.getVirtualFile())) { - return null; - } - - if (!isShowBuildMethodGuides) { - ApplicationManager.getApplication().invokeLater(() -> { - // Reset the editor back to its default indent guide setting as build - // method guides are disabled and the file is a dart file. - e.getSettings().setIndentGuidesShown(isIndentGuidesShown); - // Cleanup custom filtered build method guides that may be left around - // from when our custom filtered build method guides were previously - // shown. This cleanup is very cheap if it has already been performed - // so there is no harm in performing it more than once. - FilteredIndentsHighlightingPass.cleanupHighlighters(e); - }); - return null; - } - // If we are showing build method guides we can never show the regular - // IntelliJ indent guides for a file because they will overlap with the - // widget indent guides in distracting ways. - if (EdtInvocationManager.getInstance().isEventDispatchThread()) { - e.getSettings().setIndentGuidesShown(false); - } - else { - ApplicationManager.getApplication().invokeLater(() -> { - if (!e.isDisposed()) { - e.getSettings().setIndentGuidesShown(false); - } - }); - } - // If regular indent guides should be shown we need to show the filtered - // indents guides which look like the regular indent guides except that - // guides that intersect with the widget guides are filtered out. - final TextEditorHighlightingPass highlightingPass; - if (isIndentGuidesShown) { - highlightingPass = new FilteredIndentsHighlightingPass(project, e, file); - } - else { - highlightingPass = null; - // The filtered pass might have been shown before in which case we need to clean it up. - // Cleanup custom filtered build method guides that may be left around - // from when our custom filtered build method guides were previously - // shown. This cleanup is very cheap if it has already been performed - // so there is no harm in performing it more than once. - FilteredIndentsHighlightingPass.cleanupHighlighters(e); - } - - if (!(e instanceof EditorEx editor)) return highlightingPass; - - final VirtualFile virtualFile = editor.getVirtualFile(); - if (!FlutterUtils.couldContainWidgets(project, virtualFile)) { - return highlightingPass; - } - final FlutterOutline outline = getEditorOutlineService().getOutline(virtualFile.getCanonicalPath()); - - if (outline != null) { - ApplicationManager.getApplication().invokeLater(() -> runWidgetIndentsPass(editor, outline)); - } - - return highlightingPass; - } - - void runWidgetIndentsPass(EditorEx editor, FlutterOutline outline) { - if (editor.isDisposed() || project.isDisposed()) { - // The editor might have been disposed before we got a new FlutterOutline. - // It is safe to ignore it as it isn't relevant. - return; - } - - if (!isShowBuildMethodGuides || outline == null) { - // If build method guides are disabled or there is no outline to use in this pass, - // then do nothing. - WidgetIndentsHighlightingPass.cleanupHighlighters(editor); - return; - } - final VirtualFile file = editor.getVirtualFile(); - if (!FlutterUtils.couldContainWidgets(project, file)) { - return; - } - // If the editor and the outline have different lengths then - // the outline is out of date and cannot safely be displayed. - final DocumentEx document = editor.getDocument(); - final int documentLength = document.getTextLength(); - final int outlineLength = outline.getLength(); - // TODO(jacobr): determine why we sometimes have to check against both the - // raw outlineLength and the converted outline length for things to work - // correctly on windows. - if (documentLength != outlineLength && - documentLength != DartAnalysisServerService.getInstance(project).getConvertedOffset(file, outlineLength)) { - // Outline is out of date. That is ok. Ignore it for now. - // An up to date outline will probably arrive shortly. Showing an - // outline from data inconsistent with the current - // content will show annoying flicker. It is better to - // instead - return; - } - // We only need to convert offsets when the document and outline disagree - // on the document length. - final boolean convertOffsets = documentLength != outlineLength; - - WidgetIndentsHighlightingPass.run( - project, - editor, - outline, - getAnalysisService(), - getInspectorGroupManagerService(), - getEditorEventService(), - getEditorPositionService(), - convertOffsets - ); - } - - @Override - public void dispose() { - FlutterSettings.getInstance().removeListener(settingsListener); - getEditorOutlineService().removeListener(outlineListener); - } - - void onSettingsChanged() { - if (project.isDisposed()) { - return; - } - final FlutterSettings settings = FlutterSettings.getInstance(); - // Skip if none of the settings that impact Widget Idents were changed. - if (isShowBuildMethodGuides == settings.isShowBuildMethodGuides()) { - // Change doesn't matter for us. - return; - } - syncSettings(settings); - } - - public void registerHighlightingPassFactory(@NotNull TextEditorHighlightingPassRegistrar registrar) { - registrar - .registerTextEditorHighlightingPass(this, TextEditorHighlightingPassRegistrar.Anchor.AFTER, Pass.UPDATE_FOLDING, false, false); - } -} diff --git a/flutter-idea/src/io/flutter/editor/WidgetIndentsHighlightingPassFactoryRegistrar.java b/flutter-idea/src/io/flutter/editor/WidgetIndentsHighlightingPassFactoryRegistrar.java deleted file mode 100644 index 3722a12ae6..0000000000 --- a/flutter-idea/src/io/flutter/editor/WidgetIndentsHighlightingPassFactoryRegistrar.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2020 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -package io.flutter.editor; - -import com.intellij.codeHighlighting.*; -import com.intellij.openapi.project.DumbAware; -import com.intellij.openapi.project.Project; -import io.flutter.utils.FlutterModuleUtils; -import org.jetbrains.annotations.NotNull; - -/** - * Registrar that adds the WidgetIndentsHighlightingPassFactory. - * - * It is now neccessary to register highlighting passes with a registrar to - * ensure that highlighting passes do not unpredictably disappear. - */ -public class WidgetIndentsHighlightingPassFactoryRegistrar implements TextEditorHighlightingPassFactoryRegistrar, DumbAware { - - @Override - public void registerHighlightingPassFactory(@NotNull TextEditorHighlightingPassRegistrar registrar, @NotNull Project project) { - if (FlutterModuleUtils.hasFlutterModule(project)) { - WidgetIndentsHighlightingPassFactory.getInstance(project).registerHighlightingPassFactory(registrar); - } - } -} - diff --git a/flutter-idea/src/io/flutter/editor/WidgetIndentsPassData.java b/flutter-idea/src/io/flutter/editor/WidgetIndentsPassData.java deleted file mode 100644 index 857293eda2..0000000000 --- a/flutter-idea/src/io/flutter/editor/WidgetIndentsPassData.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2019 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -package io.flutter.editor; - -import com.intellij.openapi.editor.markup.RangeHighlighter; -import org.dartlang.analysis.server.protocol.FlutterOutline; - -import java.util.Collections; -import java.util.List; - -/** - * Data describing widget indents for an editor that is persisted across - * multiple runs of the WidgetIndentsHighlightingPass. - */ -public class WidgetIndentsPassData { - public PreviewsForEditor previewsForEditor; - /** - * Descriptors describing the data model to render the widget indents. - *

- * This data is computed from the FlutterOutline and contains additional - * information to manage how the locations need to be updated to reflect - * edits to the documents. - */ - java.util.List myDescriptors = Collections.emptyList(); - - /** - * Descriptors combined with their current locations in the possibly modified document. - */ - java.util.List myRangesWidgets = Collections.emptyList(); - - /** - * Highlighters that perform the actual rendering of the widget indent - * guides. - */ - List highlighters; - - /** - * Source of truth for whether other UI overlaps with the widget indents. - */ - WidgetIndentHitTester hitTester; - - /** - * Outline the widget indents are based on. - */ - FlutterOutline outline; -} diff --git a/flutter-idea/src/io/flutter/editor/WidgetViewController.java b/flutter-idea/src/io/flutter/editor/WidgetViewController.java deleted file mode 100644 index 1138f0f268..0000000000 --- a/flutter-idea/src/io/flutter/editor/WidgetViewController.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright 2019 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -package io.flutter.editor; - -import com.intellij.openapi.Disposable; -import com.intellij.openapi.util.Disposer; -import io.flutter.inspector.DiagnosticsNode; -import io.flutter.inspector.InspectorGroupManagerService; -import io.flutter.inspector.InspectorObjectGroupManager; -import io.flutter.inspector.InspectorService; -import io.flutter.run.daemon.FlutterApp; - -import java.awt.*; -import java.util.ArrayList; - -/** - * Base class for a controller managing UI describing a widget. - *

- * See PreviewViewController which extends this class to render previews of - * widgets. This class is also intented to be extended to support rendering - * visualizations of widget properties inline in a text editor. - */ -public abstract class WidgetViewController implements EditorMouseEventService.Listener, Disposable { - protected boolean isSelected = false; - protected final WidgetViewModelData data; - - protected boolean visible = false; - - Rectangle visibleRect; - DiagnosticsNode inspectorSelection; - - final InspectorGroupManagerService.Client groupClient; - - protected abstract void onFlutterFrame(); - - public InspectorObjectGroupManager getGroupManagner() { - return groupClient.getGroupManager(); - } - - public FlutterApp getApp() { - return groupClient.getApp(); - } - - InspectorObjectGroupManager getGroups() { - return groupClient.getGroupManager(); - } - - public ArrayList elements; - public int activeIndex = 0; - - WidgetViewController(WidgetViewModelData data, Disposable parent) { - this.data = data; - Disposer.register(parent, this); - groupClient = new InspectorGroupManagerService.Client(this) { - @Override - public void onInspectorAvailabilityChanged() { - WidgetViewController.this.onInspectorAvailabilityChanged(); - } - - @Override - public void requestRepaint(boolean force) { - onFlutterFrame(); - } - - @Override - public void onFlutterFrame() { - WidgetViewController.this.onFlutterFrame(); - } - - public void onSelectionChanged(DiagnosticsNode selection) { - WidgetViewController.this.onSelectionChanged(selection); - } - }; - data.context.inspectorGroupManagerService.addListener(groupClient, parent); - } - - /** - * Subclasses can override this method to be notified when whether the widget is visible in IntelliJ. - *

- * This is whether the UI for this component is visible not whether the widget is visible on the device. - */ - public void onVisibleChanged() { - } - - public boolean updateVisiblityLocked(Rectangle newRectangle) { - return false; - } - - public void onInspectorAvailabilityChanged() { - setElements(null); - inspectorSelection = null; - onVisibleChanged(); - forceRender(); - } - - public abstract void forceRender(); - - public InspectorService getInspectorService() { - return groupClient.getInspectorService(); - } - - @Override - public void dispose() { - } - - public void onSelectionChanged(DiagnosticsNode selection) { - - final InspectorObjectGroupManager manager = getGroups(); - if (manager != null) { - manager.cancelNext(); - } - } - - abstract InspectorService.Location getLocation(); - - public boolean isElementsEmpty() { - return elements == null || elements.isEmpty(); - } - - public void setElements(ArrayList elements) { - this.elements = elements; - } - - public void onActiveElementsChanged() { - if (isElementsEmpty()) return; - final InspectorObjectGroupManager manager = getGroups(); - if (manager == null) return; - - if (isSelected) { - manager.getCurrent().setSelection( - getSelectedElement().getValueRef(), - false, - true - ); - } - } - - public DiagnosticsNode getSelectedElement() { - if (isElementsEmpty()) return null; - return elements.get(0); - } -} diff --git a/flutter-idea/src/io/flutter/inspector/TransformedRect.java b/flutter-idea/src/io/flutter/inspector/TransformedRect.java index 9fb79c352f..3ffd5da454 100644 --- a/flutter-idea/src/io/flutter/inspector/TransformedRect.java +++ b/flutter-idea/src/io/flutter/inspector/TransformedRect.java @@ -5,9 +5,7 @@ */ package io.flutter.inspector; -import com.google.gson.JsonArray; import com.google.gson.JsonObject; -import io.flutter.utils.math.Matrix4; import java.awt.geom.Rectangle2D; @@ -26,13 +24,4 @@ public Rectangle2D getRectangle() { json.getAsJsonPrimitive("height").getAsDouble() ); } - - public Matrix4 getTransform() { - final JsonArray data = json.getAsJsonArray("transform"); - final double[] storage = new double[16]; - for (int i = 0; i < 16; i++) { - storage[i] = data.get(i).getAsDouble(); - } - return new Matrix4(storage); - } } diff --git a/flutter-idea/src/io/flutter/preview/ModelUtils.java b/flutter-idea/src/io/flutter/preview/ModelUtils.java index c220f61eb8..f66bb6b579 100644 --- a/flutter-idea/src/io/flutter/preview/ModelUtils.java +++ b/flutter-idea/src/io/flutter/preview/ModelUtils.java @@ -23,28 +23,4 @@ public static boolean isBuildMethod(@NotNull Element element) { return StringUtil.equals("build", element.getName()) && StringUtil.startsWith(element.getParameters(), "(BuildContext "); } - - public static boolean containsBuildMethod(FlutterOutline outline) { - final Element element = outline.getDartElement(); - if (element == null) { - return false; - } - - if (isBuildMethod(element)) { - return true; - } - - final List children = outline.getChildren(); - if (children == null) { - return false; - } - - for (FlutterOutline child : children) { - if (containsBuildMethod(child)) { - return true; - } - } - - return false; - } } diff --git a/flutter-idea/src/io/flutter/preview/PreviewArea.java b/flutter-idea/src/io/flutter/preview/PreviewArea.java deleted file mode 100644 index 0415a8cf44..0000000000 --- a/flutter-idea/src/io/flutter/preview/PreviewArea.java +++ /dev/null @@ -1,176 +0,0 @@ -/* - * Copyright 2019 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -package io.flutter.preview; - -import com.intellij.openapi.Disposable; -import com.intellij.openapi.actionSystem.*; -import com.intellij.openapi.actionSystem.ex.CustomComponentAction; -import com.intellij.openapi.editor.Editor; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.SimpleToolWindowPanel; -import com.intellij.ui.components.JBLabel; -import com.intellij.util.ui.JBUI; -import com.intellij.util.ui.UIUtil; -import io.flutter.dart.FlutterDartAnalysisServer; -import io.flutter.editor.EditorPositionService; -import io.flutter.editor.PreviewViewController; -import io.flutter.editor.WidgetEditingContext; -import io.flutter.editor.WidgetViewModelData; -import io.flutter.inspector.InspectorGroupManagerService; -import io.flutter.inspector.InspectorService; -import org.dartlang.analysis.server.protocol.FlutterOutline; -import org.jetbrains.annotations.NotNull; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.ComponentAdapter; -import java.awt.event.ComponentEvent; -import java.util.List; -import java.util.Set; - -/** - * Class that manages displaying the DeviceMirror. - */ -public class PreviewArea { - public static int BORDER_WIDTH = 0; - - private final DefaultActionGroup toolbarGroup = new DefaultActionGroup(); - private final ActionToolbar windowToolbar; - - private final SimpleToolWindowPanel window; - - private final JLayeredPane layeredPanel = new JLayeredPane(); - private final JPanel deviceMirrorPanel; - - private final PreviewViewController preview; - private final WidgetEditingContext context; - private final Set outlinesWithWidgets; - - private final InspectorGroupManagerService.Client inspectorClient; - - public PreviewArea(Project project, Set outlinesWithWidgets, Disposable parent) { - this.outlinesWithWidgets = outlinesWithWidgets; - - context = new WidgetEditingContext( - project, - FlutterDartAnalysisServer.getInstance(project), - InspectorGroupManagerService.getInstance(project), - EditorPositionService.getInstance(project) - ); - - inspectorClient = new InspectorGroupManagerService.Client(parent); - context.inspectorGroupManagerService.addListener(inspectorClient, parent); - preview = new PreviewViewController(new WidgetViewModelData(context), false, layeredPanel, parent); - - deviceMirrorPanel = new PreviewViewModelPanel(preview); - - windowToolbar = ActionManager.getInstance().createActionToolbar("PreviewArea", toolbarGroup, true); - toolbarGroup.add(new TitleAction("Device Mirror")); - - window = new SimpleToolWindowPanel(true, true); - window.setToolbar(windowToolbar.getComponent()); - - deviceMirrorPanel.setLayout(new BorderLayout()); - - // TODO(jacobr): reafactor to remove the layeredPanel as we aren't getting any benefit from it. - window.setContent(layeredPanel); - layeredPanel.add(deviceMirrorPanel, Integer.valueOf(0)); - - // Layers should cover the whole root panel. - layeredPanel.addComponentListener(new ComponentAdapter() { - @Override - public void componentResized(ComponentEvent e) { - final Dimension renderSize = getRenderSize(); - preview.setScreenshotBounds(new Rectangle(0, 0, renderSize.width, renderSize.height)); - } - }); - } - - /** - * Return the Swing component of the area. - */ - public JComponent getComponent() { - return window; - } - - // TODO(jacobr): switch the previewArea to listen on the event stream of - // selected outlines and editors instead like the PropertyEditPanel - // already does. - public void select(@NotNull List outlines, Editor editor) { - if (editor.isDisposed()) return; - - final InspectorService.ObjectGroup group = getObjectGroup(); - if (group != null && !outlines.isEmpty()) { - final FlutterOutline outline = findWidgetToHighlight(outlines.get(0)); - if (outline == null) return; - final InspectorService.Location location = InspectorService.Location.outlineToLocation(editor, outline); - if (location == null) return; - group.setSelection(location, false, true); - } - } - - /** - * Find the first descendant of the outline that is a widget as long as there - * is only one path down the tree that leeds to a widget. - */ - private FlutterOutline findWidgetToHighlight(FlutterOutline outline) { - if (outline == null || outline.getClassName() != null) return outline; - if (!outlinesWithWidgets.contains(outline)) return null; - FlutterOutline candidate = null; - for (FlutterOutline child : outline.getChildren()) { - if (outlinesWithWidgets.contains(child)) { - if (candidate != null) { - // It is ambiguous which candidate to show so don't show anything. - // TODO(jacobr): consider showing multiple locations instead if the - // inspector on device protocol is enhanced to support that. - return null; - } - candidate = findWidgetToHighlight(child); - } - } - return candidate; - } - - public Dimension getRenderSize() { - final int width = layeredPanel.getWidth(); - final int height = layeredPanel.getHeight(); - for (Component child : layeredPanel.getComponents()) { - child.setBounds(0, 0, width, height); - } - - final int renderWidth = width - 2 * BORDER_WIDTH; - final int renderHeight = height - 2 * BORDER_WIDTH; - return new Dimension(renderWidth, renderHeight); - } - - InspectorService.ObjectGroup getObjectGroup() { - return inspectorClient.getCurrentObjectGroup(); - } -} - -class TitleAction extends AnAction implements CustomComponentAction { - TitleAction(String text) { - super(text); - } - - @Override - public void actionPerformed(@NotNull AnActionEvent event) { - } - - @NotNull - @Override - public JComponent createCustomComponent(@NotNull Presentation presentation) { - final JPanel panel = new JPanel(new BorderLayout()); - - // Add left border to make the title look similar to the tool window title. - panel.setBorder(BorderFactory.createEmptyBorder(0, JBUI.scale(3), 0, 0)); - - final String text = getTemplatePresentation().getText(); - panel.add(new JBLabel(text != null ? text : "", UIUtil.ComponentStyle.SMALL)); - - return panel; - } -} diff --git a/flutter-idea/src/io/flutter/preview/PreviewView.java b/flutter-idea/src/io/flutter/preview/PreviewView.java index 6902a435ef..694d2bc11a 100644 --- a/flutter-idea/src/io/flutter/preview/PreviewView.java +++ b/flutter-idea/src/io/flutter/preview/PreviewView.java @@ -92,7 +92,6 @@ public class PreviewView implements PersistentStateComponent { private Splitter propertyEditSplitter; private JScrollPane scrollPane; private OutlineTree tree; - private @Nullable PreviewArea previewArea; private final Set outlinesWithWidgets = Sets.newHashSet(); private final Map outlineToNodeMap = Maps.newHashMap(); @@ -336,12 +335,6 @@ private void jumpToOutlineInEditor(FlutterOutline outline, boolean focusEditor) currentEditor.getCaretModel().addCaretListener(caretListener); } } - - // TODO(jacobr): refactor the previewArea to listen on the stream of - // selected outlines instead. - if (previewArea != null) { - previewArea.select(ImmutableList.of(outline), currentEditor); - } } // TODO: Add parent relationship info to FlutterOutline instead of this O(n^2) traversal. @@ -409,31 +402,13 @@ private void updateOutline(@NotNull FlutterOutline outline) { @Override public void onInspectorAvailabilityChanged() { super.onInspectorAvailabilityChanged(); - // Only show the screen mirror if there is a running device and - // the inspector supports the neccessary apis. - if (getInspectorService() != null && getInspectorService().isHotUiScreenMirrorSupported()) { - // Wait to create the preview area until it is needed. - if (previewArea == null) { - previewArea = new PreviewArea(project, outlinesWithWidgets, project); - } - propertyEditSplitter.setSecondComponent(previewArea.getComponent()); - } - else { - propertyEditSplitter.setSecondComponent(null); - } + propertyEditSplitter.setSecondComponent(null); } }; inspectorGroupManagerService.addListener(inspectorStateServiceClient, project); splitter.setSecondComponent(propertyEditSplitter); } - - // TODO(jacobr): this is the wrong spot. - if (propertyEditToolbarGroup != null) { - final TitleAction propertyTitleAction = new TitleAction("Properties"); - propertyEditToolbarGroup.removeAll(); - propertyEditToolbarGroup.add(propertyTitleAction); - } } private boolean computeOutlinesWithWidgets(FlutterOutline outline) { @@ -591,12 +566,6 @@ private void applyEditorSelectionToTree(Caret caret) { activeOutlines.setValue(selectedOutlines); applyOutlinesSelectionToTree(selectedOutlines); - - // TODO(jacobr): refactor the previewArea to listen on the stream of - // selected outlines instead. - if (previewArea != null) { - previewArea.select(selectedOutlines, currentEditor); - } } private void applyOutlinesSelectionToTree(List outlines) { diff --git a/flutter-idea/src/io/flutter/preview/PreviewViewModelPanel.java b/flutter-idea/src/io/flutter/preview/PreviewViewModelPanel.java deleted file mode 100644 index 9672d6f2b9..0000000000 --- a/flutter-idea/src/io/flutter/preview/PreviewViewModelPanel.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2019 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -package io.flutter.preview; - -import io.flutter.editor.PreviewViewController; -import io.flutter.editor.PreviewViewControllerBase; - -import javax.swing.*; -import java.awt.*; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import java.awt.event.MouseMotionListener; - -/** - * Class that provides the glue to render the preview view in a regular JPanel. - */ -public class PreviewViewModelPanel extends JPanel { - final PreviewViewControllerBase preview; - - @Override - public void paint(Graphics g) { - super.paint(g); - preview.paint(g, 16); - } - - public PreviewViewModelPanel(PreviewViewController preview) { - this.preview = preview; - - addMouseMotionListener(new MouseMotionListener() { - - @Override - public void mouseDragged(MouseEvent e) { - // The PreviewViewController does not care about drag events. - // If it ever does, this code will need to be updated. - } - - @Override - public void mouseMoved(MouseEvent e) { - preview.onMouseMoved(e); - } - }); - addMouseListener(new MouseListener() { - @Override - public void mouseClicked(MouseEvent e) { - // The PreviewViewController does not care about click events instead - // relying on mouseReleased events. - // If it ever does, this code will need to be updated. - } - - @Override - public void mousePressed(MouseEvent e) { - preview.onMousePressed(e); - } - - @Override - public void mouseReleased(MouseEvent e) { - preview.onMouseReleased(e); - } - - @Override - public void mouseEntered(MouseEvent e) { - preview.onMouseEntered(e); - } - - @Override - public void mouseExited(MouseEvent e) { - preview.onMouseExited(e); - } - }); - } -} diff --git a/flutter-idea/src/io/flutter/settings/FlutterSettings.java b/flutter-idea/src/io/flutter/settings/FlutterSettings.java index 087bda9b28..67ee5495e9 100644 --- a/flutter-idea/src/io/flutter/settings/FlutterSettings.java +++ b/flutter-idea/src/io/flutter/settings/FlutterSettings.java @@ -331,12 +331,6 @@ public void setEnableHotUi(boolean value) { fireEvent(); } - public boolean isEnableHotUiInCodeEditor() { - // We leave this setting off for now to avoid possible performance and - // usability issues rendering previews directly in the code editor. - return false; - } - public boolean isEnableBazelHotRestart() { return getPropertiesComponent().getBoolean(enableBazelHotRestartKey, false); diff --git a/flutter-idea/src/io/flutter/utils/animation/Cubic.java b/flutter-idea/src/io/flutter/utils/animation/Cubic.java deleted file mode 100644 index 1508dd0ecc..0000000000 --- a/flutter-idea/src/io/flutter/utils/animation/Cubic.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2018 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -package io.flutter.utils.animation; - -/// A cubic polynomial mapping of the unit interval. -/// -/// The Curves class contains some commonly used cubic curves: -/// -/// * Curves.EASE -/// * Curves.EASE_IN -/// * Curves.EASE_OUT -/// * Curves.EASE_IN_OUT -/// -/// https://flutter.github.io/assets-for-api-docs/animation/curve_ease.png -/// https://flutter.github.io/assets-for-api-docs/animation/curve_ease_in.png -/// https://flutter.github.io/assets-for-api-docs/animation/curve_ease_out.png -/// https://flutter.github.io/assets-for-api-docs/animation/curve_ease_in_out.png -/// -/// The Cubic class implements third-order Bézier curves. -class Cubic extends Curve { - /// Creates a cubic curve. - /// - /// Rather than creating a new instance, consider using one of the common - /// cubic curves in Curves. - Cubic(double a, double b, double c, double d) { - this.a = a; - this.b = b; - this.c = c; - this.d = d; - } - - /// The x coordinate of the first control point. - /// - /// The line through the point (0, 0) and the first control point is tangent - /// to the curve at the point (0, 0). - final double a; - - /// The y coordinate of the first control point. - /// - /// The line through the point (0, 0) and the first control point is tangent - /// to the curve at the point (0, 0). - final double b; - - /// The x coordinate of the second control point. - /// - /// The line through the point (1, 1) and the second control point is tangent - /// to the curve at the point (1, 1). - final double c; - - /// The y coordinate of the second control point. - /// - /// The line through the point (1, 1) and the second control point is tangent - /// to the curve at the point (1, 1). - final double d; - - static final double _kCubicErrorBound = 0.001; - - double evaluateCubic(double a, double b, double m) { - return 3 * a * (1 - m) * (1 - m) * m + - 3 * b * (1 - m) * m * m + - m * m * m; - } - - @Override - public double transform(double t) { - assert (t >= 0.0 && t <= 1.0); - double start = 0.0; - double end = 1.0; - while (true) { - final double midpoint = (start + end) / 2; - final double estimate = evaluateCubic(a, c, midpoint); - if (Math.abs(t - estimate) < _kCubicErrorBound) { - return evaluateCubic(b, d, midpoint); - } - if (estimate < t) { - start = midpoint; - } - else { - end = midpoint; - } - } - } - - @Override - public String toString() { - return getClass() + "(" + a + ", " + b + ", " + c + ", " + d + ")"; - } -} diff --git a/flutter-idea/src/io/flutter/utils/animation/Curve.java b/flutter-idea/src/io/flutter/utils/animation/Curve.java deleted file mode 100644 index ec76147e8a..0000000000 --- a/flutter-idea/src/io/flutter/utils/animation/Curve.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2018 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -package io.flutter.utils.animation; - -/** - * A mapping of the unit interval to the unit interval. - *

- * A curve must map t=0.0 to 0.0 and t=1.0 to 1.0. - */ -public abstract class Curve { - /// Returns the value of the curve at point `t`. - /// - /// The value of `t` must be between 0.0 and 1.0, inclusive. Subclasses should - /// assert that this is true. - /// - /// A curve must map t=0.0 to 0.0 and t=1.0 to 1.0. - public abstract double transform(double t); - - public double interpolate(double start, double end, double t) { - final double fraction = transform(t); - return start * (1 - fraction) + end * fraction; - } - - public int interpolate(int start, int end, double t) { - return (int)Math.round(interpolate((double)start, (double)end, t)); - } - - /// Returns a new curve that is the reversed inversion of this one. - /// This is often useful as the reverseCurve of an [Animation]. - /// - /// ![](https://flutter.github.io/assets-for-api-docs/animation/curve_bounce_in.png) - /// ![](https://flutter.github.io/assets-for-api-docs/animation/curve_flipped.png) - /// - /// See also: - /// - /// * [FlippedCurve], the class that is used to implement this getter. - public Curve getFlipped() { - return new FlippedCurve(this); - } - - @Override - public String toString() { - return getClass().toString(); - } -} \ No newline at end of file diff --git a/flutter-idea/src/io/flutter/utils/animation/Curves.java b/flutter-idea/src/io/flutter/utils/animation/Curves.java deleted file mode 100644 index b3a37144f5..0000000000 --- a/flutter-idea/src/io/flutter/utils/animation/Curves.java +++ /dev/null @@ -1,357 +0,0 @@ -/* - * Copyright 2018 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -package io.flutter.utils.animation; - -/** - * A collection of common animation curves. - *

- * https://flutter.github.io/assets-for-api-docs/animation/curve_bounce_in.png - * https://flutter.github.io/assets-for-api-docs/animation/curve_bounce_in_out.png - * https://flutter.github.io/assets-for-api-docs/animation/curve_bounce_out.png - * https://flutter.github.io/assets-for-api-docs/animation/curve_decelerate.png - * https://flutter.github.io/assets-for-api-docs/animation/curve_ease.png - * https://flutter.github.io/assets-for-api-docs/animation/curve_ease_in.png - * https://flutter.github.io/assets-for-api-docs/animation/curve_ease_in_out.png - * https://flutter.github.io/assets-for-api-docs/animation/curve_ease_out.png - * https://flutter.github.io/assets-for-api-docs/animation/curve_elastic_in.png - * https://flutter.github.io/assets-for-api-docs/animation/curve_elastic_in_out.png - * https://flutter.github.io/assets-for-api-docs/animation/curve_elastic_out.png - * https://flutter.github.io/assets-for-api-docs/animation/curve_fast_out_slow_in.png - * https://flutter.github.io/assets-for-api-docs/animation/curve_linear.png - *

- * See also: - *

- * * Curve, the interface implemented by the constants available from the - * Curves class. - */ -public class Curves { - /** - * A LINEAR animation curve. - *

- * This is the identity map over the unit interval: its Curve.transform - * method returns its input unmodified. This is useful as a default curve for - * cases where a Curve is required but no actual curve is desired. - *

- * https://flutter.github.io/assets-for-api-docs/animation/curve_linear.png - */ - public static final Curve LINEAR = new Linear(); - - /** - * A curve where the rate of change starts out quickly and then decelerates; an - * upside-down `f(t) = t²` parabola. - *

- * This is equivalent to the Android `DecelerateInterpolator` class with a unit - * factor (the default factor). - *

- * https://flutter.github.io/assets-for-api-docs/animation/curve_decelerate.png - */ - public static final Curve DECELERATE = new DecelerateCurve(); - - /** - * A cubic animation curve that speeds up quickly and ends slowly. - *

- * https://flutter.github.io/assets-for-api-docs/animation/curve_ease.png - */ - public static final Cubic EASE = new Cubic(0.25, 0.1, 0.25, 1.0); - - /** - * A cubic animation curve that starts slowly and ends quickly. - *

- * https://flutter.github.io/assets-for-api-docs/animation/curve_ease_in.png - */ - public static final Cubic EASE_IN = new Cubic(0.42, 0.0, 1.0, 1.0); - - /** - * A cubic animation curve that starts quickly and ends slowly. - *

- * https://flutter.github.io/assets-for-api-docs/animation/curve_ease_out.png - */ - public static final Cubic EASE_OUT = new Cubic(0.0, 0.0, 0.58, 1.0); - - /** - * A cubic animation curve that starts slowly, speeds up, and then and ends slowly. - *

- * https://flutter.github.io/assets-for-api-docs/animation/curve_ease_in_out.png - */ - public static final Cubic EASE_IN_OUT = new Cubic(0.42, 0.0, 0.58, 1.0); - - /** - * A curve that starts quickly and eases into its final position. - *

- * Over the course of the animation, the object spends more time near its - * final destination. As a result, the user isn’t left waiting for the - * animation to finish, and the negative effects of motion are minimized. - *

- * https://flutter.github.io/assets-for-api-docs/animation/curve_fast_out_slow_in.png - */ - public static final Cubic FAST_OUT_SLOW_IN = new Cubic(0.4, 0.0, 0.2, 1.0); - - /** - * An oscillating curve that grows in magnitude. - *

- * https://flutter.github.io/assets-for-api-docs/animation/curve_bounce_in.png - */ - public static final Curve BOUNCE_IN = new BounceInCurve(); - - /** - * An oscillating curve that first grows and then shrink in magnitude. - *

- * https://flutter.github.io/assets-for-api-docs/animation/curve_bounce_out.png - */ - public static final Curve BOUNCE_OUT = new BounceOutCurve(); - - /** - * An oscillating curve that first grows and then shrink in magnitude. - *

- * https://flutter.github.io/assets-for-api-docs/animation/curve_bounce_in_out.png - */ - public static final Curve BOUNCE_IN_OUT = new BounceInOutCurve(); - - /** - * An oscillating curve that grows in magnitude while overshooting its bounds. - *

- * https://flutter.github.io/assets-for-api-docs/animation/curve_elastic_in.png - */ - public static final ElasticInCurve ELASTIC_IN = new ElasticInCurve(); - - /** - * An oscillating curve that shrinks in magnitude while overshooting its bounds. - *

- * https://flutter.github.io/assets-for-api-docs/animation/curve_elastic_out.png - */ - public static final ElasticOutCurve ELASTIC_OUT = new ElasticOutCurve(); - - /** - * An oscillating curve that grows and then shrinks in magnitude while overshooting its bounds. - *

- * https://flutter.github.io/assets-for-api-docs/animation/curve_elastic_in_out.png - */ - public static final ElasticInOutCurve ELASTIC_IN_OUT = new ElasticInOutCurve(); -} - -/** - * A curve where the rate of change starts out quickly and then decelerates; an - * upside-down `f(t) = t²` parabola. - *

- * This is equivalent to the Android `DecelerateInterpolator` class with a unit - * factor (the default factor). - *

- * See Curves.DECELERATE for an instance of this class. - */ -class DecelerateCurve extends Curve { - - @Override - public double transform(double t) { - assert (t >= 0.0 && t <= 1.0); - // Intended to match the behavior of: - // https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/view/animation/DecelerateInterpolator.java - // ...as of December 2016. - t = 1.0 - t; - return 1.0 - t * t; - } -} - -/** - * The identity map over the unit interval. - *

- * See Curves.LINEAR for an instance of this class. - */ -class Linear extends Curve { - @Override - public double transform(double t) { - return t; - } -} - - -/** - * An oscillating curve that grows in magnitude. - *

- * See Curves.BOUNCE_IN for an instance of this class. - */ -class BounceInCurve extends Curve { - @Override - public double transform(double t) { - assert (t >= 0.0 && t <= 1.0); - return 1.0 - BounceInOutCurve._bounce(1.0 - t); - } -} - -/** - * An oscillating curve that shrink in magnitude. - *

- * See Curves.BOUNCE_OUT for an instance of this class. - */ -class BounceOutCurve extends Curve { - @Override - public double transform(double t) { - assert (t >= 0.0 && t <= 1.0); - return BounceInOutCurve._bounce(t); - } -} - -/** - * An oscillating curve that first grows and then shrink in magnitude. - *

- * See Curves.BOUNCE_IN_OUT for an instance of this class. - */ -class BounceInOutCurve extends Curve { - - @Override - public double transform(double t) { - assert (t >= 0.0 && t <= 1.0); - if (t < 0.5) { - return (1.0 - _bounce(1.0 - t)) * 0.5; - } - else { - return _bounce(t * 2.0 - 1.0) * 0.5 + 0.5; - } - } - - static double _bounce(double t) { - if (t < 1.0 / 2.75) { - return 7.5625 * t * t; - } - else if (t < 2 / 2.75) { - t -= 1.5 / 2.75; - return 7.5625 * t * t + 0.75; - } - else if (t < 2.5 / 2.75) { - t -= 2.25 / 2.75; - return 7.5625 * t * t + 0.9375; - } - t -= 2.625 / 2.75; - return 7.5625 * t * t + 0.984375; - } -} - -/** - * An oscillating curve that grows in magnitude while overshooting its bounds. - *

- * An instance of this class using the default period of 0.4 is available as - * Curves.ELASTIC_IN. - *

- * https://flutter.github.io/assets-for-api-docs/animation/curve_elastic_in.png - */ -class ElasticInCurve extends Curve { - /** - * Creates an elastic-in curve. - *

- * Rather than creating a new instance, consider using Curves.ELASTIC_IN. - */ - public ElasticInCurve() { - this(0.4); - } - - public ElasticInCurve(double period) { - this.period = period; - } - - /** - * The duration of the oscillation. - */ - final double period; - - @Override - public double transform(double t) { - assert (t >= 0.0 && t <= 1.0); - final double s = period / 4.0; - t = t - 1.0; - return -Math.pow(2.0, 10.0 * t) * Math.sin((t - s) * (Math.PI * 2.0) / period); - } - - @Override - public String toString() { - return getClass() + "(" + period + ")"; - } -} - -/** - * An oscillating curve that shrinks in magnitude while overshooting its bounds. - *

- * An instance of this class using the default period of 0.4 is available as - * Curves.ELASTIC_OUT. - *

- * https://flutter.github.io/assets-for-api-docs/animation/curve_elastic_out.png - */ -class ElasticOutCurve extends Curve { - /** - * Creates an elastic-out curve. - *

- * Rather than creating a new instance, consider using Curves.ELASTIC_OUT. - */ - public ElasticOutCurve() { - this(0.4); - } - - public ElasticOutCurve(double period) { - this.period = period; - } - - /** - * The duration of the oscillation. - */ - final double period; - - @Override - public double transform(double t) { - assert (t >= 0.0 && t <= 1.0); - final double s = period / 4.0; - return Math.pow(2.0, -10 * t) * Math.sin((t - s) * (Math.PI * 2.0) / period) + 1.0; - } - - @Override - public String toString() { - return getClass() + "(" + period + ")"; - } -} - -/** - * An oscillating curve that grows and then shrinks in magnitude while - * overshooting its bounds. - *

- * An instance of this class using the default period of 0.4 is available as - * Curves.ELASTIC_IN_OUT. - *

- * https://flutter.github.io/assets-for-api-docs/animation/curve_elastic_in_out.png - */ -class ElasticInOutCurve extends Curve { - /** - * Creates an elastic-in-out curve. - *

- * Rather than creating a new instance, consider using Curves.ELASTIC_IN_OUT. - */ - public ElasticInOutCurve() { - this(0.4); - } - - public ElasticInOutCurve(double period) { - this.period = 0.4; - } - - /** - * The duration of the oscillation. - */ - final double period; - - @Override - public double transform(double t) { - assert (t >= 0.0 && t <= 1.0); - final double s = period / 4.0; - t = 2.0 * t - 1.0; - if (t < 0.0) { - return -0.5 * Math.pow(2.0, 10.0 * t) * Math.sin((t - s) * (Math.PI * 2.0) / period); - } - else { - return Math.pow(2.0, -10.0 * t) * Math.sin((t - s) * (Math.PI * 2.0) / period) * 0.5 + 1.0; - } - } - - @Override - public String toString() { - return getClass() + "(" + period + ")"; - } -} diff --git a/flutter-idea/src/io/flutter/utils/animation/FlippedCurve.java b/flutter-idea/src/io/flutter/utils/animation/FlippedCurve.java deleted file mode 100644 index cfa41d501e..0000000000 --- a/flutter-idea/src/io/flutter/utils/animation/FlippedCurve.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright 2018 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -package io.flutter.utils.animation; - -/** - * A curve that is the reversed inversion of its given curve. - *

- * This curve evaluates the given curve in reverse (i.e., from 1.0 to 0.0 as t - * increases from 0.0 to 1.0) and returns the inverse of the given curve's value - * (i.e., 1.0 minus the given curve's value). - *

- * This is the class used to implement the getFlipped method on curves. - */ -public class FlippedCurve extends Curve { - /** - * Creates a flipped curve. - */ - public FlippedCurve(Curve curve) { - assert (curve != null); - this.curve = curve; - } - - /** - * The curve that is being flipped. - */ - public final Curve curve; - - @Override - public double transform(double t) { - return 1.0 - curve.transform(1.0 - t); - } - - @Override - public String toString() { - return this.getClass() + "(" + curve + ")"; - } -} diff --git a/flutter-idea/src/io/flutter/utils/animation/Threshold.java b/flutter-idea/src/io/flutter/utils/animation/Threshold.java deleted file mode 100644 index 3013f64e7a..0000000000 --- a/flutter-idea/src/io/flutter/utils/animation/Threshold.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2018 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -package io.flutter.utils.animation; - -/** - * A curve that is 0.0 until it hits the threshold, then it jumps to 1.0. - *

- * https://flutter.github.io/assets-for-api-docs/animation/curve_threshold.png - */ -public class Threshold extends Curve { - /// Creates a threshold curve. - public Threshold(double threshold) { - this.threshold = threshold; - } - - /// The value before which the curve is 0.0 and after which the curve is 1.0. - /// - /// When t is exactly [threshold], the curve has the value 1.0. - final double threshold; - - @Override - public double transform(double t) { - assert (t >= 0.0 && t <= 1.0); - assert (threshold >= 0.0); - assert (threshold <= 1.0); - if (t == 0.0 || t == 1.0) { - return t; - } - return t < threshold ? 0.0 : 1.0; - } -} diff --git a/flutter-idea/src/io/flutter/utils/math/Matrix4.java b/flutter-idea/src/io/flutter/utils/math/Matrix4.java deleted file mode 100644 index 43876f4309..0000000000 --- a/flutter-idea/src/io/flutter/utils/math/Matrix4.java +++ /dev/null @@ -1,2370 +0,0 @@ -/* - * Copyright 2019 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -package io.flutter.utils.math; - -import java.util.Arrays; - -/** - * 4D Matrix. - *

- * Values are stored in column major order. - *

- * This code is ported from the Matrix4 class in the Dart vector_math - * package. The Class is ported as is without concern for making the code - * consistent with Java api conventions to keep the code consistent with - * the Dart code to simplify using Transform Matrixes returned by Flutter. - */ -@SuppressWarnings({"Duplicates", "PointlessArithmeticExpression", "UnnecessaryLocalVariable", "JoinDeclarationAndAssignmentJava"}) -public class Matrix4 { - final double[] _m4storage; - - /** - * Constructs a new mat4. - */ - public Matrix4( - double arg0, - double arg1, - double arg2, - double arg3, - double arg4, - double arg5, - double arg6, - double arg7, - double arg8, - double arg9, - double arg10, - double arg11, - double arg12, - double arg13, - double arg14, - double arg15) { - _m4storage = new double[16]; - setValues(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, - arg10, arg11, arg12, arg13, arg14, arg15); - } - - /** - * Zero matrix. - */ - Matrix4() { - _m4storage = new double[16]; - } - - /** - * Constructs Matrix4 with given [double[]] as [storage]. - */ - public Matrix4(double[] storage) { - this._m4storage = storage; - } - - /** - * Solve [A] * [x] = [b]. - */ - static void solve2(Matrix4 A, Vector2 x, Vector2 b) { - final double a11 = A.entry(0, 0); - final double a12 = A.entry(0, 1); - final double a21 = A.entry(1, 0); - final double a22 = A.entry(1, 1); - final double bx = b.getX() - A._m4storage[8]; - final double by = b.getY() - A._m4storage[9]; - double det = a11 * a22 - a12 * a21; - - if (det != 0.0) { - det = 1.0 / det; - } - - x.setX(det * (a22 * bx - a12 * by)); - x.setY(det * (a11 * by - a21 * bx)); - } - - /** - * Solve [A] * [x] = [b]. - */ - public static void solve3(Matrix4 A, Vector3 x, Vector3 b) { - final double A0x = A.entry(0, 0); - final double A0y = A.entry(1, 0); - final double A0z = A.entry(2, 0); - final double A1x = A.entry(0, 1); - final double A1y = A.entry(1, 1); - final double A1z = A.entry(2, 1); - final double A2x = A.entry(0, 2); - final double A2y = A.entry(1, 2); - final double A2z = A.entry(2, 2); - final double bx = b.getX() - A._m4storage[12]; - final double by = b.getY() - A._m4storage[13]; - final double bz = b.getZ() - A._m4storage[14]; - double rx, ry, rz; - double det; - - // Column1 cross Column 2 - rx = A1y * A2z - A1z * A2y; - ry = A1z * A2x - A1x * A2z; - rz = A1x * A2y - A1y * A2x; - - // A.getColumn(0).dot(x) - det = A0x * rx + A0y * ry + A0z * rz; - if (det != 0.0) { - det = 1.0 / det; - } - - // b dot [Column1 cross Column 2] - final double x_ = det * (bx * rx + by * ry + bz * rz); - - // Column2 cross b - rx = -(A2y * bz - A2z * by); - ry = -(A2z * bx - A2x * bz); - rz = -(A2x * by - A2y * bx); - // Column0 dot -[Column2 cross b (Column3)] - final double y_ = det * (A0x * rx + A0y * ry + A0z * rz); - - // b cross Column 1 - rx = -(by * A1z - bz * A1y); - ry = -(bz * A1x - bx * A1z); - rz = -(bx * A1y - by * A1x); - // Column0 dot -[b cross Column 1] - final double z_ = det * (A0x * rx + A0y * ry + A0z * rz); - - x.setX(x_); - x.setY(y_); - x.setZ(z_); - } - - /** - * Solve [A] * [x] = [b]. - */ - static void solve(Matrix4 A, Vector4 x, Vector4 b) { - final double a00 = A._m4storage[0]; - final double a01 = A._m4storage[1]; - final double a02 = A._m4storage[2]; - final double a03 = A._m4storage[3]; - final double a10 = A._m4storage[4]; - final double a11 = A._m4storage[5]; - final double a12 = A._m4storage[6]; - final double a13 = A._m4storage[7]; - final double a20 = A._m4storage[8]; - final double a21 = A._m4storage[9]; - final double a22 = A._m4storage[10]; - final double a23 = A._m4storage[11]; - final double a30 = A._m4storage[12]; - final double a31 = A._m4storage[13]; - final double a32 = A._m4storage[14]; - final double a33 = A._m4storage[15]; - final double b00 = a00 * a11 - a01 * a10; - final double b01 = a00 * a12 - a02 * a10; - final double b02 = a00 * a13 - a03 * a10; - final double b03 = a01 * a12 - a02 * a11; - final double b04 = a01 * a13 - a03 * a11; - final double b05 = a02 * a13 - a03 * a12; - final double b06 = a20 * a31 - a21 * a30; - final double b07 = a20 * a32 - a22 * a30; - final double b08 = a20 * a33 - a23 * a30; - final double b09 = a21 * a32 - a22 * a31; - final double b10 = a21 * a33 - a23 * a31; - final double b11 = a22 * a33 - a23 * a32; - - final double bX = b.getStorage()[0]; - final double bY = b.getStorage()[1]; - final double bZ = b.getStorage()[2]; - final double bW = b.getStorage()[3]; - - double det = - b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06; - - if (det != 0.0) { - det = 1.0 / det; - } - - x - .setX(det * - ((a11 * b11 - a12 * b10 + a13 * b09) * bX - - (a10 * b11 - a12 * b08 + a13 * b07) * bY + - (a10 * b10 - a11 * b08 + a13 * b06) * bZ - - (a10 * b09 - a11 * b07 + a12 * b06) * bW)); - x.setY(det * - -((a01 * b11 - a02 * b10 + a03 * b09) * bX - - (a00 * b11 - a02 * b08 + a03 * b07) * bY + - (a00 * b10 - a01 * b08 + a03 * b06) * bZ - - (a00 * b09 - a01 * b07 + a02 * b06) * bW)); - x.setZ(det * - ((a31 * b05 - a32 * b04 + a33 * b03) * bX - - (a30 * b05 - a32 * b02 + a33 * b01) * bY + - (a30 * b04 - a31 * b02 + a33 * b00) * bZ - - (a30 * b03 - a31 * b01 + a32 * b00) * bW)); - x.setW(det * - -((a21 * b05 - a22 * b04 + a23 * b03) * bX - - (a20 * b05 - a22 * b02 + a23 * b01) * bY + - (a20 * b04 - a21 * b02 + a23 * b00) * bZ - - (a20 * b03 - a21 * b01 + a22 * b00) * bW)); - } - - /** - * Returns a matrix that is the inverse of [other] if [other] is invertible, - * otherwise `null`. - */ - static Matrix4 tryInvert(Matrix4 other) { - final Matrix4 r = new Matrix4(); - final double determinant = r.copyInverse(other); - if (determinant == 0.0) { - return null; - } - return r; - } - - /** - * New matrix from [values]. - */ - public static Matrix4 fromList(double[] values) { - final Matrix4 ret = new Matrix4(); - ret.setValues( - values[0], - values[1], - values[2], - values[3], - values[4], - values[5], - values[6], - values[7], - values[8], - values[9], - values[10], - values[11], - values[12], - values[13], - values[14], - values[15]); - return ret; - } - - public static Matrix4 zero() { - return new Matrix4(); - } - - /** - * Identity matrix. - */ - public static Matrix4 identity() { - final Matrix4 ret = new Matrix4(); - ret.setIdentity(); - return ret; - } - - /** - * Copies values from [other]. - */ - public static Matrix4 copy(Matrix4 other) { - final Matrix4 ret = new Matrix4(); - ret.setFrom(other); - return ret; - } - - /** - * Constructs a matrix that is the inverse of [other]. - */ - public static Matrix4 inverted(Matrix4 other) { - final Matrix4 r = new Matrix4(); - final double determinant = r.copyInverse(other); - if (determinant == 0.0) { - throw new IllegalArgumentException( - "" + other + "other Matrix cannot be inverted"); - } - return r; - } - - /** - * Constructs a new mat4 from columns. - */ - public static Matrix4 columns( - Vector4 arg0, Vector4 arg1, Vector4 arg2, Vector4 arg3) { - final Matrix4 ret = new Matrix4(); - ret.setColumns(arg0, arg1, arg2, arg3); - return ret; - } - - /** - * Outer product of [u] and [v]. - */ - public static Matrix4 outer(Vector4 u, Vector4 v) { - final Matrix4 ret = new Matrix4(); - ret.setOuter(u, v); - return ret; - } - - /** - * Rotation of [radians_] around X. - */ - public static Matrix4 rotationX(double radians) { - final Matrix4 ret = new Matrix4(); - ret._m4storage[15] = 1.0; - ret.setRotationX(radians); - return ret; - } - - /** - * Rotation of [radians_] around Y. - */ - public static Matrix4 rotationY(double radians) { - final Matrix4 ret = new Matrix4(); - ret._m4storage[15] = 1.0; - ret.setRotationY(radians); - return ret; - } - - /** - * Rotation of [radians_] around Z. - */ - public static Matrix4 rotationZ(double radians) { - final Matrix4 ret = new Matrix4(); - ret._m4storage[15] = 1.0; - ret.setRotationZ(radians); - return ret; - } - - /** - * Translation matrix. - */ - public static Matrix4 translation(Vector3 translation) { - final Matrix4 ret = new Matrix4(); - ret.setIdentity(); - ret.setTranslation(translation); - return ret; - } - - /** - * Translation matrix. - */ - public static Matrix4 translationValues(double x, double y, double z) { - final Matrix4 ret = new Matrix4(); - ret.setIdentity(); - ret.setTranslationRaw(x, y, z); - return ret; - } - - /** - * Scale matrix. - */ - public static Matrix4 diagonal3(Vector3 scale) { - final Matrix4 m = new Matrix4(); - final double[] mStorage = m._m4storage; - final double[] scaleStorage = scale._v3storage; - mStorage[15] = 1.0; - mStorage[10] = scaleStorage[2]; - mStorage[5] = scaleStorage[1]; - mStorage[0] = scaleStorage[0]; - return m; - } - - /** - * Scale matrix. - */ - public static Matrix4 diagonal3Values(double x, double y, double z) { - final Matrix4 ret = new Matrix4(); - ret._m4storage[15] = 1.0; - ret._m4storage[10] = z; - ret._m4storage[5] = y; - ret._m4storage[0] = x; - return ret; - } - - /** - * Skew matrix around X axis - */ - public static Matrix4 skewX(double alpha) { - final Matrix4 m = Matrix4.identity(); - m._m4storage[4] = Math.tan(alpha); - return m; - } - - /** - * Skew matrix around Y axis. - */ - public static Matrix4 skewY(double beta) { - final Matrix4 m = Matrix4.identity(); - m._m4storage[1] = Math.tan(beta); - return m; - } - - /** - * Skew matrix around X axis (alpha) and Y axis (beta). - */ - public static Matrix4 skew(double alpha, double beta) { - final Matrix4 m = Matrix4.identity(); - m._m4storage[1] = Math.tan(beta); - m._m4storage[4] = Math.tan(alpha); - return m; - } - - /** - * Constructs Matrix4 from [translation], [rotation] and [scale]. - */ - public static Matrix4 compose( - Vector3 translation, Quaternion rotation, Vector3 scale) { - final Matrix4 matrix = new Matrix4(); - matrix.setFromTranslationRotationScale(translation, rotation, scale); - return matrix; - } - - /** - * The components of the matrix. - */ - double[] getStorage() { - return _m4storage; - } - - /** - * Return index in storage for [row], [col] value. - */ - public int index(int row, int col) { - return (col * 4) + row; - } - - /** - * Value at [row], [col]. - */ - public double entry(int row, int col) { - assert ((row >= 0) && (row < getDimension())); - assert ((col >= 0) && (col < getDimension())); - - return _m4storage[index(row, col)]; - } - - /** - * Set value at [row], [col] to be [v]. - */ - void setEntry(int row, int col, double v) { - assert ((row >= 0) && (row < getDimension())); - assert ((col >= 0) && (col < getDimension())); - - _m4storage[index(row, col)] = v; - } - - /** - * Sets the diagonal to [arg]. - */ - void splatDiagonal(double arg) { - _m4storage[0] = arg; - _m4storage[5] = arg; - _m4storage[10] = arg; - _m4storage[15] = arg; - } - - /** - * Sets the matrix with specified values. - */ - void setValues( - double arg0, - double arg1, - double arg2, - double arg3, - double arg4, - double arg5, - double arg6, - double arg7, - double arg8, - double arg9, - double arg10, - double arg11, - double arg12, - double arg13, - double arg14, - double arg15) { - _m4storage[15] = arg15; - _m4storage[14] = arg14; - _m4storage[13] = arg13; - _m4storage[12] = arg12; - _m4storage[11] = arg11; - _m4storage[10] = arg10; - _m4storage[9] = arg9; - _m4storage[8] = arg8; - _m4storage[7] = arg7; - _m4storage[6] = arg6; - _m4storage[5] = arg5; - _m4storage[4] = arg4; - _m4storage[3] = arg3; - _m4storage[2] = arg2; - _m4storage[1] = arg1; - _m4storage[0] = arg0; - } - - /** - * Sets the entire matrix to the column values. - */ - void setColumns(Vector4 arg0, Vector4 arg1, Vector4 arg2, Vector4 arg3) { - final double[] arg0Storage = arg0._v4storage; - final double[] arg1Storage = arg1._v4storage; - final double[] arg2Storage = arg2._v4storage; - final double[] arg3Storage = arg3._v4storage; - _m4storage[0] = arg0Storage[0]; - _m4storage[1] = arg0Storage[1]; - _m4storage[2] = arg0Storage[2]; - _m4storage[3] = arg0Storage[3]; - _m4storage[4] = arg1Storage[0]; - _m4storage[5] = arg1Storage[1]; - _m4storage[6] = arg1Storage[2]; - _m4storage[7] = arg1Storage[3]; - _m4storage[8] = arg2Storage[0]; - _m4storage[9] = arg2Storage[1]; - _m4storage[10] = arg2Storage[2]; - _m4storage[11] = arg2Storage[3]; - _m4storage[12] = arg3Storage[0]; - _m4storage[13] = arg3Storage[1]; - _m4storage[14] = arg3Storage[2]; - _m4storage[15] = arg3Storage[3]; - } - - /** - * Sets the entire matrix to the matrix in [arg]. - */ - void setFrom(Matrix4 arg) { - final double[] argStorage = arg._m4storage; - _m4storage[15] = argStorage[15]; - _m4storage[14] = argStorage[14]; - _m4storage[13] = argStorage[13]; - _m4storage[12] = argStorage[12]; - _m4storage[11] = argStorage[11]; - _m4storage[10] = argStorage[10]; - _m4storage[9] = argStorage[9]; - _m4storage[8] = argStorage[8]; - _m4storage[7] = argStorage[7]; - _m4storage[6] = argStorage[6]; - _m4storage[5] = argStorage[5]; - _m4storage[4] = argStorage[4]; - _m4storage[3] = argStorage[3]; - _m4storage[2] = argStorage[2]; - _m4storage[1] = argStorage[1]; - _m4storage[0] = argStorage[0]; - } - - /** - * Sets the matrix from translation [arg0] and rotation [arg1]. - */ - void setFromTranslationRotation(Vector3 arg0, Quaternion arg1) { - final double[] arg1Storage = arg1._qStorage; - final double x = arg1Storage[0]; - final double y = arg1Storage[1]; - final double z = arg1Storage[2]; - final double w = arg1Storage[3]; - final double x2 = x + x; - final double y2 = y + y; - final double z2 = z + z; - final double xx = x * x2; - final double xy = x * y2; - final double xz = x * z2; - final double yy = y * y2; - final double yz = y * z2; - final double zz = z * z2; - final double wx = w * x2; - final double wy = w * y2; - final double wz = w * z2; - - final double[] arg0Storage = arg0._v3storage; - _m4storage[0] = 1.0 - (yy + zz); - _m4storage[1] = xy + wz; - _m4storage[2] = xz - wy; - _m4storage[3] = 0.0; - _m4storage[4] = xy - wz; - _m4storage[5] = 1.0 - (xx + zz); - _m4storage[6] = yz + wx; - _m4storage[7] = 0.0; - _m4storage[8] = xz + wy; - _m4storage[9] = yz - wx; - _m4storage[10] = 1.0 - (xx + yy); - _m4storage[11] = 0.0; - _m4storage[12] = arg0Storage[0]; - _m4storage[13] = arg0Storage[1]; - _m4storage[14] = arg0Storage[2]; - _m4storage[15] = 1.0; - } - - /** - * Sets the matrix from [translation], [rotation] and [scale]. - */ - void setFromTranslationRotationScale( - Vector3 translation, Quaternion rotation, Vector3 scale) { - setFromTranslationRotation(translation, rotation); - this.scale(scale); - } - - /** - * Sets the diagonal of the matrix to be [arg]. - */ - void setDiagonal(Vector4 arg) { - final double[] argStorage = arg._v4storage; - _m4storage[0] = argStorage[0]; - _m4storage[5] = argStorage[1]; - _m4storage[10] = argStorage[2]; - _m4storage[15] = argStorage[3]; - } - - void setOuter(Vector4 u, Vector4 v) { - final double[] uStorage = u._v4storage; - final double[] vStorage = v._v4storage; - _m4storage[0] = uStorage[0] * vStorage[0]; - _m4storage[1] = uStorage[0] * vStorage[1]; - _m4storage[2] = uStorage[0] * vStorage[2]; - _m4storage[3] = uStorage[0] * vStorage[3]; - _m4storage[4] = uStorage[1] * vStorage[0]; - _m4storage[5] = uStorage[1] * vStorage[1]; - _m4storage[6] = uStorage[1] * vStorage[2]; - _m4storage[7] = uStorage[1] * vStorage[3]; - _m4storage[8] = uStorage[2] * vStorage[0]; - _m4storage[9] = uStorage[2] * vStorage[1]; - _m4storage[10] = uStorage[2] * vStorage[2]; - _m4storage[11] = uStorage[2] * vStorage[3]; - _m4storage[12] = uStorage[3] * vStorage[0]; - _m4storage[13] = uStorage[3] * vStorage[1]; - _m4storage[14] = uStorage[3] * vStorage[2]; - _m4storage[15] = uStorage[3] * vStorage[3]; - } - - /** - * Returns a printable string - */ - @Override() - public String toString() { - return - "[0] " + getRow(0) + "\n" + - "[1] " + getRow(1) + "\n" + - "[2] " + getRow(2) + "\n" + - "[3] " + getRow(3) + "\n"; - } - - /** - * Dimension of the matrix. - */ - public int getDimension() { - return 4; - } - - /** - * Access the element of the matrix at the index [i]. - */ - double get(int i) { - return _m4storage[i]; - } - - /** - * Set the element of the matrix at the index [i]. - */ - void set(int i, double v) { - _m4storage[i] = v; - } - - /** - * Check if two matrices are the same. - */ - @Override() - public boolean equals(Object o) { - if (!(o instanceof Matrix4 other)) { - return false; - } - return (_m4storage[0] == other._m4storage[0]) && - (_m4storage[1] == other._m4storage[1]) && - (_m4storage[2] == other._m4storage[2]) && - (_m4storage[3] == other._m4storage[3]) && - (_m4storage[4] == other._m4storage[4]) && - (_m4storage[5] == other._m4storage[5]) && - (_m4storage[6] == other._m4storage[6]) && - (_m4storage[7] == other._m4storage[7]) && - (_m4storage[8] == other._m4storage[8]) && - (_m4storage[9] == other._m4storage[9]) && - (_m4storage[10] == other._m4storage[10]) && - (_m4storage[11] == other._m4storage[11]) && - (_m4storage[12] == other._m4storage[12]) && - (_m4storage[13] == other._m4storage[13]) && - (_m4storage[14] == other._m4storage[14]) && - (_m4storage[15] == other._m4storage[15]); - } - - @Override() - public int hashCode() { - return Arrays.hashCode(_m4storage); - } - - /** - * Returns row 0 - */ - public Vector4 getRow0() { - return getRow(0); - } - - /** - * Sets row 0 to [arg] - */ - public void setRow0(Vector4 arg) { - setRow(0, arg); - } - - /** - * Returns row 1 - */ - public Vector4 getRow1() { - return getRow(1); - } - - /** - * Sets row 1 to [arg] - */ - public void setRow1(Vector4 arg) { - setRow(1, arg); - } - - /** - * Returns row 2 - */ - public Vector4 getRow2() { - return getRow(2); - } - - /** - * Sets row 2 to [arg] - */ - public void setRow2(Vector4 arg) { - setRow(2, arg); - } - - /** - * Returns row 3 - */ - public Vector4 getRow3() { - return getRow(3); - } - - /** - * Sets row 3 to [arg] - */ - public void setRow3(Vector4 arg) { - setRow(3, arg); - } - - /** - * Assigns the [row] of the matrix [arg] - */ - public void setRow(int row, Vector4 arg) { - final double[] argStorage = arg._v4storage; - _m4storage[index(row, 0)] = argStorage[0]; - _m4storage[index(row, 1)] = argStorage[1]; - _m4storage[index(row, 2)] = argStorage[2]; - _m4storage[index(row, 3)] = argStorage[3]; - } - - /** - * Gets the [row] of the matrix - */ - public Vector4 getRow(int row) { - final Vector4 r = new Vector4(); - final double[] rStorage = r._v4storage; - rStorage[0] = _m4storage[index(row, 0)]; - rStorage[1] = _m4storage[index(row, 1)]; - rStorage[2] = _m4storage[index(row, 2)]; - rStorage[3] = _m4storage[index(row, 3)]; - return r; - } - - /** - * Assigns the [column] of the matrix [arg] - */ - public void setColumn(int column, Vector4 arg) { - final int entry = column * 4; - final double[] argStorage = arg._v4storage; - _m4storage[entry + 3] = argStorage[3]; - _m4storage[entry + 2] = argStorage[2]; - _m4storage[entry + 1] = argStorage[1]; - _m4storage[entry + 0] = argStorage[0]; - } - - /** - * Gets the [column] of the matrix - */ - public Vector4 getColumn(int column) { - final Vector4 r = new Vector4(); - final double[] rStorage = r._v4storage; - final int entry = column * 4; - rStorage[3] = _m4storage[entry + 3]; - rStorage[2] = _m4storage[entry + 2]; - rStorage[1] = _m4storage[entry + 1]; - rStorage[0] = _m4storage[entry + 0]; - return r; - } - - /** - * Clone matrix. - */ - @SuppressWarnings("MethodDoesntCallSuperMethod") - @Override - public Matrix4 clone() { - return Matrix4.copy(this); - } - - /** - * Copy into [arg]. - */ - public Matrix4 copyInto(Matrix4 arg) { - final double[] argStorage = arg._m4storage; - argStorage[0] = _m4storage[0]; - argStorage[1] = _m4storage[1]; - argStorage[2] = _m4storage[2]; - argStorage[3] = _m4storage[3]; - argStorage[4] = _m4storage[4]; - argStorage[5] = _m4storage[5]; - argStorage[6] = _m4storage[6]; - argStorage[7] = _m4storage[7]; - argStorage[8] = _m4storage[8]; - argStorage[9] = _m4storage[9]; - argStorage[10] = _m4storage[10]; - argStorage[11] = _m4storage[11]; - argStorage[12] = _m4storage[12]; - argStorage[13] = _m4storage[13]; - argStorage[14] = _m4storage[14]; - argStorage[15] = _m4storage[15]; - return arg; - } - - /** - * Returns new matrix -this - */ - public Matrix4 operatorNegate() { - final Matrix4 ret = clone(); - ret.negate(); - return ret; - } - - /** - * Returns a new vector or matrix by multiplying [this] with [arg]. - */ - Matrix4 operatorMultiply(double arg) { - return scaled(arg); - } - - public Vector4 operatorMultiply(Vector4 arg) { - return transformed(arg); - } - - public Vector3 operatorMultiply(Vector3 arg) { - return transformed3(arg); - } - - public Matrix4 operatorMultiply(Matrix4 arg) { - return multiplied(arg); - } - - /** - * Returns new matrix after component wise [this] + [arg] - */ - public Matrix4 operatorAdd(Matrix4 arg) { - final Matrix4 ret = clone(); - ret.add(arg); - return ret; - } - - /** - * Returns new matrix after component wise [this] - [arg] - */ - public Matrix4 operatorSub(Matrix4 arg) { - final Matrix4 ret = clone(); - ret.sub(arg); - return ret; - } - - /** - * Translate this matrix by a [Vector3], [Vector4], or x,y,z - */ - public void translate(double x) { - translate(x, 0, 0); - } - - public void translate(Vector3 v) { - translate(v.getX(), v.getY(), v.getZ()); - } - - public void translate(Vector4 v) { - translate(v.getX(), v.getY(), v.getZ(), v.getW()); - } - - public void translate(double x, double y, double z) { - translate(x, y, z, 1); - } - - public void translate(double tx, double ty, double tz, double tw) { - final double t1 = _m4storage[0] * tx + - _m4storage[4] * ty + - _m4storage[8] * tz + - _m4storage[12] * tw; - final double t2 = _m4storage[1] * tx + - _m4storage[5] * ty + - _m4storage[9] * tz + - _m4storage[13] * tw; - final double t3 = _m4storage[2] * tx + - _m4storage[6] * ty + - _m4storage[10] * tz + - _m4storage[14] * tw; - final double t4 = _m4storage[3] * tx + - _m4storage[7] * ty + - _m4storage[11] * tz + - _m4storage[15] * tw; - _m4storage[12] = t1; - _m4storage[13] = t2; - _m4storage[14] = t3; - _m4storage[15] = t4; - } - - /** - * Multiply [this] by a translation from the left. - */ - public void leftTranslate(double x, double y, double z) { - leftTranslater(x, y, z, 1); - } - - public void leftTranslate(double x) { - leftTranslater(x, 0, 0, 1); - } - - public void leftTranslate(Vector4 v) { - leftTranslater(v.getX(), v.getY(), v.getZ(), v.getW()); - } - - public void leftTranslate(Vector3 v) { - leftTranslater(v.getX(), v.getY(), v.getZ(), 1.0); - } - - void leftTranslater(double tx, double ty, double tz, double tw) { - // Column 1 - _m4storage[0] += tx * _m4storage[3]; - _m4storage[1] += ty * _m4storage[3]; - _m4storage[2] += tz * _m4storage[3]; - _m4storage[3] = tw * _m4storage[3]; - - // Column 2 - _m4storage[4] += tx * _m4storage[7]; - _m4storage[5] += ty * _m4storage[7]; - _m4storage[6] += tz * _m4storage[7]; - _m4storage[7] = tw * _m4storage[7]; - - // Column 3 - _m4storage[8] += tx * _m4storage[11]; - _m4storage[9] += ty * _m4storage[11]; - _m4storage[10] += tz * _m4storage[11]; - _m4storage[11] = tw * _m4storage[11]; - - // Column 4 - _m4storage[12] += tx * _m4storage[15]; - _m4storage[13] += ty * _m4storage[15]; - _m4storage[14] += tz * _m4storage[15]; - _m4storage[15] = tw * _m4storage[15]; - } - - /** - * Rotate this [angle] radians around [axis] - */ - public void rotate(Vector3 axis, double angle) { - final double len = axis.getLength(); - final double[] axisStorage = axis._v3storage; - final double x = axisStorage[0] / len; - final double y = axisStorage[1] / len; - final double z = axisStorage[2] / len; - final double c = Math.cos(angle); - final double s = Math.sin(angle); - final double C = 1.0 - c; - final double m11 = x * x * C + c; - final double m12 = x * y * C - z * s; - final double m13 = x * z * C + y * s; - final double m21 = y * x * C + z * s; - final double m22 = y * y * C + c; - final double m23 = y * z * C - x * s; - final double m31 = z * x * C - y * s; - final double m32 = z * y * C + x * s; - final double m33 = z * z * C + c; - final double t1 = - _m4storage[0] * m11 + _m4storage[4] * m21 + _m4storage[8] * m31; - final double t2 = - _m4storage[1] * m11 + _m4storage[5] * m21 + _m4storage[9] * m31; - final double t3 = - _m4storage[2] * m11 + _m4storage[6] * m21 + _m4storage[10] * m31; - final double t4 = - _m4storage[3] * m11 + _m4storage[7] * m21 + _m4storage[11] * m31; - final double t5 = - _m4storage[0] * m12 + _m4storage[4] * m22 + _m4storage[8] * m32; - final double t6 = - _m4storage[1] * m12 + _m4storage[5] * m22 + _m4storage[9] * m32; - final double t7 = - _m4storage[2] * m12 + _m4storage[6] * m22 + _m4storage[10] * m32; - final double t8 = - _m4storage[3] * m12 + _m4storage[7] * m22 + _m4storage[11] * m32; - final double t9 = - _m4storage[0] * m13 + _m4storage[4] * m23 + _m4storage[8] * m33; - final double t10 = - _m4storage[1] * m13 + _m4storage[5] * m23 + _m4storage[9] * m33; - final double t11 = - _m4storage[2] * m13 + _m4storage[6] * m23 + _m4storage[10] * m33; - final double t12 = - _m4storage[3] * m13 + _m4storage[7] * m23 + _m4storage[11] * m33; - _m4storage[0] = t1; - _m4storage[1] = t2; - _m4storage[2] = t3; - _m4storage[3] = t4; - _m4storage[4] = t5; - _m4storage[5] = t6; - _m4storage[6] = t7; - _m4storage[7] = t8; - _m4storage[8] = t9; - _m4storage[9] = t10; - _m4storage[10] = t11; - _m4storage[11] = t12; - } - - /** - * Rotate this [angle] radians around X - */ - public void rotateX(double angle) { - final double cosAngle = Math.cos(angle); - final double sinAngle = Math.sin(angle); - final double t1 = _m4storage[4] * cosAngle + _m4storage[8] * sinAngle; - final double t2 = _m4storage[5] * cosAngle + _m4storage[9] * sinAngle; - final double t3 = _m4storage[6] * cosAngle + _m4storage[10] * sinAngle; - final double t4 = _m4storage[7] * cosAngle + _m4storage[11] * sinAngle; - final double t5 = _m4storage[4] * -sinAngle + _m4storage[8] * cosAngle; - final double t6 = _m4storage[5] * -sinAngle + _m4storage[9] * cosAngle; - final double t7 = _m4storage[6] * -sinAngle + _m4storage[10] * cosAngle; - final double t8 = _m4storage[7] * -sinAngle + _m4storage[11] * cosAngle; - _m4storage[4] = t1; - _m4storage[5] = t2; - _m4storage[6] = t3; - _m4storage[7] = t4; - _m4storage[8] = t5; - _m4storage[9] = t6; - _m4storage[10] = t7; - _m4storage[11] = t8; - } - - /** - * Rotate this matrix [angle] radians around Y - */ - public void rotateY(double angle) { - final double cosAngle = Math.cos(angle); - final double sinAngle = Math.sin(angle); - final double t1 = _m4storage[0] * cosAngle + _m4storage[8] * -sinAngle; - final double t2 = _m4storage[1] * cosAngle + _m4storage[9] * -sinAngle; - final double t3 = _m4storage[2] * cosAngle + _m4storage[10] * -sinAngle; - final double t4 = _m4storage[3] * cosAngle + _m4storage[11] * -sinAngle; - final double t5 = _m4storage[0] * sinAngle + _m4storage[8] * cosAngle; - final double t6 = _m4storage[1] * sinAngle + _m4storage[9] * cosAngle; - final double t7 = _m4storage[2] * sinAngle + _m4storage[10] * cosAngle; - final double t8 = _m4storage[3] * sinAngle + _m4storage[11] * cosAngle; - _m4storage[0] = t1; - _m4storage[1] = t2; - _m4storage[2] = t3; - _m4storage[3] = t4; - _m4storage[8] = t5; - _m4storage[9] = t6; - _m4storage[10] = t7; - _m4storage[11] = t8; - } - - /** - * Rotate this matrix [angle] radians around Z - */ - public void rotateZ(double angle) { - final double cosAngle = Math.cos(angle); - final double sinAngle = Math.sin(angle); - final double t1 = _m4storage[0] * cosAngle + _m4storage[4] * sinAngle; - final double t2 = _m4storage[1] * cosAngle + _m4storage[5] * sinAngle; - final double t3 = _m4storage[2] * cosAngle + _m4storage[6] * sinAngle; - final double t4 = _m4storage[3] * cosAngle + _m4storage[7] * sinAngle; - final double t5 = _m4storage[0] * -sinAngle + _m4storage[4] * cosAngle; - final double t6 = _m4storage[1] * -sinAngle + _m4storage[5] * cosAngle; - final double t7 = _m4storage[2] * -sinAngle + _m4storage[6] * cosAngle; - final double t8 = _m4storage[3] * -sinAngle + _m4storage[7] * cosAngle; - _m4storage[0] = t1; - _m4storage[1] = t2; - _m4storage[2] = t3; - _m4storage[3] = t4; - _m4storage[4] = t5; - _m4storage[5] = t6; - _m4storage[6] = t7; - _m4storage[7] = t8; - } - - /** - * Scale this matrix by a [Vector3], [Vector4], or x,y,z - */ - public void scale(double sx) { - scale(sx, sx, sx); - } - - public void scale(Vector3 v) { - scale(v.getX(), v.getY(), v.getZ()); - } - - public void scale(Vector4 v) { - scale(v.getX(), v.getY(), v.getZ(), v.getW()); - } - - public void scale(double sx, double sy, double sz) { - scale(sx, sy, sz, 1.0); - } - - public void scale(double sx, double sy, double sz, double sw) { - _m4storage[0] *= sx; - _m4storage[1] *= sx; - _m4storage[2] *= sx; - _m4storage[3] *= sx; - _m4storage[4] *= sy; - _m4storage[5] *= sy; - _m4storage[6] *= sy; - _m4storage[7] *= sy; - _m4storage[8] *= sz; - _m4storage[9] *= sz; - _m4storage[10] *= sz; - _m4storage[11] *= sz; - _m4storage[12] *= sw; - _m4storage[13] *= sw; - _m4storage[14] *= sw; - _m4storage[15] *= sw; - } - - /** - * Create a copy of [this] scaled by a [Vector3], [Vector4] or [x],[y], and - * [z]. - */ - public Matrix4 scaled(double x) { - return scaled(x, 1, 1); - } - - public Matrix4 scaled(double x, double y) { - return scaled(x, y, 1); - } - - public Matrix4 scaled(double x, double y, double z) { - final Matrix4 ret = clone(); - ret.scale(x, y, z); - return ret; - } - - /** - * Zeros [this]. - */ - public void setZero() { - _m4storage[0] = 0.0; - _m4storage[1] = 0.0; - _m4storage[2] = 0.0; - _m4storage[3] = 0.0; - _m4storage[4] = 0.0; - _m4storage[5] = 0.0; - _m4storage[6] = 0.0; - _m4storage[7] = 0.0; - _m4storage[8] = 0.0; - _m4storage[9] = 0.0; - _m4storage[10] = 0.0; - _m4storage[11] = 0.0; - _m4storage[12] = 0.0; - _m4storage[13] = 0.0; - _m4storage[14] = 0.0; - _m4storage[15] = 0.0; - } - - /** - * Makes [this] into the identity matrix. - */ - void setIdentity() { - _m4storage[0] = 1.0; - _m4storage[1] = 0.0; - _m4storage[2] = 0.0; - _m4storage[3] = 0.0; - _m4storage[4] = 0.0; - _m4storage[5] = 1.0; - _m4storage[6] = 0.0; - _m4storage[7] = 0.0; - _m4storage[8] = 0.0; - _m4storage[9] = 0.0; - _m4storage[10] = 1.0; - _m4storage[11] = 0.0; - _m4storage[12] = 0.0; - _m4storage[13] = 0.0; - _m4storage[14] = 0.0; - _m4storage[15] = 1.0; - } - - /** - * Returns the tranpose of this. - */ - public Matrix4 transposed() { - final Matrix4 ret = clone(); - ret.transpose(); - return ret; - } - - public void transpose() { - double temp; - temp = _m4storage[4]; - _m4storage[4] = _m4storage[1]; - _m4storage[1] = temp; - temp = _m4storage[8]; - _m4storage[8] = _m4storage[2]; - _m4storage[2] = temp; - temp = _m4storage[12]; - _m4storage[12] = _m4storage[3]; - _m4storage[3] = temp; - temp = _m4storage[9]; - _m4storage[9] = _m4storage[6]; - _m4storage[6] = temp; - temp = _m4storage[13]; - _m4storage[13] = _m4storage[7]; - _m4storage[7] = temp; - temp = _m4storage[14]; - _m4storage[14] = _m4storage[11]; - _m4storage[11] = temp; - } - - /** - * Returns the component wise absolute value of this. - */ - public Matrix4 absolute() { - final Matrix4 r = new Matrix4(); - final double[] rStorage = r._m4storage; - rStorage[0] = Math.abs(_m4storage[0]); - rStorage[1] = Math.abs(_m4storage[1]); - rStorage[2] = Math.abs(_m4storage[2]); - rStorage[3] = Math.abs(_m4storage[3]); - rStorage[4] = Math.abs(_m4storage[4]); - rStorage[5] = Math.abs(_m4storage[5]); - rStorage[6] = Math.abs(_m4storage[6]); - rStorage[7] = Math.abs(_m4storage[7]); - rStorage[8] = Math.abs(_m4storage[8]); - rStorage[9] = Math.abs(_m4storage[9]); - rStorage[10] = Math.abs(_m4storage[10]); - rStorage[11] = Math.abs(_m4storage[11]); - rStorage[12] = Math.abs(_m4storage[12]); - rStorage[13] = Math.abs(_m4storage[13]); - rStorage[14] = Math.abs(_m4storage[14]); - rStorage[15] = Math.abs(_m4storage[15]); - return r; - } - - /** - * Returns the determinant of this matrix. - */ - public double determinant() { - final double det2_01_01 = - _m4storage[0] * _m4storage[5] - _m4storage[1] * _m4storage[4]; - final double det2_01_02 = - _m4storage[0] * _m4storage[6] - _m4storage[2] * _m4storage[4]; - final double det2_01_03 = - _m4storage[0] * _m4storage[7] - _m4storage[3] * _m4storage[4]; - final double det2_01_12 = - _m4storage[1] * _m4storage[6] - _m4storage[2] * _m4storage[5]; - final double det2_01_13 = - _m4storage[1] * _m4storage[7] - _m4storage[3] * _m4storage[5]; - final double det2_01_23 = - _m4storage[2] * _m4storage[7] - _m4storage[3] * _m4storage[6]; - final double det3_201_012 = _m4storage[8] * det2_01_12 - - _m4storage[9] * det2_01_02 + - _m4storage[10] * det2_01_01; - final double det3_201_013 = _m4storage[8] * det2_01_13 - - _m4storage[9] * det2_01_03 + - _m4storage[11] * det2_01_01; - final double det3_201_023 = _m4storage[8] * det2_01_23 - - _m4storage[10] * det2_01_03 + - _m4storage[11] * det2_01_02; - final double det3_201_123 = _m4storage[9] * det2_01_23 - - _m4storage[10] * det2_01_13 + - _m4storage[11] * det2_01_12; - return -det3_201_123 * _m4storage[12] + - det3_201_023 * _m4storage[13] - - det3_201_013 * _m4storage[14] + - det3_201_012 * _m4storage[15]; - } - - /** - * Returns the dot product of row [i] and [v]. - */ - public double dotRow(int i, Vector4 v) { - final double[] vStorage = v._v4storage; - return _m4storage[i] * vStorage[0] + - _m4storage[4 + i] * vStorage[1] + - _m4storage[8 + i] * vStorage[2] + - _m4storage[12 + i] * vStorage[3]; - } - - /** - * Returns the dot product of column [j] and [v]. - */ - public double dotColumn(int j, Vector4 v) { - final double[] vStorage = v._v4storage; - return _m4storage[j * 4] * vStorage[0] + - _m4storage[j * 4 + 1] * vStorage[1] + - _m4storage[j * 4 + 2] * vStorage[2] + - _m4storage[j * 4 + 3] * vStorage[3]; - } - - /** - * Returns the trace of the matrix. The trace of a matrix is the sum of the - * diagonal entries. - */ - public double trace() { - double t = 0.0; - t += _m4storage[0]; - t += _m4storage[5]; - t += _m4storage[10]; - t += _m4storage[15]; - return t; - } - - /** - * Returns infinity norm of the matrix. Used for numerical analysis. - */ - public double infinityNorm() { - double norm = 0.0; - { - double row_norm = 0.0; - row_norm += Math.abs(_m4storage[0]); - row_norm += Math.abs(_m4storage[1]); - row_norm += Math.abs(_m4storage[2]); - row_norm += Math.abs(_m4storage[3]); - norm = Math.max(row_norm, norm); - } - { - double row_norm = 0.0; - row_norm += Math.abs(_m4storage[4]); - row_norm += Math.abs(_m4storage[5]); - row_norm += Math.abs(_m4storage[6]); - row_norm += Math.abs(_m4storage[7]); - norm = Math.max(row_norm, norm); - } - { - double row_norm = 0.0; - row_norm += Math.abs(_m4storage[8]); - row_norm += Math.abs(_m4storage[9]); - row_norm += Math.abs(_m4storage[10]); - row_norm += Math.abs(_m4storage[11]); - norm = Math.max(row_norm, norm); - } - { - double row_norm = 0.0; - row_norm += Math.abs(_m4storage[12]); - row_norm += Math.abs(_m4storage[13]); - row_norm += Math.abs(_m4storage[14]); - row_norm += Math.abs(_m4storage[15]); - norm = Math.max(row_norm, norm); - } - return norm; - } - - /** - * Returns relative error between [this] and [correct] - */ - public double relativeError(Matrix4 correct) { - final Matrix4 diff = correct.operatorSub(this); - final double correct_norm = correct.infinityNorm(); - final double diff_norm = diff.infinityNorm(); - return diff_norm / correct_norm; - } - - /** - * Returns absolute error between [this] and [correct] - */ - public double absoluteError(Matrix4 correct) { - final double this_norm = infinityNorm(); - final double correct_norm = correct.infinityNorm(); - final double diff_norm = Math.abs(this_norm - correct_norm); - return diff_norm; - } - - /** - * Returns the translation vector from this homogeneous transformation matrix. - */ - public Vector3 getTranslation() { - final double z = _m4storage[14]; - final double y = _m4storage[13]; - final double x = _m4storage[12]; - return new Vector3(x, y, z); - } - - /** - * Sets the translation vector in this homogeneous transformation matrix. - */ - public void setTranslation(Vector3 t) { - final double[] tStorage = t._v3storage; - final double z = tStorage[2]; - final double y = tStorage[1]; - final double x = tStorage[0]; - _m4storage[14] = z; - _m4storage[13] = y; - _m4storage[12] = x; - } - - /** - * Sets the translation vector in this homogeneous transformation matrix. - */ - public void setTranslationRaw(double x, double y, double z) { - _m4storage[14] = z; - _m4storage[13] = y; - _m4storage[12] = x; - } - - /** - * Returns the max scale value of the 3 axes. - */ - public double getMaxScaleOnAxis() { - final double scaleXSq = _m4storage[0] * _m4storage[0] + - _m4storage[1] * _m4storage[1] + - _m4storage[2] * _m4storage[2]; - final double scaleYSq = _m4storage[4] * _m4storage[4] + - _m4storage[5] * _m4storage[5] + - _m4storage[6] * _m4storage[6]; - final double scaleZSq = _m4storage[8] * _m4storage[8] + - _m4storage[9] * _m4storage[9] + - _m4storage[10] * _m4storage[10]; - return Math.sqrt(Math.max(scaleXSq, Math.max(scaleYSq, scaleZSq))); - } - - /** - * Transposes just the upper 3x3 rotation matrix. - */ - public void transposeRotation() { - double temp; - temp = _m4storage[1]; - _m4storage[1] = _m4storage[4]; - _m4storage[4] = temp; - temp = _m4storage[2]; - _m4storage[2] = _m4storage[8]; - _m4storage[8] = temp; - temp = _m4storage[4]; - _m4storage[4] = _m4storage[1]; - _m4storage[1] = temp; - temp = _m4storage[6]; - _m4storage[6] = _m4storage[9]; - _m4storage[9] = temp; - temp = _m4storage[8]; - _m4storage[8] = _m4storage[2]; - _m4storage[2] = temp; - temp = _m4storage[9]; - _m4storage[9] = _m4storage[6]; - _m4storage[6] = temp; - } - - /** - * Invert [this]. - */ - public double invert() { - return copyInverse(this); - } - - /** - * Set this matrix to be the inverse of [arg] - */ - public double copyInverse(Matrix4 arg) { - final double[] argStorage = arg._m4storage; - final double a00 = argStorage[0]; - final double a01 = argStorage[1]; - final double a02 = argStorage[2]; - final double a03 = argStorage[3]; - final double a10 = argStorage[4]; - final double a11 = argStorage[5]; - final double a12 = argStorage[6]; - final double a13 = argStorage[7]; - final double a20 = argStorage[8]; - final double a21 = argStorage[9]; - final double a22 = argStorage[10]; - final double a23 = argStorage[11]; - final double a30 = argStorage[12]; - final double a31 = argStorage[13]; - final double a32 = argStorage[14]; - final double a33 = argStorage[15]; - final double b00 = a00 * a11 - a01 * a10; - final double b01 = a00 * a12 - a02 * a10; - final double b02 = a00 * a13 - a03 * a10; - final double b03 = a01 * a12 - a02 * a11; - final double b04 = a01 * a13 - a03 * a11; - final double b05 = a02 * a13 - a03 * a12; - final double b06 = a20 * a31 - a21 * a30; - final double b07 = a20 * a32 - a22 * a30; - final double b08 = a20 * a33 - a23 * a30; - final double b09 = a21 * a32 - a22 * a31; - final double b10 = a21 * a33 - a23 * a31; - final double b11 = a22 * a33 - a23 * a32; - final double det = - (b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06); - if (det == 0.0) { - setFrom(arg); - return 0.0; - } - final double invDet = 1.0 / det; - _m4storage[0] = (a11 * b11 - a12 * b10 + a13 * b09) * invDet; - _m4storage[1] = (-a01 * b11 + a02 * b10 - a03 * b09) * invDet; - _m4storage[2] = (a31 * b05 - a32 * b04 + a33 * b03) * invDet; - _m4storage[3] = (-a21 * b05 + a22 * b04 - a23 * b03) * invDet; - _m4storage[4] = (-a10 * b11 + a12 * b08 - a13 * b07) * invDet; - _m4storage[5] = (a00 * b11 - a02 * b08 + a03 * b07) * invDet; - _m4storage[6] = (-a30 * b05 + a32 * b02 - a33 * b01) * invDet; - _m4storage[7] = (a20 * b05 - a22 * b02 + a23 * b01) * invDet; - _m4storage[8] = (a10 * b10 - a11 * b08 + a13 * b06) * invDet; - _m4storage[9] = (-a00 * b10 + a01 * b08 - a03 * b06) * invDet; - _m4storage[10] = (a30 * b04 - a31 * b02 + a33 * b00) * invDet; - _m4storage[11] = (-a20 * b04 + a21 * b02 - a23 * b00) * invDet; - _m4storage[12] = (-a10 * b09 + a11 * b07 - a12 * b06) * invDet; - _m4storage[13] = (a00 * b09 - a01 * b07 + a02 * b06) * invDet; - _m4storage[14] = (-a30 * b03 + a31 * b01 - a32 * b00) * invDet; - _m4storage[15] = (a20 * b03 - a21 * b01 + a22 * b00) * invDet; - return det; - } - - public double invertRotation() { - final double det = determinant(); - if (det == 0.0) { - return 0.0; - } - final double invDet = 1.0 / det; - final double ix; - final double iy; - final double iz; - final double jx; - final double jy; - final double jz; - final double kx; - final double ky; - final double kz; - ix = invDet * - (_m4storage[5] * _m4storage[10] - _m4storage[6] * _m4storage[9]); - iy = invDet * - (_m4storage[2] * _m4storage[9] - _m4storage[1] * _m4storage[10]); - iz = invDet * - (_m4storage[1] * _m4storage[6] - _m4storage[2] * _m4storage[5]); - jx = invDet * - (_m4storage[6] * _m4storage[8] - _m4storage[4] * _m4storage[10]); - jy = invDet * - (_m4storage[0] * _m4storage[10] - _m4storage[2] * _m4storage[8]); - jz = invDet * - (_m4storage[2] * _m4storage[4] - _m4storage[0] * _m4storage[6]); - kx = invDet * - (_m4storage[4] * _m4storage[9] - _m4storage[5] * _m4storage[8]); - ky = invDet * - (_m4storage[1] * _m4storage[8] - _m4storage[0] * _m4storage[9]); - kz = invDet * - (_m4storage[0] * _m4storage[5] - _m4storage[1] * _m4storage[4]); - _m4storage[0] = ix; - _m4storage[1] = iy; - _m4storage[2] = iz; - _m4storage[4] = jx; - _m4storage[5] = jy; - _m4storage[6] = jz; - _m4storage[8] = kx; - _m4storage[9] = ky; - _m4storage[10] = kz; - return det; - } - - /** - * Sets the upper 3x3 to a rotation of [radians] around X - */ - public void setRotationX(double radians) { - final double c = Math.cos(radians); - final double s = Math.sin(radians); - _m4storage[0] = 1.0; - _m4storage[1] = 0.0; - _m4storage[2] = 0.0; - _m4storage[4] = 0.0; - _m4storage[5] = c; - _m4storage[6] = s; - _m4storage[8] = 0.0; - _m4storage[9] = -s; - _m4storage[10] = c; - _m4storage[3] = 0.0; - _m4storage[7] = 0.0; - _m4storage[11] = 0.0; - } - - /** - * Sets the upper 3x3 to a rotation of [radians] around Y - */ - public void setRotationY(double radians) { - final double c = Math.cos(radians); - final double s = Math.sin(radians); - _m4storage[0] = c; - _m4storage[1] = 0.0; - _m4storage[2] = -s; - _m4storage[4] = 0.0; - _m4storage[5] = 1.0; - _m4storage[6] = 0.0; - _m4storage[8] = s; - _m4storage[9] = 0.0; - _m4storage[10] = c; - _m4storage[3] = 0.0; - _m4storage[7] = 0.0; - _m4storage[11] = 0.0; - } - - /** - * Sets the upper 3x3 to a rotation of [radians] around Z - */ - public void setRotationZ(double radians) { - final double c = Math.cos(radians); - final double s = Math.sin(radians); - _m4storage[0] = c; - _m4storage[1] = s; - _m4storage[2] = 0.0; - _m4storage[4] = -s; - _m4storage[5] = c; - _m4storage[6] = 0.0; - _m4storage[8] = 0.0; - _m4storage[9] = 0.0; - _m4storage[10] = 1.0; - _m4storage[3] = 0.0; - _m4storage[7] = 0.0; - _m4storage[11] = 0.0; - } - - /** - * Converts into Adjugate matrix and scales by [scale] - */ - public void scaleAdjoint(double scale) { - // Adapted from code by Richard Carling. - final double a1 = _m4storage[0]; - final double b1 = _m4storage[4]; - final double c1 = _m4storage[8]; - final double d1 = _m4storage[12]; - final double a2 = _m4storage[1]; - final double b2 = _m4storage[5]; - final double c2 = _m4storage[9]; - final double d2 = _m4storage[13]; - final double a3 = _m4storage[2]; - final double b3 = _m4storage[6]; - final double c3 = _m4storage[10]; - final double d3 = _m4storage[14]; - final double a4 = _m4storage[3]; - final double b4 = _m4storage[7]; - final double c4 = _m4storage[11]; - final double d4 = _m4storage[15]; - _m4storage[0] = (b2 * (c3 * d4 - c4 * d3) - - c2 * (b3 * d4 - b4 * d3) + - d2 * (b3 * c4 - b4 * c3)) * - scale; - _m4storage[1] = -(a2 * (c3 * d4 - c4 * d3) - - c2 * (a3 * d4 - a4 * d3) + - d2 * (a3 * c4 - a4 * c3)) * - scale; - _m4storage[2] = (a2 * (b3 * d4 - b4 * d3) - - b2 * (a3 * d4 - a4 * d3) + - d2 * (a3 * b4 - a4 * b3)) * - scale; - _m4storage[3] = -(a2 * (b3 * c4 - b4 * c3) - - b2 * (a3 * c4 - a4 * c3) + - c2 * (a3 * b4 - a4 * b3)) * - scale; - _m4storage[4] = -(b1 * (c3 * d4 - c4 * d3) - - c1 * (b3 * d4 - b4 * d3) + - d1 * (b3 * c4 - b4 * c3)) * - scale; - _m4storage[5] = (a1 * (c3 * d4 - c4 * d3) - - c1 * (a3 * d4 - a4 * d3) + - d1 * (a3 * c4 - a4 * c3)) * - scale; - _m4storage[6] = -(a1 * (b3 * d4 - b4 * d3) - - b1 * (a3 * d4 - a4 * d3) + - d1 * (a3 * b4 - a4 * b3)) * - scale; - _m4storage[7] = (a1 * (b3 * c4 - b4 * c3) - - b1 * (a3 * c4 - a4 * c3) + - c1 * (a3 * b4 - a4 * b3)) * - scale; - _m4storage[8] = (b1 * (c2 * d4 - c4 * d2) - - c1 * (b2 * d4 - b4 * d2) + - d1 * (b2 * c4 - b4 * c2)) * - scale; - _m4storage[9] = -(a1 * (c2 * d4 - c4 * d2) - - c1 * (a2 * d4 - a4 * d2) + - d1 * (a2 * c4 - a4 * c2)) * - scale; - _m4storage[10] = (a1 * (b2 * d4 - b4 * d2) - - b1 * (a2 * d4 - a4 * d2) + - d1 * (a2 * b4 - a4 * b2)) * - scale; - _m4storage[11] = -(a1 * (b2 * c4 - b4 * c2) - - b1 * (a2 * c4 - a4 * c2) + - c1 * (a2 * b4 - a4 * b2)) * - scale; - _m4storage[12] = -(b1 * (c2 * d3 - c3 * d2) - - c1 * (b2 * d3 - b3 * d2) + - d1 * (b2 * c3 - b3 * c2)) * - scale; - _m4storage[13] = (a1 * (c2 * d3 - c3 * d2) - - c1 * (a2 * d3 - a3 * d2) + - d1 * (a2 * c3 - a3 * c2)) * - scale; - _m4storage[14] = -(a1 * (b2 * d3 - b3 * d2) - - b1 * (a2 * d3 - a3 * d2) + - d1 * (a2 * b3 - a3 * b2)) * - scale; - _m4storage[15] = (a1 * (b2 * c3 - b3 * c2) - - b1 * (a2 * c3 - a3 * c2) + - c1 * (a2 * b3 - a3 * b2)) * - scale; - } - - /** - * Rotates [arg] by the absolute rotation of [this] - * Returns [arg]. - * Primarily used by AABB transformation code. - */ - public Vector3 absoluteRotate(Vector3 arg) { - final double m00 = Math.abs(_m4storage[0]); - final double m01 = Math.abs(_m4storage[4]); - final double m02 = Math.abs(_m4storage[8]); - final double m10 = Math.abs(_m4storage[1]); - final double m11 = Math.abs(_m4storage[5]); - final double m12 = Math.abs(_m4storage[9]); - final double m20 = Math.abs(_m4storage[2]); - final double m21 = Math.abs(_m4storage[6]); - final double m22 = Math.abs(_m4storage[10]); - final double[] argStorage = arg._v3storage; - final double x = argStorage[0]; - final double y = argStorage[1]; - final double z = argStorage[2]; - argStorage[0] = x * m00 + y * m01 + z * m02 + 0.0 * 0.0; - argStorage[1] = x * m10 + y * m11 + z * m12 + 0.0 * 0.0; - argStorage[2] = x * m20 + y * m21 + z * m22 + 0.0 * 0.0; - return arg; - } - - /** - * Adds [o] to [this]. - */ - public void add(Matrix4 o) { - final double[] oStorage = o._m4storage; - _m4storage[0] = _m4storage[0] + oStorage[0]; - _m4storage[1] = _m4storage[1] + oStorage[1]; - _m4storage[2] = _m4storage[2] + oStorage[2]; - _m4storage[3] = _m4storage[3] + oStorage[3]; - _m4storage[4] = _m4storage[4] + oStorage[4]; - _m4storage[5] = _m4storage[5] + oStorage[5]; - _m4storage[6] = _m4storage[6] + oStorage[6]; - _m4storage[7] = _m4storage[7] + oStorage[7]; - _m4storage[8] = _m4storage[8] + oStorage[8]; - _m4storage[9] = _m4storage[9] + oStorage[9]; - _m4storage[10] = _m4storage[10] + oStorage[10]; - _m4storage[11] = _m4storage[11] + oStorage[11]; - _m4storage[12] = _m4storage[12] + oStorage[12]; - _m4storage[13] = _m4storage[13] + oStorage[13]; - _m4storage[14] = _m4storage[14] + oStorage[14]; - _m4storage[15] = _m4storage[15] + oStorage[15]; - } - - /** - * Subtracts [o] from [this]. - */ - public void sub(Matrix4 o) { - final double[] oStorage = o._m4storage; - _m4storage[0] = _m4storage[0] - oStorage[0]; - _m4storage[1] = _m4storage[1] - oStorage[1]; - _m4storage[2] = _m4storage[2] - oStorage[2]; - _m4storage[3] = _m4storage[3] - oStorage[3]; - _m4storage[4] = _m4storage[4] - oStorage[4]; - _m4storage[5] = _m4storage[5] - oStorage[5]; - _m4storage[6] = _m4storage[6] - oStorage[6]; - _m4storage[7] = _m4storage[7] - oStorage[7]; - _m4storage[8] = _m4storage[8] - oStorage[8]; - _m4storage[9] = _m4storage[9] - oStorage[9]; - _m4storage[10] = _m4storage[10] - oStorage[10]; - _m4storage[11] = _m4storage[11] - oStorage[11]; - _m4storage[12] = _m4storage[12] - oStorage[12]; - _m4storage[13] = _m4storage[13] - oStorage[13]; - _m4storage[14] = _m4storage[14] - oStorage[14]; - _m4storage[15] = _m4storage[15] - oStorage[15]; - } - - /** - * Negate [this]. - */ - public void negate() { - _m4storage[0] = -_m4storage[0]; - _m4storage[1] = -_m4storage[1]; - _m4storage[2] = -_m4storage[2]; - _m4storage[3] = -_m4storage[3]; - _m4storage[4] = -_m4storage[4]; - _m4storage[5] = -_m4storage[5]; - _m4storage[6] = -_m4storage[6]; - _m4storage[7] = -_m4storage[7]; - _m4storage[8] = -_m4storage[8]; - _m4storage[9] = -_m4storage[9]; - _m4storage[10] = -_m4storage[10]; - _m4storage[11] = -_m4storage[11]; - _m4storage[12] = -_m4storage[12]; - _m4storage[13] = -_m4storage[13]; - _m4storage[14] = -_m4storage[14]; - _m4storage[15] = -_m4storage[15]; - } - - /** - * Multiply [this] by [arg]. - */ - public void multiply(Matrix4 arg) { - final double m00 = _m4storage[0]; - final double m01 = _m4storage[4]; - final double m02 = _m4storage[8]; - final double m03 = _m4storage[12]; - final double m10 = _m4storage[1]; - final double m11 = _m4storage[5]; - final double m12 = _m4storage[9]; - final double m13 = _m4storage[13]; - final double m20 = _m4storage[2]; - final double m21 = _m4storage[6]; - final double m22 = _m4storage[10]; - final double m23 = _m4storage[14]; - final double m30 = _m4storage[3]; - final double m31 = _m4storage[7]; - final double m32 = _m4storage[11]; - final double m33 = _m4storage[15]; - final double[] argStorage = arg._m4storage; - final double n00 = argStorage[0]; - final double n01 = argStorage[4]; - final double n02 = argStorage[8]; - final double n03 = argStorage[12]; - final double n10 = argStorage[1]; - final double n11 = argStorage[5]; - final double n12 = argStorage[9]; - final double n13 = argStorage[13]; - final double n20 = argStorage[2]; - final double n21 = argStorage[6]; - final double n22 = argStorage[10]; - final double n23 = argStorage[14]; - final double n30 = argStorage[3]; - final double n31 = argStorage[7]; - final double n32 = argStorage[11]; - final double n33 = argStorage[15]; - _m4storage[0] = (m00 * n00) + (m01 * n10) + (m02 * n20) + (m03 * n30); - _m4storage[4] = (m00 * n01) + (m01 * n11) + (m02 * n21) + (m03 * n31); - _m4storage[8] = (m00 * n02) + (m01 * n12) + (m02 * n22) + (m03 * n32); - _m4storage[12] = (m00 * n03) + (m01 * n13) + (m02 * n23) + (m03 * n33); - _m4storage[1] = (m10 * n00) + (m11 * n10) + (m12 * n20) + (m13 * n30); - _m4storage[5] = (m10 * n01) + (m11 * n11) + (m12 * n21) + (m13 * n31); - _m4storage[9] = (m10 * n02) + (m11 * n12) + (m12 * n22) + (m13 * n32); - _m4storage[13] = (m10 * n03) + (m11 * n13) + (m12 * n23) + (m13 * n33); - _m4storage[2] = (m20 * n00) + (m21 * n10) + (m22 * n20) + (m23 * n30); - _m4storage[6] = (m20 * n01) + (m21 * n11) + (m22 * n21) + (m23 * n31); - _m4storage[10] = (m20 * n02) + (m21 * n12) + (m22 * n22) + (m23 * n32); - _m4storage[14] = (m20 * n03) + (m21 * n13) + (m22 * n23) + (m23 * n33); - _m4storage[3] = (m30 * n00) + (m31 * n10) + (m32 * n20) + (m33 * n30); - _m4storage[7] = (m30 * n01) + (m31 * n11) + (m32 * n21) + (m33 * n31); - _m4storage[11] = (m30 * n02) + (m31 * n12) + (m32 * n22) + (m33 * n32); - _m4storage[15] = (m30 * n03) + (m31 * n13) + (m32 * n23) + (m33 * n33); - } - - /** - * Multiply a copy of [this] with [arg]. - */ - public Matrix4 multiplied(Matrix4 arg) { - final Matrix4 ret = clone(); - ret.multiply(arg); - return ret; - } - - /** - * Multiply a transposed [this] with [arg]. - */ - public void transposeMultiply(Matrix4 arg) { - final double m00 = _m4storage[0]; - final double m01 = _m4storage[1]; - final double m02 = _m4storage[2]; - final double m03 = _m4storage[3]; - final double m10 = _m4storage[4]; - final double m11 = _m4storage[5]; - final double m12 = _m4storage[6]; - final double m13 = _m4storage[7]; - final double m20 = _m4storage[8]; - final double m21 = _m4storage[9]; - final double m22 = _m4storage[10]; - final double m23 = _m4storage[11]; - final double m30 = _m4storage[12]; - final double m31 = _m4storage[13]; - final double m32 = _m4storage[14]; - final double m33 = _m4storage[15]; - final double[] argStorage = arg._m4storage; - _m4storage[0] = (m00 * argStorage[0]) + - (m01 * argStorage[1]) + - (m02 * argStorage[2]) + - (m03 * argStorage[3]); - _m4storage[4] = (m00 * argStorage[4]) + - (m01 * argStorage[5]) + - (m02 * argStorage[6]) + - (m03 * argStorage[7]); - _m4storage[8] = (m00 * argStorage[8]) + - (m01 * argStorage[9]) + - (m02 * argStorage[10]) + - (m03 * argStorage[11]); - _m4storage[12] = (m00 * argStorage[12]) + - (m01 * argStorage[13]) + - (m02 * argStorage[14]) + - (m03 * argStorage[15]); - _m4storage[1] = (m10 * argStorage[0]) + - (m11 * argStorage[1]) + - (m12 * argStorage[2]) + - (m13 * argStorage[3]); - _m4storage[5] = (m10 * argStorage[4]) + - (m11 * argStorage[5]) + - (m12 * argStorage[6]) + - (m13 * argStorage[7]); - _m4storage[9] = (m10 * argStorage[8]) + - (m11 * argStorage[9]) + - (m12 * argStorage[10]) + - (m13 * argStorage[11]); - _m4storage[13] = (m10 * argStorage[12]) + - (m11 * argStorage[13]) + - (m12 * argStorage[14]) + - (m13 * argStorage[15]); - _m4storage[2] = (m20 * argStorage[0]) + - (m21 * argStorage[1]) + - (m22 * argStorage[2]) + - (m23 * argStorage[3]); - _m4storage[6] = (m20 * argStorage[4]) + - (m21 * argStorage[5]) + - (m22 * argStorage[6]) + - (m23 * argStorage[7]); - _m4storage[10] = (m20 * argStorage[8]) + - (m21 * argStorage[9]) + - (m22 * argStorage[10]) + - (m23 * argStorage[11]); - _m4storage[14] = (m20 * argStorage[12]) + - (m21 * argStorage[13]) + - (m22 * argStorage[14]) + - (m23 * argStorage[15]); - _m4storage[3] = (m30 * argStorage[0]) + - (m31 * argStorage[1]) + - (m32 * argStorage[2]) + - (m33 * argStorage[3]); - _m4storage[7] = (m30 * argStorage[4]) + - (m31 * argStorage[5]) + - (m32 * argStorage[6]) + - (m33 * argStorage[7]); - _m4storage[11] = (m30 * argStorage[8]) + - (m31 * argStorage[9]) + - (m32 * argStorage[10]) + - (m33 * argStorage[11]); - _m4storage[15] = (m30 * argStorage[12]) + - (m31 * argStorage[13]) + - (m32 * argStorage[14]) + - (m33 * argStorage[15]); - } - - /** - * Multiply [this] with a transposed [arg]. - */ - public void multiplyTranspose(Matrix4 arg) { - final double m00 = _m4storage[0]; - final double m01 = _m4storage[4]; - final double m02 = _m4storage[8]; - final double m03 = _m4storage[12]; - final double m10 = _m4storage[1]; - final double m11 = _m4storage[5]; - final double m12 = _m4storage[9]; - final double m13 = _m4storage[13]; - final double m20 = _m4storage[2]; - final double m21 = _m4storage[6]; - final double m22 = _m4storage[10]; - final double m23 = _m4storage[14]; - final double m30 = _m4storage[3]; - final double m31 = _m4storage[7]; - final double m32 = _m4storage[11]; - final double m33 = _m4storage[15]; - final double[] argStorage = arg._m4storage; - _m4storage[0] = (m00 * argStorage[0]) + - (m01 * argStorage[4]) + - (m02 * argStorage[8]) + - (m03 * argStorage[12]); - _m4storage[4] = (m00 * argStorage[1]) + - (m01 * argStorage[5]) + - (m02 * argStorage[9]) + - (m03 * argStorage[13]); - _m4storage[8] = (m00 * argStorage[2]) + - (m01 * argStorage[6]) + - (m02 * argStorage[10]) + - (m03 * argStorage[14]); - _m4storage[12] = (m00 * argStorage[3]) + - (m01 * argStorage[7]) + - (m02 * argStorage[11]) + - (m03 * argStorage[15]); - _m4storage[1] = (m10 * argStorage[0]) + - (m11 * argStorage[4]) + - (m12 * argStorage[8]) + - (m13 * argStorage[12]); - _m4storage[5] = (m10 * argStorage[1]) + - (m11 * argStorage[5]) + - (m12 * argStorage[9]) + - (m13 * argStorage[13]); - _m4storage[9] = (m10 * argStorage[2]) + - (m11 * argStorage[6]) + - (m12 * argStorage[10]) + - (m13 * argStorage[14]); - _m4storage[13] = (m10 * argStorage[3]) + - (m11 * argStorage[7]) + - (m12 * argStorage[11]) + - (m13 * argStorage[15]); - _m4storage[2] = (m20 * argStorage[0]) + - (m21 * argStorage[4]) + - (m22 * argStorage[8]) + - (m23 * argStorage[12]); - _m4storage[6] = (m20 * argStorage[1]) + - (m21 * argStorage[5]) + - (m22 * argStorage[9]) + - (m23 * argStorage[13]); - _m4storage[10] = (m20 * argStorage[2]) + - (m21 * argStorage[6]) + - (m22 * argStorage[10]) + - (m23 * argStorage[14]); - _m4storage[14] = (m20 * argStorage[3]) + - (m21 * argStorage[7]) + - (m22 * argStorage[11]) + - (m23 * argStorage[15]); - _m4storage[3] = (m30 * argStorage[0]) + - (m31 * argStorage[4]) + - (m32 * argStorage[8]) + - (m33 * argStorage[12]); - _m4storage[7] = (m30 * argStorage[1]) + - (m31 * argStorage[5]) + - (m32 * argStorage[9]) + - (m33 * argStorage[13]); - _m4storage[11] = (m30 * argStorage[2]) + - (m31 * argStorage[6]) + - (m32 * argStorage[10]) + - (m33 * argStorage[14]); - _m4storage[15] = (m30 * argStorage[3]) + - (m31 * argStorage[7]) + - (m32 * argStorage[11]) + - (m33 * argStorage[15]); - } - - // TODO(jacobr): this method might be worth pulling in the matrix3 class for. - // Decomposes [this] into [translation], [rotation] and [scale] components. - /* - public void decompose(Vector3 translation, Quaternion rotation, Vector3 scale) { - final Vector3 v = new Vector3(); - v.setValues(_m4storage[0], _m4storage[1], _m4storage[2]); - double sx = v.getLength(); - v.setValues(_m4storage[4], _m4storage[5], _m4storage[6]); - final double sy = v.getLength(); - v.setValues(_m4storage[8], _m4storage[9], _m4storage[10]); - final double sz = v.getLength(); - - if (determinant() < 0) { - sx = -sx; - } - - translation._v3storage[0] = _m4storage[12]; - translation._v3storage[1] = _m4storage[13]; - translation._v3storage[2] = _m4storage[14]; - - final double invSX = 1.0 / sx; - final double invSY = 1.0 / sy; - final double invSZ = 1.0 / sz; - - final Matrix4 m = Matrix4.copy(this); - m._m4storage[0] *= invSX; - m._m4storage[1] *= invSX; - m._m4storage[2] *= invSX; - m._m4storage[4] *= invSY; - m._m4storage[5] *= invSY; - m._m4storage[6] *= invSY; - m._m4storage[8] *= invSZ; - m._m4storage[9] *= invSZ; - m._m4storage[10] *= invSZ; - - rotation.setFromRotation(m.getRotation()); - - scale._v3storage[0] = sx; - scale._v3storage[1] = sy; - scale._v3storage[2] = sz; - } -*/ - - /** - * Rotate [arg] of type [Vector3] using the rotation defined by [this]. - */ - public Vector3 rotate3(Vector3 arg) { - final double[] argStorage = arg._v3storage; - final double x_ = (_m4storage[0] * argStorage[0]) + - (_m4storage[4] * argStorage[1]) + - (_m4storage[8] * argStorage[2]); - final double y_ = (_m4storage[1] * argStorage[0]) + - (_m4storage[5] * argStorage[1]) + - (_m4storage[9] * argStorage[2]); - final double z_ = (_m4storage[2] * argStorage[0]) + - (_m4storage[6] * argStorage[1]) + - (_m4storage[10] * argStorage[2]); - argStorage[0] = x_; - argStorage[1] = y_; - argStorage[2] = z_; - return arg; - } - - /** - * Rotate a copy of [arg] of type [Vector3] using the rotation defined by - * [this]. If a [out] parameter is supplied, the copy is stored in [out]. - */ - public Vector3 rotated3(Vector3 arg, Vector3 out) { - if (out == null) { - out = Vector3.copy(arg); - } - else { - out.setFrom(arg); - } - return rotate3(out); - } - - /** - * Transform [arg] of type [Vector3] using the transformation defined by - * [this]. - */ - public Vector3 transform3(Vector3 arg) { - final double[] argStorage = arg._v3storage; - final double x_ = (_m4storage[0] * argStorage[0]) + - (_m4storage[4] * argStorage[1]) + - (_m4storage[8] * argStorage[2]) + - _m4storage[12]; - final double y_ = (_m4storage[1] * argStorage[0]) + - (_m4storage[5] * argStorage[1]) + - (_m4storage[9] * argStorage[2]) + - _m4storage[13]; - final double z_ = (_m4storage[2] * argStorage[0]) + - (_m4storage[6] * argStorage[1]) + - (_m4storage[10] * argStorage[2]) + - _m4storage[14]; - argStorage[0] = x_; - argStorage[1] = y_; - argStorage[2] = z_; - return arg; - } - - /** - * Transform a copy of [arg] of type [Vector3] using the transformation - * defined by [this]. If a [out] parameter is supplied, the copy is stored in - * [out]. - */ - public Vector3 transformed3(Vector3 arg) { - return transformed3(arg, null); - } - - public Vector3 transformed3(Vector3 arg, Vector3 out) { - if (out == null) { - out = Vector3.copy(arg); - } - else { - out.setFrom(arg); - } - return transform3(out); - } - - /** - * Transform [arg] of type [Vector4] using the transformation defined by - * [this]. - */ - public Vector4 transform(Vector4 arg) { - final double[] argStorage = arg._v4storage; - final double x_ = (_m4storage[0] * argStorage[0]) + - (_m4storage[4] * argStorage[1]) + - (_m4storage[8] * argStorage[2]) + - (_m4storage[12] * argStorage[3]); - final double y_ = (_m4storage[1] * argStorage[0]) + - (_m4storage[5] * argStorage[1]) + - (_m4storage[9] * argStorage[2]) + - (_m4storage[13] * argStorage[3]); - final double z_ = (_m4storage[2] * argStorage[0]) + - (_m4storage[6] * argStorage[1]) + - (_m4storage[10] * argStorage[2]) + - (_m4storage[14] * argStorage[3]); - final double w_ = (_m4storage[3] * argStorage[0]) + - (_m4storage[7] * argStorage[1]) + - (_m4storage[11] * argStorage[2]) + - (_m4storage[15] * argStorage[3]); - argStorage[0] = x_; - argStorage[1] = y_; - argStorage[2] = z_; - argStorage[3] = w_; - return arg; - } - - /** - * Transform [arg] of type [Vector3] using the perspective transformation - * defined by [this]. - */ - public Vector3 perspectiveTransform(Vector3 arg) { - final double[] argStorage = arg._v3storage; - final double x_ = (_m4storage[0] * argStorage[0]) + - (_m4storage[4] * argStorage[1]) + - (_m4storage[8] * argStorage[2]) + - _m4storage[12]; - final double y_ = (_m4storage[1] * argStorage[0]) + - (_m4storage[5] * argStorage[1]) + - (_m4storage[9] * argStorage[2]) + - _m4storage[13]; - final double z_ = (_m4storage[2] * argStorage[0]) + - (_m4storage[6] * argStorage[1]) + - (_m4storage[10] * argStorage[2]) + - _m4storage[14]; - final double w_ = 1.0 / - ((_m4storage[3] * argStorage[0]) + - (_m4storage[7] * argStorage[1]) + - (_m4storage[11] * argStorage[2]) + - _m4storage[15]); - argStorage[0] = x_ * w_; - argStorage[1] = y_ * w_; - argStorage[2] = z_ * w_; - return arg; - } - - /** - * Transform a copy of [arg] of type [Vector4] using the transformation - * defined by [this]. If a [out] parameter is supplied, the copy is stored in - * [out]. - */ - public Vector4 transformed(Vector4 arg) { - return transformed(arg, null); - } - - public Vector4 transformed(Vector4 arg, Vector4 out) { - if (out == null) { - out = Vector4.copy(arg); - } - else { - out.setFrom(arg); - } - return transform(out); - } - - /** - * Copies [this] into [array] starting at [offset]. - */ - void copyIntoArray(double[] array, int offset /*= 0*/) { - final int i = offset; - array[i + 15] = _m4storage[15]; - array[i + 14] = _m4storage[14]; - array[i + 13] = _m4storage[13]; - array[i + 12] = _m4storage[12]; - array[i + 11] = _m4storage[11]; - array[i + 10] = _m4storage[10]; - array[i + 9] = _m4storage[9]; - array[i + 8] = _m4storage[8]; - array[i + 7] = _m4storage[7]; - array[i + 6] = _m4storage[6]; - array[i + 5] = _m4storage[5]; - array[i + 4] = _m4storage[4]; - array[i + 3] = _m4storage[3]; - array[i + 2] = _m4storage[2]; - array[i + 1] = _m4storage[1]; - array[i + 0] = _m4storage[0]; - } - - /** - * Copies elements from [array] into [this] starting at [offset]. - */ - void copyFromArray(double[] array, int offset/* = 0*/) { - final int i = offset; - _m4storage[15] = array[i + 15]; - _m4storage[14] = array[i + 14]; - _m4storage[13] = array[i + 13]; - _m4storage[12] = array[i + 12]; - _m4storage[11] = array[i + 11]; - _m4storage[10] = array[i + 10]; - _m4storage[9] = array[i + 9]; - _m4storage[8] = array[i + 8]; - _m4storage[7] = array[i + 7]; - _m4storage[6] = array[i + 6]; - _m4storage[5] = array[i + 5]; - _m4storage[4] = array[i + 4]; - _m4storage[3] = array[i + 3]; - _m4storage[2] = array[i + 2]; - _m4storage[1] = array[i + 1]; - _m4storage[0] = array[i + 0]; - } - - /** - * Multiply [this] to each set of xyz values in [array] starting at [offset]. - */ - double[] applyToVector3Array(double[] array) { - return applyToVector3Array(array, 0); - } - - double[] applyToVector3Array(double[] array, int offset) { - for (int i = 0, j = offset; i < array.length; i += 3, j += 3) { - final Vector3 v = Vector3.array(array, j); - v.applyMatrix4(this); - array[j] = v.getStorage()[0]; - array[j + 1] = v.getStorage()[1]; - array[j + 2] = v.getStorage()[2]; - } - - return array; - } - - public Vector3 getRight() { - final double x = _m4storage[0]; - final double y = _m4storage[1]; - final double z = _m4storage[2]; - return new Vector3(x, y, z); - } - - public Vector3 getUp() { - final double x = _m4storage[4]; - final double y = _m4storage[5]; - final double z = _m4storage[6]; - return new Vector3(x, y, z); - } - - public Vector3 getForward() { - final double x = _m4storage[8]; - final double y = _m4storage[9]; - final double z = _m4storage[10]; - return new Vector3(x, y, z); - } - - /** - * Is [this] the identity matrix? - */ - public boolean isIdentity() { - return _m4storage[0] == 1.0 // col 1 - && - _m4storage[1] == 0.0 && - _m4storage[2] == 0.0 && - _m4storage[3] == 0.0 && - _m4storage[4] == 0.0 // col 2 - && - _m4storage[5] == 1.0 && - _m4storage[6] == 0.0 && - _m4storage[7] == 0.0 && - _m4storage[8] == 0.0 // col 3 - && - _m4storage[9] == 0.0 && - _m4storage[10] == 1.0 && - _m4storage[11] == 0.0 && - _m4storage[12] == 0.0 // col 4 - && - _m4storage[13] == 0.0 && - _m4storage[14] == 0.0 && - _m4storage[15] == 1.0; - } - - /** - * Is [this] the zero matrix? - */ - public boolean isZero() { - return _m4storage[0] == 0.0 // col 1 - && - _m4storage[1] == 0.0 && - _m4storage[2] == 0.0 && - _m4storage[3] == 0.0 && - _m4storage[4] == 0.0 // col 2 - && - _m4storage[5] == 0.0 && - _m4storage[6] == 0.0 && - _m4storage[7] == 0.0 && - _m4storage[8] == 0.0 // col 3 - && - _m4storage[9] == 0.0 && - _m4storage[10] == 0.0 && - _m4storage[11] == 0.0 && - _m4storage[12] == 0.0 // col 4 - && - _m4storage[13] == 0.0 && - _m4storage[14] == 0.0 && - _m4storage[15] == 0.0; - } -} diff --git a/flutter-idea/src/io/flutter/utils/math/Quaternion.java b/flutter-idea/src/io/flutter/utils/math/Quaternion.java deleted file mode 100644 index 09d0a96eeb..0000000000 --- a/flutter-idea/src/io/flutter/utils/math/Quaternion.java +++ /dev/null @@ -1,530 +0,0 @@ -/* - * Copyright 2019 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -package io.flutter.utils.math; - -/** - * Defines a [Quaternion] (a four-dimensional vector) for efficient rotation - * calculations. - *

- * This code is ported from the Quaternion class in the Dart vector_math - * package. The Class is ported as is without concern for making the code - * consistent with Java api conventions to keep the code consistent with - * the Dart code to simplify using Transform Matrixes returned by Flutter. - *

- * Quaternion are better for interpolating between rotations and avoid the - * [gimbal lock](http://de.wikipedia.org/wiki/Gimbal_Lock) problem compared to - * euler rotations. - */ -@SuppressWarnings({"Duplicates", "UnnecessaryLocalVariable"}) -class Quaternion { - final double[] _qStorage; - - private Quaternion() { - _qStorage = new double[4]; - } - - public Quaternion(Quaternion other) { - _qStorage = other._qStorage.clone(); - } - - /** - * Constructs a quaternion using the raw values [x], [y], [z], and [w]. - */ - public Quaternion(double x, double y, double z, double w) { - _qStorage = new double[4]; - setValues(x, y, z, w); - } - - /** - * Constructs a quaternion with given double[] as [storage]. - */ - public Quaternion(double[] _qStorage) { - this._qStorage = _qStorage; - } - - /** - * Constructs a quaternion from a rotation of [angle] around [axis]. - */ - public static Quaternion axisAngle(Vector3 axis, double angle) { - final Quaternion ret = new Quaternion(); - ret.setAxisAngle(axis, angle); - return ret; - } - - /** - * Constructs a quaternion to be the rotation that rotates vector [a] to [b]. - */ - public static Quaternion fromTwoVectors(Vector3 a, Vector3 b) { - final Quaternion ret = new Quaternion(); - ret.setFromTwoVectors(a, b); - return ret; - } - - /** - * Constructs a quaternion as a copy of [original]. - */ - public static Quaternion copy(Quaternion original) { - final Quaternion ret = new Quaternion(); - ret.setFrom(original); - return ret; - } - - /** - * Constructs a quaternion set to the identity quaternion. - */ - public static Quaternion identity() { - final Quaternion ret = new Quaternion(); - ret._qStorage[3] = 1.0; - return ret; - } - - /** - * Constructs a quaternion from time derivative of [q] with angular - * velocity [omega]. - */ - public static Quaternion dq(Quaternion q, Vector3 omega) { - final Quaternion ret = new Quaternion(); - ret.setDQ(q, omega); - return ret; - } - - /** - * Access the internal [storage] of the quaternions components. - */ - public double[] getStorage() { - return _qStorage; - } - - /** - * Access the [x] component of the quaternion. - */ - public double getX() { - return _qStorage[0]; - } - - public void setX(double x) { - _qStorage[0] = x; - } - - /** - * Access the [y] component of the quaternion. - */ - public double getY() { - return _qStorage[1]; - } - - public void setY(double y) { - _qStorage[1] = y; - } - - /** - * Access the [z] component of the quaternion. - */ - public double getZ() { - return _qStorage[2]; - } - - public void setZ(double z) { - _qStorage[2] = z; - } - - /** - * Access the [w] component of the quaternion. - */ - public double getW() { - return _qStorage[3]; - } - - public void setW(double w) { - _qStorage[3] = w; - } - - /** - * Constructs a quaternion from [yaw], [pitch] and [roll]. - */ - public Quaternion euler(double yaw, double pitch, double roll) { - final Quaternion ret = new Quaternion(); - ret.setEuler(yaw, pitch, roll); - return ret; - } - - /** - * Returns a new copy of [this]. - */ - @SuppressWarnings("MethodDoesntCallSuperMethod") - @Override - public Quaternion clone() { - return new Quaternion(this); - } - - /** - * Copy [source] into [this]. - */ - public void setFrom(Quaternion source) { - final double[] sourceStorage = source._qStorage; - _qStorage[0] = sourceStorage[0]; - _qStorage[1] = sourceStorage[1]; - _qStorage[2] = sourceStorage[2]; - _qStorage[3] = sourceStorage[3]; - } - - /** - * Set the quaternion to the raw values [x], [y], [z], and [w]. - */ - public void setValues(double x, double y, double z, double w) { - _qStorage[0] = x; - _qStorage[1] = y; - _qStorage[2] = z; - _qStorage[3] = w; - } - - /** - * Set the quaternion with rotation of [radians] around [axis]. - */ - public void setAxisAngle(Vector3 axis, double radians) { - final double len = axis.getLength(); - if (len == 0.0) { - return; - } - final double halfSin = Math.sin(radians * 0.5) / len; - final double[] axisStorage = axis.getStorage(); - _qStorage[0] = axisStorage[0] * halfSin; - _qStorage[1] = axisStorage[1] * halfSin; - _qStorage[2] = axisStorage[2] * halfSin; - _qStorage[3] = Math.cos(radians * 0.5); - } - - public void setFromTwoVectors(Vector3 a, Vector3 b) { - final Vector3 v1 = a.normalized(); - final Vector3 v2 = b.normalized(); - - final double c = v1.dot(v2); - double angle = Math.acos(c); - Vector3 axis = v1.cross(v2); - - if (Math.abs(1.0 + c) < 0.0005) { - // c \approx -1 indicates 180 degree rotation - angle = Math.PI; - - // a and b are parallel in opposite directions. We need any - // vector as our rotation axis that is perpendicular. - // Find one by taking the cross product of v1 with an appropriate unit axis - if (v1.getX() > v1.getY() && v1.getX() > v1.getZ()) { - // v1 points in a dominantly x direction, so don't cross with that axis - axis = v1.cross(new Vector3(0.0, 1.0, 0.0)); - } - else { - // Predominantly points in some other direction, so x-axis should be safe - axis = v1.cross(new Vector3(1.0, 0.0, 0.0)); - } - } - else if (Math.abs(1.0 - c) < 0.0005) { - // c \approx 1 is 0-degree rotation, axis is arbitrary - angle = 0.0; - axis = new Vector3(1.0, 0.0, 0.0); - } - - setAxisAngle(axis.normalized(), angle); - } - - /** - * Set the quaternion to the time derivative of [q] with angular velocity - * [omega]. - */ - public void setDQ(Quaternion q, Vector3 omega) { - final double[] qStorage = q._qStorage; - final double[] omegaStorage = omega.getStorage(); - final double qx = qStorage[0]; - final double qy = qStorage[1]; - final double qz = qStorage[2]; - final double qw = qStorage[3]; - final double ox = omegaStorage[0]; - final double oy = omegaStorage[1]; - final double oz = omegaStorage[2]; - final double _x = ox * qw + oy * qz - oz * qy; - final double _y = oy * qw + oz * qx - ox * qz; - final double _z = oz * qw + ox * qy - oy * qx; - final double _w = -ox * qx - oy * qy - oz * qz; - _qStorage[0] = _x * 0.5; - _qStorage[1] = _y * 0.5; - _qStorage[2] = _z * 0.5; - _qStorage[3] = _w * 0.5; - } - - /** - * Set quaternion with rotation of [yaw], [pitch] and [roll]. - */ - public void setEuler(double yaw, double pitch, double roll) { - final double halfYaw = yaw * 0.5; - final double halfPitch = pitch * 0.5; - final double halfRoll = roll * 0.5; - final double cosYaw = Math.cos(halfYaw); - final double sinYaw = Math.sin(halfYaw); - final double cosPitch = Math.cos(halfPitch); - final double sinPitch = Math.sin(halfPitch); - final double cosRoll = Math.cos(halfRoll); - final double sinRoll = Math.sin(halfRoll); - _qStorage[0] = cosRoll * sinPitch * cosYaw + sinRoll * cosPitch * sinYaw; - _qStorage[1] = cosRoll * cosPitch * sinYaw - sinRoll * sinPitch * cosYaw; - _qStorage[2] = sinRoll * cosPitch * cosYaw - cosRoll * sinPitch * sinYaw; - _qStorage[3] = cosRoll * cosPitch * cosYaw + sinRoll * sinPitch * sinYaw; - } - - /** - * Normalize [this]. - */ - public double normalize() { - final double l = getLength(); - if (l == 0.0) { - return 0.0; - } - final double d = 1.0 / l; - _qStorage[0] *= d; - _qStorage[1] *= d; - _qStorage[2] *= d; - _qStorage[3] *= d; - return l; - } - - /** - * Conjugate [this]. - */ - public void conjugate() { - _qStorage[2] = -_qStorage[2]; - _qStorage[1] = -_qStorage[1]; - _qStorage[0] = -_qStorage[0]; - } - - /** - * Invert [this]. - */ - public void inverse() { - final double l = 1.0 / getLength2(); - _qStorage[3] = _qStorage[3] * l; - _qStorage[2] = -_qStorage[2] * l; - _qStorage[1] = -_qStorage[1] * l; - _qStorage[0] = -_qStorage[0] * l; - } - - /** - * Normalized copy of [this]. - */ - public Quaternion normalized() { - final Quaternion ret = clone(); - ret.normalize(); - return ret; - } - - /** - * Conjugated copy of [this]. - */ - public Quaternion conjugated() { - final Quaternion ret = clone(); - ret.conjugate(); - return ret; - } - - /** - * Inverted copy of [this]. - */ - public Quaternion inverted() { - final Quaternion ret = clone(); - ret.inverse(); - return ret; - } - - /** - * [radians] of rotation around the [axis] of the rotation. - */ - public double getRadians() { - return 2.0 * Math.acos(_qStorage[3]); - } - - /** - * [axis] of rotation. - */ - public Vector3 getAxis() { - final double den = 1.0 - (_qStorage[3] * _qStorage[3]); - if (den < 0.0005) { - // 0-angle rotation, so axis does not matter - return new Vector3(); - } - - final double scale = 1.0 / Math.sqrt(den); - return new Vector3( - _qStorage[0] * scale, _qStorage[1] * scale, _qStorage[2] * scale); - } - - /** - * Length squared. - */ - public double getLength2() { - final double x = _qStorage[0]; - final double y = _qStorage[1]; - final double z = _qStorage[2]; - final double w = _qStorage[3]; - return (x * x) + (y * y) + (z * z) + (w * w); - } - - /** - * Length. - */ - public double getLength() { - return Math.sqrt(getLength2()); - } - - /** - * Returns a copy of [v] rotated by quaternion. - */ - public Vector3 rotated(Vector3 v) { - final Vector3 out = v.clone(); - rotate(out); - return out; - } - - /** - * Rotates [v] by [this]. - */ - public Vector3 rotate(Vector3 v) { - // conjugate(this) * [v,0] * this - final double _w = _qStorage[3]; - final double _z = _qStorage[2]; - final double _y = _qStorage[1]; - final double _x = _qStorage[0]; - final double tiw = _w; - final double tiz = -_z; - final double tiy = -_y; - final double tix = -_x; - final double tx = tiw * v.getX() + tix * 0.0 + tiy * v.getZ() - tiz * v.getY(); - final double ty = tiw * v.getY() + tiy * 0.0 + tiz * v.getX() - tix * v.getZ(); - final double tz = tiw * v.getZ() + tiz * 0.0 + tix * v.getY() - tiy * v.getX(); - final double tw = tiw * 0.0 - tix * v.getX() - tiy * v.getY() - tiz * v.getZ(); - final double result_x = tw * _x + tx * _w + ty * _z - tz * _y; - final double result_y = tw * _y + ty * _w + tz * _x - tx * _z; - final double result_z = tw * _z + tz * _w + tx * _y - ty * _x; - final double[] vStorage = v.getStorage(); - vStorage[2] = result_z; - vStorage[1] = result_y; - vStorage[0] = result_x; - return v; - } - - /** - * Add [arg] to [this]. - */ - public void add(Quaternion arg) { - final double[] argStorage = arg._qStorage; - _qStorage[0] = _qStorage[0] + argStorage[0]; - _qStorage[1] = _qStorage[1] + argStorage[1]; - _qStorage[2] = _qStorage[2] + argStorage[2]; - _qStorage[3] = _qStorage[3] + argStorage[3]; - } - - /** - * Subtracts [arg] from [this]. - */ - public void sub(Quaternion arg) { - final double[] argStorage = arg._qStorage; - _qStorage[0] = _qStorage[0] - argStorage[0]; - _qStorage[1] = _qStorage[1] - argStorage[1]; - _qStorage[2] = _qStorage[2] - argStorage[2]; - _qStorage[3] = _qStorage[3] - argStorage[3]; - } - - /** - * Scales [this] by [scale]. - */ - public void scale(double scale) { - _qStorage[3] = _qStorage[3] * scale; - _qStorage[2] = _qStorage[2] * scale; - _qStorage[1] = _qStorage[1] * scale; - _qStorage[0] = _qStorage[0] * scale; - } - - /** - * Scaled copy of [this]. - */ - public Quaternion scaled(double scale) { - final Quaternion ret = clone(); - ret.scale(scale); - return ret; - } - - /** - * [this] rotated by [other]. - */ - public Quaternion operatorMultiply(Quaternion other) { - final double _w = _qStorage[3]; - final double _z = _qStorage[2]; - final double _y = _qStorage[1]; - final double _x = _qStorage[0]; - final double[] otherStorage = other._qStorage; - final double ow = otherStorage[3]; - final double oz = otherStorage[2]; - final double oy = otherStorage[1]; - final double ox = otherStorage[0]; - return new Quaternion( - _w * ox + _x * ow + _y * oz - _z * oy, - _w * oy + _y * ow + _z * ox - _x * oz, - _w * oz + _z * ow + _x * oy - _y * ox, - _w * ow - _x * ox - _y * oy - _z * oz); - } - - /** - * Returns copy of [this] + [other]. - */ - public Quaternion operatorAdd(Quaternion other) { - final Quaternion ret = clone(); - ret.add(other); - return ret; - } - - /** - * Returns copy of [this] - [other]. - */ - public Quaternion operatorSub(Quaternion other) { - final Quaternion ret = clone(); - ret.sub(other); - return ret; - } - - /** - * Returns negated copy of [this]. - */ - public Quaternion operatorConjugated() { - final Quaternion ret = clone(); - ret.conjugated(); - return ret; - } - - /** - * Printable string. - */ - @Override() - public String toString() { - return "" + _qStorage[0] + ", " + _qStorage[1] + ", " + _qStorage[2] + " @ " + _qStorage[3]; - } - - /** - * Relative error between [this] and [correct]. - */ - public double relativeError(Quaternion correct) { - final Quaternion diff = correct.operatorSub(this); - final double norm_diff = diff.getLength(); - final double correct_norm = correct.getLength(); - return norm_diff / correct_norm; - } - - /** - * Absolute error between [this] and [correct]. - */ - public double absoluteError(Quaternion correct) { - final double this_norm = getLength(); - final double correct_norm = correct.getLength(); - final double norm_diff = Math.abs(this_norm - correct_norm); - return norm_diff; - } -} diff --git a/flutter-idea/src/io/flutter/utils/math/Vector.java b/flutter-idea/src/io/flutter/utils/math/Vector.java deleted file mode 100644 index bd7ecd7b12..0000000000 --- a/flutter-idea/src/io/flutter/utils/math/Vector.java +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright 2019 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -package io.flutter.utils.math; - -/** - * This class is ported from the Vector class in the Dart vector_math - * package. - */ -public interface Vector { - double[] getStorage(); -} diff --git a/flutter-idea/src/io/flutter/utils/math/Vector2.java b/flutter-idea/src/io/flutter/utils/math/Vector2.java deleted file mode 100644 index 4d3c484fb5..0000000000 --- a/flutter-idea/src/io/flutter/utils/math/Vector2.java +++ /dev/null @@ -1,550 +0,0 @@ -/* - * Copyright 2019 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -package io.flutter.utils.math; - - -import java.util.Arrays; - -/** - * This class is ported from the Vector2 class in the Dart vector_math - * package. The code is ported as is without concern for making the code - * consistent with Java api conventions to keep the code consistent with - * the Dart code to simplify using Transform Matrixes returned by Flutter. - */ -public class Vector2 implements Vector { - final double[] _v2storage; - - /** - * Construct a new vector with the specified values. - */ - Vector2(double x, double y) { - _v2storage = new double[]{x, y}; - } - - /** - * Zero vector. - */ - public Vector2() { - _v2storage = new double[2]; - } - - /** - * Set the values of [result] to the minimum of [a] and [b] for each line. - */ - static void min(Vector2 a, Vector2 b, Vector2 result) { - result.setX(Math.min(a.getX(), b.getX())); - result.setY(Math.min(a.getY(), b.getY())); - } - - /** - * Set the values of [result] to the maximum of [a] and [b] for each line. - */ - static void max(Vector2 a, Vector2 b, Vector2 result) { - result - .setX(Math.max(a.getX(), b.getX())); - result - .setY(Math.max(a.getY(), b.getY())); - } - - /** - * Interpolate between [min] and [max] with the amount of [a] using a linear - * interpolation and store the values in [result]. - */ - static void mix(Vector2 min, Vector2 max, double a, Vector2 result) { - result.setX(min.getX() + a * (max.getX() - min.getX())); - result.setY(min.getY() + a * (max.getY()) - min.getY()); - } - - /** - * Splat [value] into all lanes of the vector. - */ - static Vector2 all(double value) { - final Vector2 ret = new Vector2(); - ret.splat(value); - return ret; - } - - /** - * Copy of [other]. - */ - static Vector2 copy(Vector2 other) { - final Vector2 ret = new Vector2(); - ret.setFrom(other); - return ret; - } - - /** - * The components of the vector. - */ - @Override() - public double[] getStorage() { - return _v2storage; - } - - /** - * Set the values of the vector. - */ - public void setValues(double x_, double y_) { - _v2storage[0] = x_; - _v2storage[1] = y_; - } - - /** - * Zero the vector. - */ - public void setZero() { - _v2storage[0] = 0.0; - _v2storage[1] = 0.0; - } - - /** - * Set the values by copying them from [other]. - */ - public void setFrom(Vector2 other) { - final double[] otherStorage = other._v2storage; - _v2storage[1] = otherStorage[1]; - _v2storage[0] = otherStorage[0]; - } - - /** - * Splat [arg] into all lanes of the vector. - */ - public void splat(double arg) { - _v2storage[0] = arg; - _v2storage[1] = arg; - } - - /** - * Returns a printable string - */ - @Override() - public String toString() { - return "[" + _v2storage[0] + "," + _v2storage[1] + "]"; - } - - /** - * Check if two vectors are the same. - */ - @Override() - public boolean equals(Object other) { - if (!(other instanceof Vector2 otherV)) return false; - return (_v2storage[0] == otherV._v2storage[0]) && - (_v2storage[1] == otherV._v2storage[1]); - } - - @Override() - public int hashCode() { - return Arrays.hashCode(_v2storage); - } - - /** - * Negate. - */ - Vector2 operatorNegate() { - final Vector2 ret = clone(); - ret.negate(); - return ret; - } - - /** - * Subtract two vectors. - */ - public Vector2 operatorSub(Vector2 other) { - final Vector2 ret = clone(); - ret.sub(other); - return ret; - } - - /** - * Add two vectors. - */ - public Vector2 operatorAdd(Vector2 other) { - final Vector2 ret = clone(); - ret.add(other); - return ret; - } - - /** - * Scale. - */ - public Vector2 operatorDiv(double scale) { - final Vector2 ret = clone(); - ret.scale(1.0 / scale); - return ret; - } - - /** - * Scale. - */ - public Vector2 operatorScale(double scale) { - final Vector2 ret = clone(); - ret.scale(scale); - return ret; - } - - /** - * Length. - */ - public double getLength() { - return Math.sqrt(getLength2()); - } - - /** - * Set the length of the vector. A negative [value] will change the vectors - * orientation and a [value] of zero will set the vector to zero. - */ - public void setLength(double value) { - if (value == 0.0) { - setZero(); - } - else { - double l = getLength(); - if (l == 0.0) { - return; - } - l = value / l; - _v2storage[0] *= l; - _v2storage[1] *= l; - } - } - - /** - * Length squared. - */ - public double getLength2() { - double sum; - sum = (_v2storage[0] * _v2storage[0]); - sum += (_v2storage[1] * _v2storage[1]); - return sum; - } - - /** - * Normalize [this]. - */ - public double normalize() { - final double l = getLength(); - if (l == 0.0) { - return 0.0; - } - final double d = 1.0 / l; - _v2storage[0] *= d; - _v2storage[1] *= d; - return l; - } - - - /** - * Normalized copy of [this]. - */ - public Vector2 normalized() { - final Vector2 ret = clone(); - ret.normalize(); - return ret; - } - - /** - * Normalize vector into [out]. - */ - public Vector2 normalizeInto(Vector2 out) { - out.setFrom(this); - out.normalize(); - return out; - } - - /** - * Distance from [this] to [arg] - */ - public double distanceTo(Vector2 arg) { - return Math.sqrt(distanceToSquared(arg)); - } - - /** - * Squared distance from [this] to [arg] - */ - public double distanceToSquared(Vector2 arg) { - final double dx = getX() - arg.getX(); - final double dy = getY() - arg.getY(); - - return dx * dx + dy * dy; - } - - /** - * Returns the angle between [this] vector and [other] in radians. - */ - public double angleTo(Vector2 other) { - final double[] otherStorage = other._v2storage; - if (_v2storage[0] == otherStorage[0] && _v2storage[1] == otherStorage[1]) { - return 0.0; - } - - final double d = dot(other) / (getLength() * other.getLength()); - - return Math.acos(VectorUtil.clamp(d, -1.0, 1.0)); - } - - /** - * Returns the signed angle between [this] and [other] in radians. - */ - public double angleToSigned(Vector2 other) { - final double[] otherStorage = other._v2storage; - if (_v2storage[0] == otherStorage[0] && _v2storage[1] == otherStorage[1]) { - return 0.0; - } - - final double s = cross(other); - final double c = dot(other); - - return Math.atan2(s, c); - } - - /** - * Inner product. - */ - public double dot(Vector2 other) { - final double[] otherStorage = other._v2storage; - double sum; - sum = _v2storage[0] * otherStorage[0]; - sum += _v2storage[1] * otherStorage[1]; - return sum; - } - - /** - * Cross product. - */ - public double cross(Vector2 other) { - final double[] otherStorage = other._v2storage; - return _v2storage[0] * otherStorage[1] - _v2storage[1] * otherStorage[0]; - } - - /** - * Rotate [this] by 90 degrees then scale it. Store result in [out]. Return [out]. - */ - public Vector2 scaleOrthogonalInto(double scale, Vector2 out) { - out.setValues(-scale * _v2storage[1], scale * _v2storage[0]); - return out; - } - - /** - * Reflect [this]. - */ - public void reflect(Vector2 normal) { - sub(normal.scaled(2.0 * normal.dot(this))); - } - - /** - * Reflected copy of [this]. - */ - public Vector2 reflected(Vector2 normal) { - final Vector2 ret = clone(); - ret.reflect(normalized()); - return ret; - } - - /** - * Relative error between [this] and [correct] - */ - public double relativeError(Vector2 correct) { - final double correct_norm = correct.getLength(); - final double diff_norm = (this.operatorSub(correct)).getLength(); - return diff_norm / correct_norm; - } - - /** - * Absolute error between [this] and [correct] - */ - public double absoluteError(Vector2 correct) { - return operatorSub(correct).getLength(); - } - - /** - * True if any component is infinite. - */ - public boolean isInfinite() { - boolean is_infinite = Double.isInfinite(_v2storage[0]); - is_infinite = is_infinite || Double.isInfinite(_v2storage[1]); - return is_infinite; - } - - /** - * True if any component is NaN. - */ - public boolean isNaN() { - boolean is_nan = Double.isNaN(_v2storage[0]); - is_nan = is_nan || Double.isNaN(_v2storage[1]); - return is_nan; - } - - /** - * Add [arg] to [this]. - */ - public void add(Vector2 arg) { - final double[] argStorage = arg._v2storage; - _v2storage[0] = _v2storage[0] + argStorage[0]; - _v2storage[1] = _v2storage[1] + argStorage[1]; - } - - /** - * Add [arg] scaled by [factor] to [this]. - */ - public void addScaled(Vector2 arg, double factor) { - final double[] argStorage = arg._v2storage; - _v2storage[0] = _v2storage[0] + argStorage[0] * factor; - _v2storage[1] = _v2storage[1] + argStorage[1] * factor; - } - - /** - * Subtract [arg] from [this]. - */ - public void sub(Vector2 arg) { - final double[] argStorage = arg._v2storage; - _v2storage[0] = _v2storage[0] - argStorage[0]; - _v2storage[1] = _v2storage[1] - argStorage[1]; - } - - /** - * Multiply entries in [this] with entries in [arg]. - */ - public void multiply(Vector2 arg) { - final double[] argStorage = arg._v2storage; - _v2storage[0] = _v2storage[0] * argStorage[0]; - _v2storage[1] = _v2storage[1] * argStorage[1]; - } - - /** - * Divide entries in [this] with entries in [arg]. - */ - public void divide(Vector2 arg) { - final double[] argStorage = arg._v2storage; - _v2storage[0] = _v2storage[0] / argStorage[0]; - _v2storage[1] = _v2storage[1] / argStorage[1]; - } - - /** - * Scale [this] by [arg]. - */ - public void scale(double arg) { - _v2storage[1] = _v2storage[1] * arg; - _v2storage[0] = _v2storage[0] * arg; - } - - /** - * Return a copy of [this] scaled by [arg]. - */ - public Vector2 scaled(double arg) { - final Vector2 ret = clone(); - ret.scale(arg); - return ret; - } - - /** - * Negate. - */ - public void negate() { - _v2storage[1] = -_v2storage[1]; - _v2storage[0] = -_v2storage[0]; - } - - /** - * Absolute value. - */ - public void absolute() { - _v2storage[1] = Math.abs(_v2storage[1]); - _v2storage[0] = Math.abs(_v2storage[0]); - } - - /** - * Clamp each entry n in [this] in the range [min[n]]-[max[n]]. - */ - public void clamp(Vector2 min, Vector2 max) { - final double[] minStorage = min.getStorage(); - final double[] maxStorage = max.getStorage(); - _v2storage[0] = - VectorUtil.clamp(_v2storage[0], minStorage[0], maxStorage[0]); - _v2storage[1] = - VectorUtil.clamp(_v2storage[1], minStorage[1], maxStorage[1]); - } - - /** - * Clamp entries [this] in the range [min]-[max]. - */ - public void clampScalar(double min, double max) { - _v2storage[0] = VectorUtil.clamp(_v2storage[0], min, max); - _v2storage[1] = VectorUtil.clamp(_v2storage[1], min, max); - } - - /** - * Floor entries in [this]. - */ - public void floor() { - _v2storage[0] = Math.floor(_v2storage[0]); - _v2storage[1] = Math.floor(_v2storage[1]); - } - - /** - * Ceil entries in [this]. - */ - public void ceil() { - _v2storage[0] = Math.ceil(_v2storage[0]); - _v2storage[1] = Math.ceil(_v2storage[1]); - } - - /** - * Round entries in [this]. - */ - public void round() { - _v2storage[0] = Math.round(_v2storage[0]); - _v2storage[1] = Math.round(_v2storage[1]); - } - - /** - * Round entries in [this] towards zero. - */ - public void roundToZero() { - _v2storage[0] = _v2storage[0] < 0.0 - ? Math.ceil(_v2storage[0]) - : Math.floor(_v2storage[0]); - _v2storage[1] = _v2storage[1] < 0.0 - ? Math.ceil(_v2storage[1]) - : Math.floor(_v2storage[1]); - } - - /** - * Clone of [this]. - */ - @SuppressWarnings("MethodDoesntCallSuperMethod") - @Override - public Vector2 clone() { - final Vector2 ret = new Vector2(); - ret.setFrom(this); - return ret; - } - - /** - * Copy [this] into [arg]. Returns [arg]. - */ - public Vector2 copyInto(Vector2 arg) { - final double[] argStorage = arg._v2storage; - argStorage[1] = _v2storage[1]; - argStorage[0] = _v2storage[0]; - return arg; - } - - public double getX() { - return _v2storage[0]; - } - - public void setX(double arg) { - _v2storage[0] = arg; - } - - public double getY() { - return _v2storage[1]; - } - - public void setY(double arg) { - _v2storage[1] = arg; - } -} \ No newline at end of file diff --git a/flutter-idea/src/io/flutter/utils/math/Vector3.java b/flutter-idea/src/io/flutter/utils/math/Vector3.java deleted file mode 100644 index 3a936c37d0..0000000000 --- a/flutter-idea/src/io/flutter/utils/math/Vector3.java +++ /dev/null @@ -1,748 +0,0 @@ -/* - * Copyright 2019 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -package io.flutter.utils.math; - -import java.util.Arrays; - -/** - * This class is ported from the Vector3 class in the Dart vector_math - * package. The code is ported as is without concern for making the code - * consistent with Java api conventions to keep the code consistent with - * the Dart code to simplify using Transform Matrixes returned by Flutter. - */ -@SuppressWarnings({"PointlessArithmeticExpression", "UnusedReturnValue", "DuplicatedCode", "ConstantConditions"}) -public class Vector3 implements Vector { - final double[] _v3storage; - - /** - * Construct a new vector with the specified values. - */ - public Vector3(double x, double y, double z) { - _v3storage = new double[]{x, y, z}; - } - - /** - * Zero vector. - */ - Vector3() { - _v3storage = new double[3]; - } - - - /** - * Constructs Vector3 with given double[] as [storage]. - */ - public Vector3(double[] v3storage) { - this._v3storage = v3storage; - } - - /** - * Set the values of [result] to the minimum of [a] and [b] for each line. - */ - public static void min(Vector3 a, Vector3 b, Vector3 result) { - result.setX(Math.min(a.getX(), b.getX())); - result.setY(Math.min(a.getY(), b.getY())); - result.setZ(Math.min(a.getZ(), b.getZ())); - } - - /** - * Set the values of [result] to the maximum of [a] and [b] for each line. - */ - public static void max(Vector3 a, Vector3 b, Vector3 result) { - result.setX(Math.max(a.getX(), b.getX())); - result.setY(Math.max(a.getY(), b.getY())); - result.setZ(Math.max(a.getZ(), b.getZ())); - } - - /* - * Interpolate between [min] and [max] with the amount of [a] using a linear - * interpolation and store the values in [result]. - */ - public static void mix(Vector3 min, Vector3 max, double a, Vector3 result) { - result.setX(min.getX() + a * (max.getX() - min.getX())); - result.setY(min.getY() + a * (max.getY() - min.getY())); - result.setZ(min.getZ() + a * (max.getZ() - min.getZ())); - } - - /** - * Initialized with values from [array] starting at [offset]. - */ - public static Vector3 array(double[] array) { - return array(array, 0); - } - - public static Vector3 array(double[] array, int offset) { - final Vector3 ret = new Vector3(); - ret.copyFromArray(array, offset); - return ret; - } - - public static Vector3 getZero() { - return new Vector3(); - } - - /** - * Splat [value] into all lanes of the vector. - */ - static Vector3 all(double value) { - final Vector3 ret = Vector3.getZero(); - ret.splat(value); - return ret; - } - - /** - * Copy of [other]. - */ - static Vector3 copy(Vector3 other) { - final Vector3 ret = Vector3.getZero(); - ret.setFrom(other); - return ret; - } - - public static Vector3 zero() { - return new Vector3(); - } - - /** - * The components of the vector. - */ - @Override() - public double[] getStorage() { - return _v3storage; - } - - /** - * Set the values of the vector. - */ - public void setValues(double x_, double y_, double z_) { - _v3storage[0] = x_; - _v3storage[1] = y_; - _v3storage[2] = z_; - } - - /** - * Zero vector. - */ - public void setZero() { - _v3storage[2] = 0.0; - _v3storage[1] = 0.0; - _v3storage[0] = 0.0; - } - - /** - * Set the values by copying them from [other]. - */ - public void setFrom(Vector3 other) { - final double[] otherStorage = other._v3storage; - _v3storage[0] = otherStorage[0]; - _v3storage[1] = otherStorage[1]; - _v3storage[2] = otherStorage[2]; - } - - /** - * Splat [arg] into all lanes of the vector. - */ - public void splat(double arg) { - _v3storage[2] = arg; - _v3storage[1] = arg; - _v3storage[0] = arg; - } - - /** - * Returns a printable string - */ - @Override() - public String toString() { - return "[" + _v3storage[0] + "," + _v3storage[1] + "," + _v3storage[2] + "]"; - } - - /** - * Check if two vectors are the same. - */ - @Override() - public boolean equals(Object o) { - if (!(o instanceof Vector3 other)) { - return false; - } - return (_v3storage[0] == other._v3storage[0]) && - (_v3storage[1] == other._v3storage[1]) && - (_v3storage[2] == other._v3storage[2]); - } - - @Override() - public int hashCode() { - return Arrays.hashCode(_v3storage); - } - - /** - * Negate - */ - public Vector3 operatorNegate() { - final Vector3 ret = clone(); - ret.negate(); - return ret; - } - - /** - * Subtract two vectors. - */ - public Vector3 operatorSub(Vector3 other) { - final Vector3 ret = clone(); - ret.sub(other); - return ret; - } - - /** - * Add two vectors. - */ - public Vector3 operatorAdd(Vector3 other) { - final Vector3 ret = clone(); - ret.add(other); - return ret; - } - - /** - * Scale. - */ - public Vector3 operatorDiv(double scale) { - final Vector3 ret = clone(); - ret.scaled(1.0 / scale); - return ret; - } - - /** - * Scale by [scale]. - */ - public Vector3 operatorScaled(double scale) { - final Vector3 ret = clone(); - ret.scaled(scale); - return ret; - } - - /** - * Length. - */ - public double getLength() { - return Math.sqrt(getLength2()); - } - - /** - * Set the length of the vector. A negative [value] will change the vectors - * orientation and a [value] of zero will set the vector to zero. - */ - public void setLength(double value) { - if (value == 0.0) { - setZero(); - } - else { - double l = getLength(); - if (l == 0.0) { - return; - } - l = value / l; - _v3storage[0] *= l; - _v3storage[1] *= l; - _v3storage[2] *= l; - } - } - - /** - * Length squared. - */ - public double getLength2() { - double sum; - sum = (_v3storage[0] * _v3storage[0]); - sum += (_v3storage[1] * _v3storage[1]); - sum += (_v3storage[2] * _v3storage[2]); - return sum; - } - - /** - * Normalizes [this]. - */ - public double normalize() { - final double l = getLength(); - if (l == 0.0) { - return 0.0; - } - final double d = 1.0 / l; - _v3storage[0] *= d; - _v3storage[1] *= d; - _v3storage[2] *= d; - return l; - } - - /** - * Normalizes copy of [this]. - */ - public Vector3 normalized() { - final Vector3 ret = Vector3.copy(this); - ret.normalize(); - return ret; - } - - /** - * Normalize vector into [out]. - */ - public Vector3 normalizeInto(Vector3 out) { - out.setFrom(this); - out.normalize(); - return out; - } - - /** - * Distance from [this] to [arg] - */ - public double distanceTo(Vector3 arg) { - return Math.sqrt(distanceToSquared(arg)); - } - - /** - * Squared distance from [this] to [arg] - */ - public double distanceToSquared(Vector3 arg) { - final double[] argStorage = arg._v3storage; - final double dx = _v3storage[0] - argStorage[0]; - final double dy = _v3storage[1] - argStorage[1]; - final double dz = _v3storage[2] - argStorage[2]; - - return dx * dx + dy * dy + dz * dz; - } - - /** - * Returns the angle between [this] vector and [other] in radians. - */ - public double angleTo(Vector3 other) { - final double[] otherStorage = other._v3storage; - if (_v3storage[0] == otherStorage[0] && - _v3storage[1] == otherStorage[1] && - _v3storage[2] == otherStorage[2]) { - return 0.0; - } - - final double d = dot(other) / (getLength() * other.getLength()); - - return Math.acos(VectorUtil.clamp(d, -1.0, 1.0)); - } - - /** - * Returns the signed angle between [this] and [other] around [normal] - * in radians. - */ - public double angleToSigned(Vector3 other, Vector3 normal) { - final double angle = angleTo(other); - final Vector3 c = cross(other); - final double d = c.dot(normal); - - return d < 0.0 ? -angle : angle; - } - - /** - * Inner product. - */ - public double dot(Vector3 other) { - final double[] otherStorage = other._v3storage; - double sum; - sum = _v3storage[0] * otherStorage[0]; - sum += _v3storage[1] * otherStorage[1]; - sum += _v3storage[2] * otherStorage[2]; - return sum; - } - - /** - * Cross product. - */ - public Vector3 cross(Vector3 other) { - final double _x = _v3storage[0]; - final double _y = _v3storage[1]; - final double _z = _v3storage[2]; - final double[] otherStorage = other._v3storage; - final double ox = otherStorage[0]; - final double oy = otherStorage[1]; - final double oz = otherStorage[2]; - return new Vector3(_y * oz - _z * oy, _z * ox - _x * oz, _x * oy - _y * ox); - } - - /** - * Cross product. Stores result in [out]. - */ - public Vector3 crossInto(Vector3 other, Vector3 out) { - final double x = _v3storage[0]; - final double y = _v3storage[1]; - final double z = _v3storage[2]; - final double[] otherStorage = other._v3storage; - final double ox = otherStorage[0]; - final double oy = otherStorage[1]; - final double oz = otherStorage[2]; - final double[] outStorage = out._v3storage; - outStorage[0] = y * oz - z * oy; - outStorage[1] = z * ox - x * oz; - outStorage[2] = x * oy - y * ox; - return out; - } - - /** - * Reflect [this]. - */ - public Vector3 reflect(Vector3 normal) { - sub(normal.scaled(2.0 * normal.dot(this))); - return this; - } - - /** - * Reflected copy of [this]. - */ - public Vector3 reflected(Vector3 normal) { - final Vector3 ret = clone(); - ret.reflect(normal); - return ret; - } - - /** - * Projects [this] using the projection matrix [arg] - */ - public void applyProjection(Matrix4 arg) { - final double[] argStorage = arg.getStorage(); - final double x = _v3storage[0]; - final double y = _v3storage[1]; - final double z = _v3storage[2]; - final double d = 1.0 / - (argStorage[3] * x + - argStorage[7] * y + - argStorage[11] * z + - argStorage[15]); - _v3storage[0] = (argStorage[0] * x + - argStorage[4] * y + - argStorage[8] * z + - argStorage[12]) * - d; - _v3storage[1] = (argStorage[1] * x + - argStorage[5] * y + - argStorage[9] * z + - argStorage[13]) * - d; - _v3storage[2] = (argStorage[2] * x + - argStorage[6] * y + - argStorage[10] * z + - argStorage[14]) * - d; - } - - /** - * Applies a rotation specified by [axis] and [angle]. - */ - public void applyAxisAngle(Vector3 axis, double angle) { - applyQuaternion(Quaternion.axisAngle(axis, angle)); - } - - /** - * Applies a quaternion transform. - */ - public void applyQuaternion(Quaternion arg) { - final double[] argStorage = arg._qStorage; - final double v0 = _v3storage[0]; - final double v1 = _v3storage[1]; - final double v2 = _v3storage[2]; - final double qx = argStorage[0]; - final double qy = argStorage[1]; - final double qz = argStorage[2]; - final double qw = argStorage[3]; - final double ix = qw * v0 + qy * v2 - qz * v1; - final double iy = qw * v1 + qz * v0 - qx * v2; - final double iz = qw * v2 + qx * v1 - qy * v0; - final double iw = -qx * v0 - qy * v1 - qz * v2; - _v3storage[0] = ix * qw + iw * -qx + iy * -qz - iz * -qy; - _v3storage[1] = iy * qw + iw * -qy + iz * -qx - ix * -qz; - _v3storage[2] = iz * qw + iw * -qz + ix * -qy - iy * -qx; - } - - /** - * Multiplies [this] by a 4x3 subset of [arg]. Expects [arg] to be an affine - * transformation matrix. - */ - public void applyMatrix4(Matrix4 arg) { - final double[] argStorage = arg.getStorage(); - final double v0 = _v3storage[0]; - final double v1 = _v3storage[1]; - final double v2 = _v3storage[2]; - _v3storage[0] = argStorage[0] * v0 + - argStorage[4] * v1 + - argStorage[8] * v2 + - argStorage[12]; - _v3storage[1] = argStorage[1] * v0 + - argStorage[5] * v1 + - argStorage[9] * v2 + - argStorage[13]; - _v3storage[2] = argStorage[2] * v0 + - argStorage[6] * v1 + - argStorage[10] * v2 + - argStorage[14]; - } - - /** - * Relative error between [this] and [correct] - */ - public double relativeError(Vector3 correct) { - final double correct_norm = correct.getLength(); - final double diff_norm = (this.operatorSub(correct)).getLength(); - return diff_norm / correct_norm; - } - - /** - * Absolute error between [this] and [correct] - */ - public double absoluteError(Vector3 correct) { - return (this.operatorSub(correct)).getLength(); - } - - /** - * True if any component is infinite. - */ - public boolean isInfinite() { - boolean is_infinite = false; - is_infinite = is_infinite || Double.isInfinite(_v3storage[0]); - is_infinite = is_infinite || Double.isInfinite(_v3storage[1]); - is_infinite = is_infinite || Double.isInfinite(_v3storage[2]); - return is_infinite; - } - - /** - * True if any component is NaN. - */ - public boolean isNaN() { - boolean is_nan = false; - is_nan = is_nan || Double.isNaN(_v3storage[0]); - is_nan = is_nan || Double.isNaN(_v3storage[1]); - is_nan = is_nan || Double.isNaN(_v3storage[2]); - return is_nan; - } - - /** - * Add [arg] to [this]. - */ - public void add(Vector3 arg) { - final double[] argStorage = arg._v3storage; - _v3storage[0] = _v3storage[0] + argStorage[0]; - _v3storage[1] = _v3storage[1] + argStorage[1]; - _v3storage[2] = _v3storage[2] + argStorage[2]; - } - - /** - * Add [arg] scaled by [factor] to [this]. - */ - public void addScaled(Vector3 arg, double factor) { - final double[] argStorage = arg._v3storage; - _v3storage[0] = _v3storage[0] + argStorage[0] * factor; - _v3storage[1] = _v3storage[1] + argStorage[1] * factor; - _v3storage[2] = _v3storage[2] + argStorage[2] * factor; - } - - /** - * Subtract [arg] from [this]. - */ - public Vector3 sub(Vector3 arg) { - final double[] argStorage = arg._v3storage; - _v3storage[0] = _v3storage[0] - argStorage[0]; - _v3storage[1] = _v3storage[1] - argStorage[1]; - _v3storage[2] = _v3storage[2] - argStorage[2]; - return this; - } - - /** - * Multiply entries in [this] with entries in [arg]. - */ - public Vector3 multiply(Vector3 arg) { - final double[] argStorage = arg._v3storage; - _v3storage[0] = _v3storage[0] * argStorage[0]; - _v3storage[1] = _v3storage[1] * argStorage[1]; - _v3storage[2] = _v3storage[2] * argStorage[2]; - return this; - } - - /** - * Divide entries in [this] with entries in [arg]. - */ - public Vector3 divide(Vector3 arg) { - final double[] argStorage = arg._v3storage; - _v3storage[0] = _v3storage[0] / argStorage[0]; - _v3storage[1] = _v3storage[1] / argStorage[1]; - _v3storage[2] = _v3storage[2] / argStorage[2]; - return this; - } - - /** - * Scale [this]. - */ - public void scale(double arg) { - _v3storage[2] = _v3storage[2] * arg; - _v3storage[1] = _v3storage[1] * arg; - _v3storage[0] = _v3storage[0] * arg; - } - - /** - * Create a copy of [this] and scale it by [arg]. - */ - public Vector3 scaled(double arg) { - final Vector3 ret = clone(); - ret.scale(arg); - return ret; - } - - /** - * Negate [this]. - */ - public void negate() { - _v3storage[2] = -_v3storage[2]; - _v3storage[1] = -_v3storage[1]; - _v3storage[0] = -_v3storage[0]; - } - - /** - * Absolute value. - */ - public void absolute() { - _v3storage[0] = Math.abs(_v3storage[0]); - _v3storage[1] = Math.abs(_v3storage[1]); - _v3storage[2] = Math.abs(_v3storage[2]); - } - - /** - * Clamp each entry n in [this] in the range [min[n]]-[max[n]]. - */ - public void clamp(Vector3 min, Vector3 max) { - final double[] minStorage = min.getStorage(); - final double[] maxStorage = max.getStorage(); - _v3storage[0] = - VectorUtil.clamp(_v3storage[0], minStorage[0], maxStorage[0]); - _v3storage[1] = - VectorUtil.clamp(_v3storage[1], minStorage[1], maxStorage[1]); - _v3storage[2] = - VectorUtil.clamp(_v3storage[2], minStorage[2], maxStorage[2]); - } - - /** - * Clamp entries in [this] in the range [min]-[max]. - */ - public void clampScalar(double min, double max) { - _v3storage[0] = VectorUtil.clamp(_v3storage[0], min, max); - _v3storage[1] = VectorUtil.clamp(_v3storage[1], min, max); - _v3storage[2] = VectorUtil.clamp(_v3storage[2], min, max); - } - - /** - * Floor entries in [this]. - */ - public void floor() { - _v3storage[0] = Math.floor(_v3storage[0]); - _v3storage[1] = Math.floor(_v3storage[1]); - _v3storage[2] = Math.floor(_v3storage[2]); - } - - /** - * Ceil entries in [this]. - */ - public void ceil() { - _v3storage[0] = Math.ceil(_v3storage[0]); - _v3storage[1] = Math.ceil(_v3storage[1]); - _v3storage[2] = Math.ceil(_v3storage[2]); - } - - /** - * Round entries in [this]. - */ - public void round() { - _v3storage[0] = Math.round(_v3storage[0]); - _v3storage[1] = Math.round(_v3storage[1]); - _v3storage[2] = Math.round(_v3storage[2]); - } - - /** - * Round entries in [this] towards zero. - */ - public void roundToZero() { - _v3storage[0] = _v3storage[0] < 0.0 - ? Math.ceil(_v3storage[0]) - : Math.floor(_v3storage[0]); - _v3storage[1] = _v3storage[1] < 0.0 - ? Math.ceil(_v3storage[1]) - : Math.floor(_v3storage[1]); - _v3storage[2] = _v3storage[2] < 0.0 - ? Math.ceil(_v3storage[2]) - : Math.floor(_v3storage[2]); - } - - /** - * Clone of [this]. - */ - @SuppressWarnings("MethodDoesntCallSuperMethod") - @Override - public Vector3 clone() { - final Vector3 ret = new Vector3(); - copyInto(ret); - return ret; - } - - /** - * Copy [this] into [arg]. - */ - public Vector3 copyInto(Vector3 arg) { - final double[] argStorage = arg._v3storage; - argStorage[0] = _v3storage[0]; - argStorage[1] = _v3storage[1]; - argStorage[2] = _v3storage[2]; - return arg; - } - - /** - * Copies [this] into [array] starting at [offset]. - */ - public void copyIntoArray(double[] array) { - copyIntoArray(array, 0); - } - - public void copyIntoArray(double[] array, int offset) { - array[offset + 2] = _v3storage[2]; - array[offset + 1] = _v3storage[1]; - array[offset + 0] = _v3storage[0]; - } - - /** - * Copies elements from [array] into [this] starting at [offset]. - */ - public void copyFromArray(double[] array) { - copyFromArray(array, 0); - } - - public void copyFromArray(double[] array, int offset) { - _v3storage[2] = array[offset + 2]; - _v3storage[1] = array[offset + 1]; - _v3storage[0] = array[offset + 0]; - } - - public double getX() { - return _v3storage[0]; - } - - public void setX(double arg) { - _v3storage[0] = arg; - } - - public double getY() { - return _v3storage[1]; - } - - public void setY(double arg) { - _v3storage[1] = arg; - } - - public double getZ() { - return _v3storage[2]; - } - - public void setZ(double arg) { - _v3storage[2] = arg; - } -} diff --git a/flutter-idea/src/io/flutter/utils/math/Vector4.java b/flutter-idea/src/io/flutter/utils/math/Vector4.java deleted file mode 100644 index 69bcbb7319..0000000000 --- a/flutter-idea/src/io/flutter/utils/math/Vector4.java +++ /dev/null @@ -1,623 +0,0 @@ -/* - * Copyright 2019 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -package io.flutter.utils.math; - -import java.util.Arrays; - -/** - * 4D column vector. - *

- * This class is ported from the Vector4 class in the Dart vector_math - * package. The code is ported as is without concern for making the code - * consistent with Java api conventions to keep the code consistent with - * the Dart code to simplify using Transform Matrixes returned by Flutter. - */ -@SuppressWarnings("Duplicates") -class Vector4 implements Vector { - final double[] _v4storage; - - /** - * Construct a new vector with the specified values. - */ - Vector4(double x, double y, double z, double w) { - _v4storage = new double[]{x, y, z, w}; - } - - /** - * Zero vector. - */ - Vector4() { - _v4storage = new double[4]; - } - - /** - * Constructs Vector4 with given double[] as [.getStorage()]. - */ - Vector4(double[] values) { - _v4storage = values; - } - - /** - * Set the values of [result] to the minimum of [a] and [b] for each line. - */ - public static void min(Vector4 a, Vector4 b, Vector4 result) { - result.setX(Math.min(a.getX(), b.getX())); - result.setY(Math.min(a.getY(), b.getY())); - result.setZ(Math.min(a.getZ(), b.getZ())); - result.setW(Math.min(a.getW(), b.getW())); - } - - /** - * Set the values of [result] to the maximum of [a] and [b] for each line. - */ - public static void max(Vector4 a, Vector4 b, Vector4 result) { - result.setX(Math.max(a.getX(), b.getX())); - result.setY(Math.max(a.getY(), b.getY())); - result.setZ(Math.max(a.getZ(), b.getZ())); - result.setW(Math.max(a.getW(), b.getW())); - } - - /* - * Interpolate between [min] and [max] with the amount of [a] using a linear - * interpolation and store the values in [result]. - */ - public static void mix(Vector4 min, Vector4 max, double a, Vector4 result) { - result.setX(min.getX() + a * (max.getX() - min.getX())); - result.setY(min.getY() + a * (max.getY() - min.getY())); - result.setZ(min.getZ() + a * (max.getZ() - min.getZ())); - result.setW(min.getW() + a * (max.getW() - min.getW())); - } - - /** - * Constructs the identity vector. - */ - public static Vector4 identity() { - final Vector4 ret = new Vector4(); - ret.setIdentity(); - return ret; - } - - /** - * Splat [value] into all lanes of the vector. - */ - public static Vector4 all(double value) { - final Vector4 ret = new Vector4(); - ret.splat(value); - return ret; - } - - /** - * Copy of [other]. - */ - public static Vector4 copy(Vector4 other) { - final Vector4 ret = new Vector4(); - ret.setFrom(other); - return ret; - } - - /** - * The components of the vector. - */ - @Override() - public double[] getStorage() { - return _v4storage; - } - - /** - * Set the values of the vector. - */ - public void setValues(double x_, double y_, double z_, double w_) { - _v4storage[3] = w_; - _v4storage[2] = z_; - _v4storage[1] = y_; - _v4storage[0] = x_; - } - - /** - * Zero the vector. - */ - public void setZero() { - _v4storage[0] = 0.0; - _v4storage[1] = 0.0; - _v4storage[2] = 0.0; - _v4storage[3] = 0.0; - } - - /** - * Set to the identity vector. - */ - public void setIdentity() { - _v4storage[0] = 0.0; - _v4storage[1] = 0.0; - _v4storage[2] = 0.0; - _v4storage[3] = 1.0; - } - - /** - * Set the values by copying them from [other]. - */ - public void setFrom(Vector4 other) { - final double[] otherStorage = other._v4storage; - _v4storage[3] = otherStorage[3]; - _v4storage[2] = otherStorage[2]; - _v4storage[1] = otherStorage[1]; - _v4storage[0] = otherStorage[0]; - } - - /** - * Splat [arg] into all lanes of the vector. - */ - public void splat(double arg) { - _v4storage[3] = arg; - _v4storage[2] = arg; - _v4storage[1] = arg; - _v4storage[0] = arg; - } - - /** - * Returns a printable string - */ - @Override() - public String toString() { - return "" + _v4storage[0] + "," + _v4storage[1] + "," + _v4storage[2] + "," + _v4storage[3]; - } - - /** - * Check if two vectors are the same. - */ - @Override() - public boolean equals(Object o) { - if (!(o instanceof Vector4 other)) { - return false; - } - return (_v4storage[0] == other._v4storage[0]) && - (_v4storage[1] == other._v4storage[1]) && - (_v4storage[2] == other._v4storage[2]) && - (_v4storage[3] == other._v4storage[3]); - } - - @Override() - public int hashCode() { - return Arrays.hashCode(_v4storage); - } - - /** - * Negate. - */ - public Vector4 operatorNegate() { - final Vector4 ret = clone(); - ret.negate(); - return ret; - } - - /** - * Subtract two vectors. - */ - public Vector4 operatorSub(Vector4 other) { - final Vector4 ret = clone(); - ret.sub(other); - return ret; - } - - /** - * Add two vectors. - */ - public Vector4 operatorAdd(Vector4 other) { - final Vector4 ret = clone(); - ret.add(other); - return ret; - } - - /** - * Scale. - */ - public Vector4 operatorDiv(double scale) { - final Vector4 ret = clone(); - ret.scale(1.0 / scale); - return ret; - } - - /** - * Scale. - */ - public Vector4 operatorScale(double scale) { - final Vector4 ret = clone(); - ret.scale(scale); - return ret; - } - - /** - * Length. - */ - public double getLength() { - return Math.sqrt(getLength2()); - } - - /** - * Set the length of the vector. A negative [value] will change the vectors - * orientation and a [value] of zero will set the vector to zero. - */ - public void setLength(double value) { - if (value == 0.0) { - setZero(); - } - else { - double l = getLength(); - if (l == 0.0) { - return; - } - l = value / l; - _v4storage[0] *= l; - _v4storage[1] *= l; - _v4storage[2] *= l; - _v4storage[3] *= l; - } - } - - /** - * Length squared. - */ - public double getLength2() { - double sum; - sum = (_v4storage[0] * _v4storage[0]); - sum += (_v4storage[1] * _v4storage[1]); - sum += (_v4storage[2] * _v4storage[2]); - sum += (_v4storage[3] * _v4storage[3]); - return sum; - } - - /** - * Normalizes [this]. - */ - public double normalize() { - final double l = getLength(); - if (l == 0.0) { - return 0.0; - } - final double d = 1.0 / l; - _v4storage[0] *= d; - _v4storage[1] *= d; - _v4storage[2] *= d; - _v4storage[3] *= d; - return l; - } - - /** - * Normalizes copy of [this]. - */ - public Vector4 normalized() { - final Vector4 ret = clone(); - ret.normalize(); - return ret; - } - - /** - * Normalize vector into [out]. - */ - public Vector4 normalizeInto(Vector4 out) { - out.setFrom(this); - out.normalize(); - return out; - } - - /** - * Distance from [this] to [arg] - */ - public double distanceTo(Vector4 arg) { - return Math.sqrt(distanceToSquared(arg)); - } - - /** - * Squared distance from [this] to [arg] - */ - public double distanceToSquared(Vector4 arg) { - final double[] argStorage = arg._v4storage; - final double dx = _v4storage[0] - argStorage[0]; - final double dy = _v4storage[1] - argStorage[1]; - final double dz = _v4storage[2] - argStorage[2]; - final double dw = _v4storage[3] - argStorage[3]; - - return dx * dx + dy * dy + dz * dz + dw * dw; - } - - /** - * Inner product. - */ - public double dot(Vector4 other) { - final double[] otherStorage = other._v4storage; - double sum; - sum = _v4storage[0] * otherStorage[0]; - sum += _v4storage[1] * otherStorage[1]; - sum += _v4storage[2] * otherStorage[2]; - sum += _v4storage[3] * otherStorage[3]; - return sum; - } - - /** - * Multiplies [this] by [arg]. - */ - public void applyMatrix4(Matrix4 arg) { - final double v1 = _v4storage[0]; - final double v2 = _v4storage[1]; - final double v3 = _v4storage[2]; - final double v4 = _v4storage[3]; - final double[] argStorage = arg.getStorage(); - _v4storage[0] = argStorage[0] * v1 + - argStorage[4] * v2 + - argStorage[8] * v3 + - argStorage[12] * v4; - _v4storage[1] = argStorage[1] * v1 + - argStorage[5] * v2 + - argStorage[9] * v3 + - argStorage[13] * v4; - _v4storage[2] = argStorage[2] * v1 + - argStorage[6] * v2 + - argStorage[10] * v3 + - argStorage[14] * v4; - _v4storage[3] = argStorage[3] * v1 + - argStorage[7] * v2 + - argStorage[11] * v3 + - argStorage[15] * v4; - } - - /** - * Relative error between [this] and [correct] - */ - double relativeError(Vector4 correct) { - final double correct_norm = correct.getLength(); - final double diff_norm = (this.operatorSub(correct)).getLength(); - return diff_norm / correct_norm; - } - - /** - * Absolute error between [this] and [correct] - */ - double absoluteError(Vector4 correct) { - return (this.operatorSub(correct)).getLength(); - } - - /** - * True if any component is infinite. - */ - public boolean isInfinite() { - boolean is_infinite; - is_infinite = Double.isInfinite(_v4storage[0]); - is_infinite = is_infinite || Double.isInfinite(_v4storage[1]); - is_infinite = is_infinite || Double.isInfinite(_v4storage[2]); - is_infinite = is_infinite || Double.isInfinite(_v4storage[3]); - return is_infinite; - } - - /** - * True if any component is NaN. - */ - public boolean isNaN() { - boolean is_nan; - is_nan = Double.isNaN(_v4storage[0]); - is_nan = is_nan || Double.isNaN(_v4storage[1]); - is_nan = is_nan || Double.isNaN(_v4storage[2]); - is_nan = is_nan || Double.isNaN(_v4storage[3]); - return is_nan; - } - - public void add(Vector4 arg) { - final double[] argStorage = arg._v4storage; - _v4storage[0] = _v4storage[0] + argStorage[0]; - _v4storage[1] = _v4storage[1] + argStorage[1]; - _v4storage[2] = _v4storage[2] + argStorage[2]; - _v4storage[3] = _v4storage[3] + argStorage[3]; - } - - /** - * Add [arg] scaled by [factor] to [this]. - */ - public void addScaled(Vector4 arg, double factor) { - final double[] argStorage = arg._v4storage; - _v4storage[0] = _v4storage[0] + argStorage[0] * factor; - _v4storage[1] = _v4storage[1] + argStorage[1] * factor; - _v4storage[2] = _v4storage[2] + argStorage[2] * factor; - _v4storage[3] = _v4storage[3] + argStorage[3] * factor; - } - - /** - * Subtract [arg] from [this]. - */ - public void sub(Vector4 arg) { - final double[] argStorage = arg._v4storage; - _v4storage[0] = _v4storage[0] - argStorage[0]; - _v4storage[1] = _v4storage[1] - argStorage[1]; - _v4storage[2] = _v4storage[2] - argStorage[2]; - _v4storage[3] = _v4storage[3] - argStorage[3]; - } - - /** - * Multiply [this] by [arg]. - */ - public void multiply(Vector4 arg) { - final double[] argStorage = arg._v4storage; - _v4storage[0] = _v4storage[0] * argStorage[0]; - _v4storage[1] = _v4storage[1] * argStorage[1]; - _v4storage[2] = _v4storage[2] * argStorage[2]; - _v4storage[3] = _v4storage[3] * argStorage[3]; - } - - /** - * Divide [this] by [arg]. - */ - public void div(Vector4 arg) { - final double[] argStorage = arg._v4storage; - _v4storage[0] = _v4storage[0] / argStorage[0]; - _v4storage[1] = _v4storage[1] / argStorage[1]; - _v4storage[2] = _v4storage[2] / argStorage[2]; - _v4storage[3] = _v4storage[3] / argStorage[3]; - } - - /** - * Scale [this] by [arg]. - */ - public void scale(double arg) { - _v4storage[0] = _v4storage[0] * arg; - _v4storage[1] = _v4storage[1] * arg; - _v4storage[2] = _v4storage[2] * arg; - _v4storage[3] = _v4storage[3] * arg; - } - - /** - * Create a copy of [this] scaled by [arg]. - */ - public Vector4 scaled(double arg) { - final Vector4 ret = clone(); - ret.scale(arg); - return ret; - } - - /** - * Negate [this]. - */ - public void negate() { - _v4storage[0] = -_v4storage[0]; - _v4storage[1] = -_v4storage[1]; - _v4storage[2] = -_v4storage[2]; - _v4storage[3] = -_v4storage[3]; - } - - /** - * Set [this] to the absolute. - */ - public void absolute() { - _v4storage[3] = Math.abs(_v4storage[3]); - _v4storage[2] = Math.abs(_v4storage[2]); - _v4storage[1] = Math.abs(_v4storage[1]); - _v4storage[0] = Math.abs(_v4storage[0]); - } - - /** - * Clamp each entry n in [this] in the range [min[n]]-[max[n]]. - */ - public void clamp(Vector4 min, Vector4 max) { - final double[] minStorage = min.getStorage(); - final double[] maxStorage = max.getStorage(); - _v4storage[0] = VectorUtil.clamp( - _v4storage[0], minStorage[0], maxStorage[0]); - _v4storage[1] = VectorUtil.clamp( - _v4storage[1], minStorage[1], maxStorage[1]); - _v4storage[2] = VectorUtil.clamp( - _v4storage[2], minStorage[2], maxStorage[2]); - _v4storage[3] = VectorUtil.clamp( - _v4storage[3], minStorage[3], maxStorage[3]); - } - - /** - * Clamp entries in [this] in the range [min]-[max]. - */ - public void clampScalar(double min, double max) { - _v4storage[0] = VectorUtil.clamp(_v4storage[0], min, max); - _v4storage[1] = VectorUtil.clamp(_v4storage[1], min, max); - _v4storage[2] = VectorUtil.clamp(_v4storage[2], min, max); - _v4storage[3] = VectorUtil.clamp(_v4storage[3], min, max); - } - - /** - * Floor entries in [this]. - */ - public void floor() { - _v4storage[0] = Math.floor(_v4storage[0]); - _v4storage[1] = Math.floor(_v4storage[1]); - _v4storage[2] = Math.floor(_v4storage[2]); - _v4storage[3] = Math.floor(_v4storage[3]); - } - - /** - * Ceil entries in [this]. - */ - public void ceil() { - _v4storage[0] = Math.ceil(_v4storage[0]); - _v4storage[1] = Math.ceil(_v4storage[1]); - _v4storage[2] = Math.ceil(_v4storage[2]); - _v4storage[3] = Math.ceil(_v4storage[3]); - } - - /** - * Round entries in [this]. - */ - public void round() { - _v4storage[0] = Math.round(_v4storage[0]); - _v4storage[1] = Math.round(_v4storage[1]); - _v4storage[2] = Math.round(_v4storage[2]); - _v4storage[3] = Math.round(_v4storage[3]); - } - - /** - * Round entries in [this] towards zero. - */ - public void roundToZero() { - _v4storage[0] = _v4storage[0] < 0.0 - ? Math.ceil(_v4storage[0]) - : Math.floor(_v4storage[0]); - _v4storage[1] = _v4storage[1] < 0.0 - ? Math.ceil(_v4storage[1]) - : Math.floor(_v4storage[1]); - _v4storage[2] = _v4storage[2] < 0.0 - ? Math.ceil(_v4storage[2]) - : Math.floor(_v4storage[2]); - _v4storage[3] = _v4storage[3] < 0.0 - ? Math.ceil(_v4storage[3]) - : Math.floor(_v4storage[3]); - } - - /** - * Create a copy of [this]. - */ - @SuppressWarnings("MethodDoesntCallSuperMethod") - @Override - public Vector4 clone() { - return Vector4.copy(this); - } - - /** - * Copy [this] - */ - public Vector4 copyInto(Vector4 arg) { - final double[] argStorage = arg._v4storage; - argStorage[0] = _v4storage[0]; - argStorage[1] = _v4storage[1]; - argStorage[2] = _v4storage[2]; - argStorage[3] = _v4storage[3]; - return arg; - } - - public double getX() { - return _v4storage[0]; - } - - public void setX(double arg) { - _v4storage[0] = arg; - } - - public double getY() { - return _v4storage[1]; - } - - public void setY(double arg) { - _v4storage[1] = arg; - } - - public double getZ() { - return _v4storage[2]; - } - - public void setZ(double arg) { - _v4storage[2] = arg; - } - - public double getW() { - return _v4storage[3]; - } - - public void setW(double arg) { - _v4storage[3] = arg; - } -} \ No newline at end of file diff --git a/flutter-idea/testSrc/unit/io/flutter/inspector/TreeScrollAnimatorTest.java b/flutter-idea/testSrc/unit/io/flutter/inspector/TreeScrollAnimatorTest.java deleted file mode 100644 index a57e917d64..0000000000 --- a/flutter-idea/testSrc/unit/io/flutter/inspector/TreeScrollAnimatorTest.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright 2018 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -package io.flutter.inspector; - -import io.flutter.inspector.TreeScrollAnimator.Interval; -import org.junit.Test; - -import static junit.framework.TestCase.assertEquals; -import static junit.framework.TestCase.assertTrue; -import static org.junit.Assert.assertNotEquals; - -public class TreeScrollAnimatorTest { - - // TODO(jacobr): add some end to end tests of TreeScrollAnimator. - @Test - public void nonOverlappingIntervals() { - { - boolean invalidInput = false; - try { - // The required outside of the ideal interval. - TreeScrollAnimator.clampInterval( - new Interval(0, 10), - new Interval(5, 15), - 8); - } - catch (IllegalArgumentException e) { - invalidInput = true; - } - assertTrue(invalidInput); - } - - { - boolean invalidInput = false; - try { - // The required outside of the ideal interval. - TreeScrollAnimator.clampInterval( - new Interval(5, 10), - new Interval(0, 12), - 8); - } - catch (IllegalArgumentException e) { - invalidInput = true; - } - assertTrue(invalidInput); - } - } - - @Test - public void negativeIntervals() { - { - boolean invalidInput = false; - try { - TreeScrollAnimator.clampInterval( - new Interval(0, -3), - new Interval(0, -1), - 8); - } - catch (IllegalArgumentException e) { - invalidInput = true; - } - assertTrue(invalidInput); - } - - { - boolean invalidInput = false; - try { - TreeScrollAnimator.clampInterval( - new Interval(0, 2), - new Interval(0, 2), - -5); - } - catch (IllegalArgumentException e) { - invalidInput = true; - } - assertTrue(invalidInput); - } - } - - @Test - public void idealIntervalFits() { - final Interval required = new Interval(10, 20); - final Interval ideal = new Interval(5, 100); - assertEquals(TreeScrollAnimator.clampInterval(required, ideal, 100), ideal); - assertEquals(TreeScrollAnimator.clampInterval(required, ideal, 150), ideal); - } - - @Test - public void requiredIntervalBarelyFits() { - final Interval required = new Interval(10, 20); - final Interval ideal = new Interval(5, 100); - assertEquals(TreeScrollAnimator.clampInterval(required, ideal, 20), required); - } - - @Test - public void equalRequiredAndIdealIntervals() { - final Interval required = new Interval(10, 20); - final Interval ideal = new Interval(10, 20); - assertEquals(TreeScrollAnimator.clampInterval(required, ideal, 30), ideal); - } - - @Test - public void requiredAtStartOfIdeal() { - final Interval required = new Interval(10, 20); - final Interval ideal = new Interval(10, 200); - assertEquals(TreeScrollAnimator.clampInterval(required, ideal, 100), new Interval(10, 100)); - } - - @Test - public void requiredAtEndOfIdeal() { - final Interval required = new Interval(180, 20); - final Interval ideal = new Interval(10, 190); - assertEquals(TreeScrollAnimator.clampInterval(required, ideal, 80), new Interval(120, 80)); - } - - @Test - public void requiredInMiddleOfIdeal() { - final Interval required = new Interval(200, 100); - final Interval ideal = new Interval(100, 300); - assertEquals(TreeScrollAnimator.clampInterval(required, ideal, 200), new Interval(150, 200)); - } - - @Test - public void requiredNearStartOfIdeal() { - final Interval required = new Interval(120, 100); - final Interval ideal = new Interval(100, 300); - assertEquals(TreeScrollAnimator.clampInterval(required, ideal, 200), new Interval(110, 200)); - } - - @Test - public void requiredNearEndOfIdeal() { - final Interval required = new Interval(280, 100); - final Interval ideal = new Interval(100, 300); - assertEquals(TreeScrollAnimator.clampInterval(required, ideal, 200), new Interval(190, 200)); - } - - @Test - public void intervalEqualityTest() { - assertNotEquals(new Interval(0, 10), new Interval(0, 11)); - assertNotEquals(new Interval(5, 10), new Interval(6, 10)); - assertEquals(new Interval(5, 10), new Interval(5, 10)); - } -} \ No newline at end of file diff --git a/flutter-idea/testSrc/unit/io/flutter/utils/animation/CurvesTest.java b/flutter-idea/testSrc/unit/io/flutter/utils/animation/CurvesTest.java deleted file mode 100644 index 1dae9dd0fd..0000000000 --- a/flutter-idea/testSrc/unit/io/flutter/utils/animation/CurvesTest.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2018 The Chromium Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ -package io.flutter.utils.animation; - -import org.junit.Test; - -import java.util.ArrayList; -import java.util.List; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -public class CurvesTest { - @Test - public void curveFlippedControl() { - final Curve ease = Curves.EASE; - final Curve flippedEase = ease.getFlipped(); - assertTrue(flippedEase.transform(0.0) < 0.001); - assertTrue(flippedEase.transform(0.5) < ease.transform(0.5)); - assertTrue(flippedEase.transform(1.0) > 0.999); - } - - @Test - public void tresholdHasAThreshold() { - final Curve step = new Threshold(0.25); - assertEquals(step.transform(0.0), 0.0, 0.0001); - assertEquals(step.transform(0.24), 0.0, 0.0001); - assertEquals(step.transform(0.25), 1.0, 0.0001); - assertEquals(step.transform(0.26), 1.0, 0.0001); - assertEquals(step.transform(1.0), 1.0, 0.0001); - } - - boolean inInclusiveRange(double value, double start, double end) { - return value >= start && value <= end; - } - - void expectStaysInBounds(Curve curve) { - assertTrue(inInclusiveRange(curve.transform(0.0), 0.0, 1.0)); - assertTrue(inInclusiveRange(curve.transform(0.1), 0.0, 1.0)); - assertTrue(inInclusiveRange(curve.transform(0.2), 0.0, 1.0)); - assertTrue(inInclusiveRange(curve.transform(0.3), 0.0, 1.0)); - assertTrue(inInclusiveRange(curve.transform(0.4), 0.0, 1.0)); - assertTrue(inInclusiveRange(curve.transform(0.5), 0.0, 1.0)); - assertTrue(inInclusiveRange(curve.transform(0.6), 0.0, 1.0)); - assertTrue(inInclusiveRange(curve.transform(0.7), 0.0, 1.0)); - assertTrue(inInclusiveRange(curve.transform(0.8), 0.0, 1.0)); - assertTrue(inInclusiveRange(curve.transform(0.9), 0.0, 1.0)); - assertTrue(inInclusiveRange(curve.transform(1.0), 0.0, 1.0)); - } - - @Test - public void bounceStaysInBounds() { - expectStaysInBounds(Curves.BOUNCE_IN); - expectStaysInBounds(Curves.BOUNCE_OUT); - expectStaysInBounds(Curves.BOUNCE_IN_OUT); - } - - List estimateBounds(Curve curve) { - final List values = new ArrayList<>(); - - values.add(curve.transform(0.0)); - values.add(curve.transform(0.1)); - values.add(curve.transform(0.2)); - values.add(curve.transform(0.3)); - values.add(curve.transform(0.4)); - values.add(curve.transform(0.5)); - values.add(curve.transform(0.6)); - values.add(curve.transform(0.7)); - values.add(curve.transform(0.8)); - values.add(curve.transform(0.9)); - values.add(curve.transform(1.0)); - - double max = values.get(0); - double min = values.get(0); - for (double value : values) { - min = Math.min(min, value); - max = Math.max(max, value); - } - final List ret = new ArrayList<>(); - ret.add(min); - ret.add(max); - return ret; - } - - @Test - public void ellasticOvershootsItsBounds() { - List bounds; - bounds = estimateBounds(Curves.ELASTIC_IN); - assertTrue(bounds.get(0) < 0.0); - assertTrue(bounds.get(1) <= 1.0); - bounds = estimateBounds(Curves.ELASTIC_OUT); - assertTrue(bounds.get(0) >= 0.0); - assertTrue(bounds.get(1) >= 1.0); - bounds = estimateBounds(Curves.ELASTIC_IN_OUT); - assertTrue(bounds.get(0) < 0.0); - assertTrue(bounds.get(1) > 1.0); - } - - @Test - public void decelerateDoesSo() { - final List bounds = estimateBounds(Curves.DECELERATE); - assertTrue(bounds.get(0) >= 0.0); - assertTrue(bounds.get(1) <= 1.0); - - final double d1 = Curves.DECELERATE.transform(0.2) - Curves.DECELERATE.transform(0.0); - final double d2 = Curves.DECELERATE.transform(1.0) - Curves.DECELERATE.transform(0.8); - assertTrue(d2 < d1); - } - - // TODO(jacobr): port this test from Dart. - /* - testInvalidTransformParameterShouldAssert() { - expect(() = > const SawTooth(2).transform(-0.0001), throwsAssertionError); - expect(() = > const SawTooth(2).transform(1.0001), throwsAssertionError); - - expect(() = > const Interval(0.0, 1.0).transform(-0.0001), throwsAssertionError); - expect(() = > const Interval(0.0, 1.0).transform(1.0001), throwsAssertionError); - - expect(() = > const Threshold(0.5).transform(-0.0001), throwsAssertionError); - expect(() = > const Threshold(0.5).transform(1.0001), throwsAssertionError); - - expect(() = > const ElasticInCurve().transform(-0.0001), throwsAssertionError); - expect(() = > const ElasticInCurve().transform(1.0001), throwsAssertionError); - - expect(() = > const ElasticOutCurve().transform(-0.0001), throwsAssertionError); - expect(() = > const ElasticOutCurve().transform(1.0001), throwsAssertionError); - - expect(() = > const Cubic(0.42, 0.0, 0.58, 1.0).transform(-0.0001), throwsAssertionError); - expect(() = > const Cubic(0.42, 0.0, 0.58, 1.0).transform(1.0001), throwsAssertionError); - - expect(() = > Curves.decelerate.transform(-0.0001), throwsAssertionError); - expect(() = > Curves.decelerate.transform(1.0001), throwsAssertionError); - - expect(() = > Curves.bounceIn.transform(-0.0001), throwsAssertionError); - expect(() = > Curves.bounceIn.transform(1.0001), throwsAssertionError); - - expect(() = > Curves.bounceOut.transform(-0.0001), throwsAssertionError); - expect(() = > Curves.bounceOut.transform(1.0001), throwsAssertionError); - - expect(() = > Curves.bounceInOut.transform(-0.0001), throwsAssertionError); - expect(() = > Curves.bounceInOut.transform(1.0001), throwsAssertionError); - } - */ -} diff --git a/resources/META-INF/plugin.xml b/resources/META-INF/plugin.xml index 5c38907c40..a62a55eff0 100644 --- a/resources/META-INF/plugin.xml +++ b/resources/META-INF/plugin.xml @@ -699,10 +699,6 @@ - - diff --git a/resources/META-INF/plugin_template.xml b/resources/META-INF/plugin_template.xml index eb3a4aba1f..4b0bfda28b 100644 --- a/resources/META-INF/plugin_template.xml +++ b/resources/META-INF/plugin_template.xml @@ -50,18 +50,6 @@ - - - - - - - - - - -